[英]Is it possible to assign port mapping to an existing Docker container by iptables on Linux?
[英]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。這發生在filter
表INPUT
鏈運行之前。
所以我認為如果你允許流量到端口 25,那么實際上你也可以訪問端口465
。 顯然您不想允許訪問所有端口 25,因為這包括您主機的端口 25。
由於 Docker,您此時執行的所有常用技巧都變得更加困難。
您可以使用顯式優於隱式路由並拆分主機與 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 仍然令人不滿意。
我相信現在 Docker 不會在*raw
或*mangle
放入任何內容,因此可以安全地在那里添加您自己的規則。 顯然,這些表存在局限性(原始是在連接跟蹤之前,mangle 僅用於標記連接),因此也不是很好。
最后,我認為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.