我正在嘗試在 Fedora 中實作一個非常簡單的防火牆,其中公共互聯網可以存取 SSH、HTTP、HTTPS 和 Cockpit,但不能存取其他內容。同時,伺服器透過 Docker 運行微服務,可以在連接埠 8000-8999 上相互通訊。
我使用以下命令在新安裝的 Fedora 伺服器上進行了設定:
firewall-cmd --zone=public --add-service=cockpit
firewall-cmd --zone=public --add-service=http
firewall-cmd --zone=public --add-service=https
firewall-cmd --zone=internal --add-source=192.168.1.65
firewall-cmd --zone=internal --add-source=192.168.1.66
firewall-cmd --zone=internal --add-port=8000-8999/tcp
firewall-cmd --runtime-to-permanent
當我使用 檢查我的配置時--list-all
,一切看起來都正確:
> firewall-cmd --list-all --zone=internal
internal (active)
target: default
icmp-block-inversion: no
interfaces:
sources: 192.168.1.65 192.168.1.66
services: dhcpv6-client ssh
ports: 8000-8999/tcp
protocols:
forward: no
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
> firewall-cmd --list-all --zone=public
public (active)
target: default
icmp-block-inversion: no
interfaces: enp2s0
sources:
services: cockpit dhcpv6-client http https ssh
ports:
protocols:
forward: no
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
然而,當我測試這個時,我能夠擊中http://192.168.1.65:8080。我可以從同一內部網路 (192.168.128.128) 上的電腦以及來自外部電腦的公共請求來存取它。由於兩者均未在internal
s中列出sources
,因此我認為firewalld 不會允許請求通過。
我有一個帶有介面的自動配置docker
區域docker0
,但刪除它似乎並沒有改變我訪問內部連接埠的能力。
為什麼我能夠從未列出的來源成功請求 :8080 internal
?
答案1
事實證明,問題源於我將 Docker 用於內部連接埠。為了簡化讓容器與世界以及彼此通訊的過程,Docker 選擇對防火牆/網路進行大量控制。這意味著如果您不希望您的容器可公開訪問和您需要透過與 Docker 守護程序相同的電腦上的防火牆來控制公共訪問,您需要稍微不同地配置防火牆。Docker 有一些關於如何執行此操作的官方文檔。基本上,您有以下選擇:
- 為您的防火牆設定一台單獨的電腦。這可能是最簡單的,因為 Docker 和防火牆不必共用資源。
- 將您的
iptables
規則添加到DOCKER-USER
鏈中(這更多的是對用戶的回答iptables
;我不確定如何firewalld
複製這種方法) iptables=false
透過在 Docker 服務配置中進行設定來停用整個功能。 (這篇博文討論這個選項)
我也找到一個帖子我認為這是DOCKER-USER
鏈條選項的一個很好的變體。基本上,您會建立一個iptables.conf
可以使用 .flush 載入的檔案iptables-restore -n
。不幸的是,這是一個iptables
解決方案,而不是一個firewalld
解決方案。
診斷此問題的困難部分之一是我將運行腳本來修改防火牆以匹配我想要的內容,並且在我重新啟動電腦或啟動 Docker 守護程式之前,這將一直有效。 Dockeriptables
啟動時會覆蓋配置。
答案2
我設法透過重新建立DOCKER-USER
鏈來解決這個問題,並希望分享它,以防其他人發現它有用。
這是基於我在 Github 上找到的解決方案(https://github.com/firewalld/firewalld/issues/869)以及一些天才的部落格上(https://roosbertl.blogspot.com/2019/06/secure-docker-ports-with-firewalld.html)但我必須稍微調整/完善它。
# 1. Stop Docker
systemctl stop docker.socket
systemctl stop docker.service
# 2. Recreate DOCKER-USER iptables chain with firewalld. Ignore warnings, do not ignore errors
firewall-cmd --permanent --direct --remove-chain ipv4 filter DOCKER-USER
firewall-cmd --permanent --direct --remove-rules ipv4 filter DOCKER-USER
firewall-cmd --permanent --direct --add-chain ipv4 filter DOCKER-USER
# 3. Add iptables rules to DOCKER-USER chain - unrestricted outbound, restricted inbound to private IPs
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -m comment --comment 'Allow containers to connect to the outside world'
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -j RETURN -s 127.0.0.0/8 -m comment --comment 'allow internal docker communication, loopback addresses'
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -j RETURN -s 172.16.0.0/12 -m comment --comment 'allow internal docker communication, private range'
# 3.1 optional: for wider internal networks
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -j RETURN -s 10.0.0.0/8 -m comment --comment 'allow internal docker communication, private range'
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -j RETURN -s 192.168.0.0/16 -m comment --comment 'allow internal docker communication, private range'
# 4. Block all other IPs. This rule has lowest precedence, so you can add rules before this one later.
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 10 -j REJECT -m comment --comment 'reject all other traffic to DOCKER-USER'
# 5. Activate rules
firewall-cmd --reload
# 6. Start Docker
systemctl start docker.socket
systemctl start docker.service