iptables с сопоставлением портов докера - PullRequest
0 голосов
/ 19 февраля 2019

Правила Iptables общеизвестно сложны для настройки, когда Docker работает на хосте, и я подумал, что у меня есть окончательное решение в этом фантастическом посте: https://unrouted.io/2017/08/15/docker-firewall/

КонфигурацияОписанное в этом сообщении в блоге долго служило мне хорошо, но теперь я столкнулся с проблемой, которой у меня никогда не было.

Я использую докер-контейнер, который предоставляет службу через порт 465 на хосте.Порт 465 отображается на порт 25 в контейнере.Вот как смоделировать такой сервис:

$ docker run --rm -it -p 465:25 python:3.6 python3 -m http.server 25

Моя проблема в том, что я не могу получить доступ к порту 465 на моем сервере извне:

$ curl mydomain.com:465
curl: (7) Failed to connect to mydomain.com port 465: No route to host

Однако ,и вот интересная часть: мне удается получить доступ к сервису, если порт на хосте соответствует тому же порту в контейнере.Другими словами, когда я запускаю на хосте:

$ docker run --rm -it -p 465:465 python:3.6 python3 -m http.server 465

, я могу получить доступ к службе извне:

$ curl mydomain.com:465
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org...

Вся эта проблема связана с моим определением iptables: Iзнаю, потому что, когда я сбрасываю правила iptables, мне удается получить доступ к службе извне, независимо от сопоставления портов.

Вот мои правила iptable:

*filter
# Source: https://unrouted.io/2017/08/15/docker-firewall/
: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
-A INPUT -j FILTERS
-A DOCKER-USER -i eth0 -j FILTERS
-A FILTERS -m state --state ESTABLISHED,RELATED -j ACCEPT
-A FILTERS -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A FILTERS -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A FILTERS -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT
-A FILTERS -m state --state NEW -m tcp -p tcp --dport 465 -j ACCEPT

-A FILTERS -j REJECT --reject-with icmp-host-prohibited
COMMIT

Как мне изменитьмои iptables, чтобы я мог получить доступ к своему контейнеру извне, независимо от назначения порта?

EDIT:

Вот полные правила iptables в сценарии сбоя (465:25 mapping):

$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination        
ACCEPT     all  --  anywhere             anywhere            
REJECT     all  --  loopback/8           anywhere             reject-with icmp-port-unreachable
ACCEPT     icmp --  anywhere             anywhere             icmp any
FILTERS    all  --  anywhere             anywhere            

Chain FORWARD (policy DROP)
target     prot opt source               destination        
DOCKER-USER  all  --  anywhere             anywhere            
DOCKER-ISOLATION-STAGE-1  all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination        

Chain DOCKER (3 references)
target     prot opt source               destination        
ACCEPT     tcp  --  anywhere             172.19.0.4           tcp dpt:3000
ACCEPT     tcp  --  anywhere             172.17.0.3           tcp dpt:smtp

Chain DOCKER-ISOLATION-STAGE-1 (1 references)
target     prot opt source               destination        
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere            
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere            
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere            
RETURN     all  --  anywhere             anywhere            

Chain DOCKER-ISOLATION-STAGE-2 (3 references)
target     prot opt source               destination        
DROP       all  --  anywhere             anywhere            
DROP       all  --  anywhere             anywhere            
DROP       all  --  anywhere             anywhere            
RETURN     all  --  anywhere             anywhere            

Chain DOCKER-USER (1 references)
target     prot opt source               destination        
FILTERS    all  --  anywhere             anywhere            

Chain FILTERS (2 references)
target     prot opt source               destination        
ACCEPT     all  --  anywhere             anywhere             state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere             state NEW tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere             state NEW tcp dpt:http
ACCEPT     tcp  --  anywhere             anywhere             state NEW tcp dpt:https
ACCEPT     tcp  --  anywhere             anywhere             state NEW tcp dpt:urd
REJECT     all  --  anywhere             anywhere             reject-with icmp-host-prohibited

1 Ответ

0 голосов
/ 21 февраля 2019

Спасибо, что обратились ко мне в Твиттере.Я на самом деле изучал эту проблему раньше без кого-то еще, кто заметил это, и я думаю, что я знаю, что происходит.В вашем примере:

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

Немного некрасиво смотреть на это, но явно о том, что происходит.Если вы попробуете это, и это сработает, дайте мне знать, и я посмотрю, как написать это / обновить этот пост в блоге.

...