なぜ、firewalld は Docker コンテナにバインドされた非公開ポートへのパブリック トラフィックを許可するのでしょうか?

なぜ、firewalld は Docker コンテナにバインドされた非公開ポートへのパブリック トラフィックを許可するのでしょうか?

私は、パブリック インターネットから SSH、HTTP、HTTPS、Cockpit にアクセスできるが、それ以外にはアクセスできない、非常にシンプルなファイアウォールを Fedora に実装しようとしています。一方、サーバーは Docker 経由でマイクロサービスを実行し、ポート 8000 ~ 8999 で相互に通信できます。

ネットワーク図

私は次のコマンドを使用して、新規の Fedora Server インストールでこれを設定しました。

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) 上のマシンからアクセスすることも、外部マシンからのパブリック リクエストにアクセスすることもできます。どちらも にリストされていなかったためinternalsourcesfirewalld はリクエストを許可しないものと想定しました。

dockerインターフェースに自動構成されたゾーンがありますdocker0が、それを削除しても内部ポートにアクセスする能力は変わらないようです。

にリストされていないソースから :8080 を正常に要求できるのはなぜですかinternal?

答え1

問題は、内部ポートにDockerを使用していることに起因していることがわかりました。コンテナが外部やコンテナ同士と通信するプロセスを簡素化するために、Dockerはファイアウォール/ネットワークを大幅に制御することを選択しました。つまり、しないコンテナをパブリックにアクセス可能にしたいそしてDocker デーモンと同じマシン上のファイアウォール経由でパブリック アクセスを制御する必要がある場合は、ファイアウォールを少し異なる方法で構成する必要があります。Dockerにはこれを行う方法に関する公式ドキュメントがあります基本的に、次のオプションがあります。

  • ファイアウォール専用の別のマシンをセットアップします。Docker とファイアウォールがリソースを共有する必要がないため、これがおそらく最も簡単です。
  • iptablesチェーンにルールを追加しますDOCKER-USER(これはユーザー向けの回答です。このアプローチを再現するiptables方法がわかりません)firewalld
  • iptables=falseDocker サービス構成で設定して全体を無効にします。(このブログ投稿このオプションについて説明します)

私も投稿を見つけたDOCKER-USERこれはチェーン オプションの優れたバリエーションだと思いました。基本的には、iptables.confを使用してフラッシュせずにロードできるファイルを作成しますiptables-restore -n。残念ながら、これはiptables解決策であって、解決策ではありませんfirewalld

この問題を診断する上で難しい点の 1 つは、ファイアウォールを希望どおりに変更するためのスクリプトを実行する必要があり、マシンを再起動するか Docker デーモンを起動するまでそれが機能しないことです。Docker はiptables起動時に構成を上書きします。

答え2

チェーンを再作成することでこの問題を解決できましたDOCKER-USER。他の方にも役立つと思われる場合に備えて共有したいと思います。

これはGithubで見つけた解決策に基づいています(参考:) とある天才のブログ (https://roosbertl.blogspot.com/2019/06/securing-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

関連情報