為什麼 firewalld 允許公共流量到達綁定到 Docker 容器的非公共連接埠?

為什麼 firewalld 允許公共流量到達綁定到 Docker 容器的非公共連接埠?

我正在嘗試在 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) 上的電腦以及來自外部電腦的公共請求來存取它。由於兩者均未在internals中列出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

相關內容