Спасибо, что обратились ко мне в Твиттере.Я на самом деле изучал эту проблему раньше без кого-то еще, кто заметил это, и я думаю, что я знаю, что происходит.В вашем примере:
docker run --rm -it -p 465:25 python:3.6 python3 -m http.server 25
Если вы посмотрите полную конфигурацию брандмауэра с iptables-save
, вы увидите кучу правил NAT.Вы, вероятно, увидите что-то похожее на это в разделе *nat
:
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
... snip ...
-A DOCKER ! -i br-abbaabbaabba -p tcp -m tcp --dport 465 -j DNAT --to-destination 172.18.0.10:25
Так что это правило выполняется в фазе PREROUTING
и переписывает входящий пакет, чтобы он выглядел как всегда для порта25, а не порт 465. И это происходит до запуска цепочки filter
tables INPUT
.
Поэтому я считаю, что если вы разрешите трафик на порт 25, то на самом деле вы сможете получить доступ к порту 465
тоже.Очевидно, что вы не хотите разрешать доступ ко всем портам 25, поскольку они включают порт 25 вашего хоста.
Все обычные уловки, которые вы делаете на этом этапе, значительно усложняются из-за Docker.
Вариант 1
Вы можете пойти по явному пути лучше, чем неявный маршрут, и разделить правила хоста против докера:
*filter
:INPUT ACCEPT [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:FILTERS - [0:0]
:DOCKER-USER - [0:0]
-F INPUT
-F DOCKER-USER
-F FILTERS
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -s 127.0.0.0/8 -j REJECT
-A INPUT -p icmp --icmp-type any -j ACCEPT
# Rules for services running on the host:
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
# Rules for services running in containers:
-A DOCKER-USER -i eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT
# This says dport 25, but is actually 465. Yay for prerouting + NAT.
# Service on real host port 25 should still be inaccessible because DOCKER-USER
# is only accessible via `FORWARD` and not `INPUT`...
-A DOCKER-USER -i eth0 -m state --state NEW -m tcp -p tcp --dport 25 -j ACCEPT
-A DOCKER-USER -j REJECT --reject-with icmp-host-prohibited
COMMIT
Все еще неудовлетворительно, что вы разрешаете трафикк порту 25 ..
Вариант 2
Я считаю, что сейчас Docker ничего не помещает в *raw
или *mangle
, поэтому безопасно добавлять туда свои собственные правила.Очевидно, что с этими таблицами есть ограничения (raw перед отслеживанием соединений, mangle - только для маркировки соединений), так что это тоже не очень хорошо.
Опция 3
Наконец, единственное, о чем я могу подуматьмодуль conntrack
iptables может иметь ответ с --ctorigdstport
, но я никогда не пробовал сам.Глядя на это , вы можете попробовать:
iptables -A FILTERS -p tcp --dport 25 -m conntrack --ctstate NEW --ctorigdstport 465 -j ACCEPT
Немного некрасиво смотреть на это, но явно о том, что происходит.Если вы попробуете это, и это сработает, дайте мне знать, и я посмотрю, как написать это / обновить этот пост в блоге.