簡體   English   中英

帶有 docker 端口映射的 iptables

[英]iptables with docker port mapping

眾所周知,當 Docker 在主機上運行時,iptables 規則很難設置,我想我在這篇精彩的博客文章中找到了一個明確的解決方案: https : //unrouted.io/2017/08/15/docker-firewall/

這篇博文中描述的配置已經為我服務了很長時間,但我現在面臨一個我以前從未遇到過的問題。

我正在運行一個 docker 容器,它在主機上的端口 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 定義造成的:我知道這是因為當我刷新 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 以便我可以從外部訪問我的容器?

編輯:

以下是失敗場景中的完整 iptables 規則( 465:25映射):

$ 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

感謝您在 Twitter 上與我聯系。 我之前實際上已經研究過這個問題,而沒有其他人注意到它,我想我知道發生了什么。 在你的例子中:

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。這發生在filterINPUT鏈運行之前。

所以我認為如果你允許流量到端口 25,那么實際上你也可以訪問端口465 顯然您不想允許訪問所有端口 25,因為這包括您主機的端口 25。

由於 Docker,您此時執行的所有常用技巧都變得更加困難。

選項1

您可以使用顯式優於隱式路由並拆分主機與 docker 規則:

*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放入任何內容,因此可以安全地在那里添加您自己的規則。 顯然,這些表存在局限性(原始是在連接跟蹤之前,mangle 僅用於標記連接),因此也不是很好。

選項 3

最后,我認為conntrack iptables 模塊可能有--ctorigdstport答案,但我自己從未嘗試過。 看看這個你可以嘗試:

iptables -A FILTERS -p tcp --dport 25 -m conntrack --ctstate NEW --ctorigdstport 465 -j ACCEPT

看起來有點難看,但明確說明了正在發生的事情。 如果您嘗試使用此方法並且它有效,請告訴我,我會看到有關編寫/更新該博客文章的信息。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM