ryandaniels.iptables_docker
Ansible Role: Docker用のiptables (およびDocker Swarm)
サーバーにiptablesを使用してファイアウォールルールを追加し、DockerおよびDocker Swarmのセキュリティを確保します。これにより、Dockerコンテナを保護できます!
このAnsibleロールは、firewalldとDocker(およびDocker Swarm)が互いに衝突するために作成されました。
解決される問題: Dockerで「公開」ポートを使用してコンテナを起動すると、ポートがサーバーのファイアウォールを通じて無制限に公開されます。iptablesや他のファイアウォールを使用しても、Dockerはその「公開」ポートを誰でもアクセスできるように開放してしまいます。
この解決策の使用例: 信頼されたIPがDockerコンテナ(およびDocker Swarmコンテナ)やその他のオープンOSポートに接続できるようにします。また、指定されたポートを公開(Docker/Docker SwarmおよびOS)にするオプションもあります。信頼されたIPアドレスは同じネットワークIP範囲にない場合や、同じネットワークサブネットにない場合があります。
これは簡単であるべきでしたが、実際にはそうではありません。このプロセスをできるだけシンプルに保とうとしました。
この方法には未知の問題があるかもしれません.. 自己責任でご利用ください!
詳細は: https://ryandaniels.ca/blog/secure-docker-with-iptables-firewall-and-ansible/
DockerのINPUTチェーンの使用について: https://ryandaniels.ca/blog/docker-iptables-input-chain/
現在テストされ、動作している環境:
- CentOS/RHEL 7
- Ubuntu 18.04
- Ubuntu 20.04
機能
- DockerおよびDocker Swarm(Docker SwarmKit)で動作します。
- デフォルトで安全です。設定後は、DockerのIPのみがすべてのコンテナとサーバー上でオープンポートを持つ他のOSプロセスにアクセスできるようになります。
- できるだけシンプルに。iptablesのルールが少ないほど、パフォーマンスが向上します(理論上)。
- 自動的に行われます。信頼されたIPアドレスのセットを使用する場合、ファイアウォール設定に手動でポートを追加する必要はありません。
- すべてのDockerコンテナおよびサーバー上でオープンポートを持つ他のOSプロセスと通信することが許可されている「信頼された」IPアドレスを追加できます。
- 指定されたDockerコンテナのポートやサーバーのOSポートを、ファイアウォールを介して公開(全員)します(例: SSH)。
- インターフェースも指定できます。デフォルトではすべてのインターフェースがフィルタリングされます。特定のネットワークインターフェースをフィルタリングし、他のインターフェースを許可することもできます(信頼されていないインターフェースのみを指定します)。
- 「オフライン」モードで実行されるため、iptablesルールが有効になってもDockerで問題が発生することはありません。
- iptablesに関する専門的な知識は必要ありません。
- Docker Swarmの未文書のiptables使用法や暗号化されたオーバーレイネットワークでも動作します(iptablesルールはINPUTチェーンに追加されます)。
このソリューションでは、iptables
をファイアウォールとして使用し、ipset
を使用して許可されているIPのリストを持たせています。ipset
は、非連続のIP範囲を使用することも可能です。
使用されるiptablesチェーンとその方法:
INPUT、フラッシュしません。OS関連のルール用のカスタムチェーンにジャンプするルールが上部に挿入されます。
DOCKER-USER、フラッシュします。全てのDocker(およびDocker Swarm)関連のルールがここにあり、コンテナがデフォルトで誰でも公開されるのを防ぎます。デフォルトでは、DockerサーバーのIPのみが許可されます。他のIPやコンテナポートはユーザーによって追加できます。
FILTERS、フラッシュします。サーバーのプロセス向けのカスタムチェーン(Dockerではない)。デフォルトでは、DockerサーバーのIPのみが許可されます。他のIPやコンテナポートはユーザーによって追加できます。
iptablesマニュアル: http://ipset.netfilter.org/iptables.man.html
警告
自分のサーバーにアクセスできなくならないようにしてください。これはファイアウォールの設定を変更することです。他にアクセス方法を確保しておくことが重要です。
IPについての注意: これはIPv4のみの対応です。IPv6はテストされていません。サーバーでのIPv6を無効にする方が安全です。
他のセキュリティ考慮点:
非Swarm(通常のDocker)を使用する場合は、内部IPにポートをバインドしてセキュリティを強化することを検討してください。
Swarmを使用している場合は、Docker Swarmの通信のために特定のIPの使用を検討してください。
例: docker swarm init --advertise-addr 192.168.100.100 --listen-addr=192.168.100.100 --data-path-addr=192.168.100.100
重要な注意: Dockerとfirewalldは互換性がありません。このAnsible Roleは、firewalldサービスが実行中または有効な場合にこのロールが失敗するチェックを有効にしています。
firewalldとDockerに関する詳細情報:
https://success.docker.com/article/why-am-i-having-network-problems-after-firewalld-is-restarted
https://www.tripwire.com/state-of-security/devops/psa-beware-exposing-ports-docker/
https://docs.docker.com/network/iptables/
SELinuxのバグ:
現在、SELinuxにバグがあり、iptablesのルールをiptables.saveファイルに保存できません。
影響: 2回目のiptablesルールの保存が静かに失敗します。
回避策が追加されており、SELinuxがchmodがiptables.saveファイルと対話できるようになっています。
SELinuxを無効にすることもできますが、推奨されません。
バグレポート: https://bugs.centos.org/view.php?id=12648
詳細については、以下を参照してください。
警告:
必ずまず非本番環境でテストしてください。保証や責任を負うことはできません。
注意してください。これはOS上でiptablesルールを削除したり追加したりします。慎重に使用してください。
既存のiptablesルールが削除される可能性があります!実行する前に設定を確認してください。
この方法には未知の問題があるかもしれません.. 自己責任でご利用ください!
テストしたDockerバージョン
Dockerエンジン - コミュニティエディションバージョン:
- 19.03.8
- 19.03.9
- 19.03.12
通常のDockerモードと3ノードのDocker Swarmクラスターでテストされました。
テストしたディストリビューション
- CentOS: 7.7, 7.8
- Ubuntu 18.04
- Ubuntu 20.04
依存関係
- iptables & iptables-services
CentOS 7で最新のv1.4.21でテスト済みです。
- ipset & ipset-service
CentOS 7で最新のv7.1でテスト済みです。
デフォルト設定
- デバッグを有効にする
debug_enabled_default: false
- プロキシ(必要なパッケージをインストールする際にプロキシの背後にいる場合に必要)
proxy_env: []
- デフォルトでロールは無効です。group_varsやplaybookでtrueに変更してください。
iptables_docker_managed: false
- サービスが実行中または有効な場合にチェックして、ロールを失敗させる
iptables_docker_check_problem_service_managed: true
- チェックするサービスとロールを失敗させる内容
iptables_docker_check_problem_service:
- docker.service
- 変数から設定を表示する
iptables_docker_show_config: true
- iptablesサービスを開始する
iptables_docker_start: true
- iptablesパッケージをインストールする
iptables_docker_managed_pkg: true
iptables_docker_packages:
- iptables
- iptables-services
- ipset
- ipset-service
- policycoreutils-python #semoduleに必要
- ipsetファイルを強制的にコピーしてipsetを再ロードする
iptables_docker_copy_ipset_force: false
- iptablesファイルを強制的にコピーしてiptablesを再ロードする
iptables_docker_copy_iptables_force: false
- iptables設定の保存場所
iptables_docker_iptables_config_save: /etc/sysconfig/iptables
- ipset設定の保存場所
iptables_docker_ipset_config_dir: /etc/sysconfig/ipset.d
ipsetの最大要素(許可リストのIP数)
初回作成後に変更した場合は、手動で削除して再作成が必要です。64KのIP数で十分でしょう。
iptables_docker_ipset_maxelem: 65536
ユーザー設定
DockerサーバーIPをオーバーライド(オプション)
DockerサーバーのIPをオプションとして指定できます。設定しない場合、IPはAnsibleインベントリのdocker_hostsグループから決定されます。
# iptables_docker_server_ip_allow_set:
# - 192.168.100.100
# - 192.168.100.101
# - 192.168.100.102
- すべてのDockerコンテナおよびサーバー上のプロセスの公開ポートを使用することを許可されているIP。
# iptables_docker_ip_allow_set: []
iptables_docker_ip_allow_set:
- 192.168.100.1
- 192.168.101.0/24
- 192.168.102.0/24
OSルールの制限されるネットワークアダプター
指定されたアダプターのみがブロックされます。他のアダプターは許可されます。デフォルトではすべてをブロックします(記号'+')。
特定のネットワークインターフェースのみを制限したい場合は、正確な名前を使用してください。
同じタイプのすべてのインターフェースの制限は、「interface+」を使用することで実現できます。これはiptablesのワイルドカードであり、すべてのインターフェースにマッチします。
例: ethXインターフェースを制限するには、「eth+」を使用します。「eth+」は、ethで始まるすべてのもののワイルドカードです。
'*'を使用しないでください。これはワイルドカードではなく、何もマッチしません!
少なくともブロックされるものが少ないほど安全です。すべてをブロックする('+')方が安全ですが、できない場合は、トラフィックが高いネットワークアダプターを優先的に追加してください。
local(lo)はここでは必要ありません。
iptables_docker_external_network_adapter:
- "+" #すべてのワイルドカード
# - "eth+"
# - "enp0s+"
# - "wlp1s+"
公開されるOS TCPポート
誰もが接続できるようにするポート(公開可能)。ここで指定すると、iptablesレベルでこれらのポートへのすべてのTCPトラフィックが許可されます。
これはOS上のポートにのみ適用され、Dockerコンテナには適用されません。
iptables_docker_global_ports_allow_tcp:
- 22 # SSH
公開されるOS UDPポート
誰もが接続できるようにするポート(公開可能)。ここで指定すると、iptablesレベルでこれらのポートへのすべてのUDPトラフィックが許可されます。
これはOS上のポートにのみ適用され、Dockerコンテナには適用されません。
iptables_docker_global_ports_allow_udp: []
Dockerルールのために制限されるネットワークアダプター
デフォルトではOSのネットワークアダプターと同じセットアップを使用します。
iptables_docker_swarm_network_adapter: "{{ iptables_docker_external_network_adapter }}"
# iptables_docker_swarm_network_adapter:
# - "+" #すべてのワイルドカード
# # - "eth+"
公開されるDocker TCPポート
誰もが開放したいDockerコンテナのTCPポートを追加します。DockerとDocker Swarmのためです。
Docker Swarmのポートはここでは必要ありません。
iptables_docker_swarm_ports_allow_tcp: []
# iptables_docker_swarm_ports_allow_tcp:
# - 9000
公開されるDocker UDPポート
誰もが開放したいDockerコンテナのUDPポートを追加します。DockerとDocker Swarmのためです。
Docker Swarmのポートはここでは必要ありません。
iptables_docker_swarm_ports_allow_udp: []
- Dockerブリッジネットワーク名(docker0)およびIP範囲(DOCKER-USER iptablesソース許可用)
iptables_docker_bridge_name: docker0
iptables_docker_bridge_ips: 172.17.0.0/16
- Docker SwarmブリッジネットワークIP範囲(docker_gwbridge)(DOCKER-USER iptablesソース許可用)
iptables_docker_swarm_bridge_name: docker_gwbridge
iptables_docker_swarm_bridge_ips: 172.18.0.0/16
例の設定ファイル (inventories/dev-env/group_vars/all.yml)
以下の例より:
IPが信頼されたリストに追加されます:
192.168.100.1
192.168.101.0/24
すべてのネットワークインターフェースは、iptables_docker_external_network_adapterのワイルドカード'+'を使用して制限されます。
ポート22が公開されます。
---
iptables_docker_ip_allow_set:
- 192.168.100.1
- 192.168.101.0/24
iptables_docker_external_network_adapter:
- "+" #すべてのワイルドカード
iptables_docker_global_ports_allow_tcp:
- 22 # SSH
例のインベントリファイル
[docker_hosts]
centoslead1 ansible_host=192.168.100.100
centoswork1 ansible_host=192.168.100.101
centoswork2 ansible_host=192.168.100.102
例のPlaybook iptables_docker.yml
---
- hosts: '{{ inventory }}'
become: yes
vars:
# このロールを使用
iptables_docker_managed: true
roles:
- ryandaniels.iptables_docker
使用方法
実行する前に、すでにiptablesを使用しているかどうかを確認してください!何も上書きしたり削除するべきではありません。これは、同じiptablesチェーンを使用している場合を除きます。
デフォルトでは、iptables_docker_managed=true
を設定しない限り、タスクは実行されません。これは、誤った操作を防ぐための設計です。
ansible-playbook iptables_docker.yml --extra-vars "inventory=centos7 iptables_docker_managed=true" -i hosts-dev
パッケージのインストールをスキップ(すでにある場合):
ansible-playbook iptables_docker.yml --extra-vars "inventory=centos7 iptables_docker_managed=true" -i hosts --skip-tags=iptables_docker_pkg_install
詳細な出力を表示(デバッグ情報):
ansible-playbook iptables_docker.yml --extra-vars "inventory=centos7 iptables_docker_managed=true debug_enabled_default=true" -i hosts-dev
iptablesサービスを開始しない、またはiptablesのためにルールを追加しない:
ansible-playbook iptables_docker.yml --extra-vars "inventory=centos7 iptables_docker_managed=true iptables_docker_start=false" -i hosts-dev
ipsetとiptablesを強制的に更新:
ansible-playbook iptables_docker.yml --extra-vars "inventory=centos7 iptables_docker_managed=true iptables_docker_copy_ipset_force=true iptables_docker_copy_iptables_force=true" -i hosts-dev
設定の表示(変数から):
ansible-playbook iptables_docker.yml --extra-vars "inventory=centos7 iptables_docker_managed=true iptables_docker_show_config=true" -i hosts --tags "iptables_docker_show_config"
ipsetサイズ制限に関する注意
重要: "エントリの数"のサイズに注意してください。この数がmaxelemサイズ(65536)に近い場合、ipset "ip_allow"を削除して再作成する必要があります。
64Kであれば十分でしょう。
ファイルの場所は: templates/ip_allow.set.j2
create -exist ip_allow hash:ip family inet hashsize 1024 maxelem 65536
ipsetリストのサイズを確認する:
ipset list |grep "Number of entries"
重要な出力:
Number of entries: 3
SELinuxの手動作業の回避策
バグの詳細: https://bugs.centos.org/view.php?id=12648
iptablesを2回目に保存する際、SELinuxがchmodをブロックするという問題があります。
以下を使用すると、SELinuxがchmodがiptables.saveファイルを変更できるようになります。もしAnsibleロールを使用していない場合はこれを行ってください。
再現するには、iptables設定が再起動/停止後に保存されるように設定したら、iptablesサービスを再起動します。
yum install audit policycoreutils policycoreutils-python
ausearch -m AVC,USER_AVC,SELINUX_ERR,USER_SELINUX_ERR -i|tail -55
ausearch -c 'chmod' --raw | audit2allow -M iptables_save_chmod
# または grep "iptables.save" /var/log/audit/audit.log|tail | audit2allow -M iptables_save_chmod
semodule -i iptables_save_chmod.pp
iptablesコマンドリファレンス
iptablesのドキュメントには他のコマンドが多数あります: http://ipset.netfilter.org/iptables.man.html
アクティブなiptablesをリスト表示する:
iptables -nvL --line-numbers
CentOS/RHELで便利なコマンド:
cat /etc/sysconfig/ipset.d/ip_allow.set
systemctl restart ipset
ipset list | head
iptables -F DOCKER-USER
iptables -F FILTERS
iptables-restore -n < ansible_iptables_docker-iptables
grep -v "^#" ansible_iptables_docker-iptables
iptables -S INPUT
iptables -S DOCKER-USER
iptables -S FILTERS
Ubuntuで便利なコマンド:
vi /etc/iptables/ipsets
# 手動でIPを削除する際は'flush'を追加します。
/usr/sbin/netfilter-persistent reload
cat /etc/iptables/ipsets
cat /etc/iptables/rules.v4
手動コマンド (CentOS/RHEL)
現在のiptablesルールを確認します。万が一失われた場合に備えてメモします!
iptables -nvL --line-numbers
必要なパッケージをインストール:
yum install iptables iptables-services ipset ipset-service
SELinuxを使用している場合は、以下もインストール:
yum install policycoreutils-python
サーバーIPや他の信頼されたIPでipsetを設定します:
mkdir -p /etc/sysconfig/ipset.d
cat > /etc/sysconfig/ipset.d/ip_allow.set << 'EOF'
create -exist ip_allow hash:ip family inet hashsize 1024 maxelem 65536
add ip_allow 192.168.1.123
add ip_allow 192.168.101.0/24
add ip_allow 192.168.102.0/24
EOF
ipsetサービスを開始し、有効にする:
systemctl status ipset
systemctl start ipset
systemctl enable ipset
ipsetのローデッド設定を確認:
ipset list | head
追加されるiptablesルール(デフォルトではポート22は誰でもアクセス可能):
cat > ansible_iptables_docker-iptables << 'EOF'
*filter
:DOCKER-USER - [0:0]
:FILTERS - [0:0]
#INPUTのフラッシュ不可。Docker Swarmの暗号化オーバーレイルールが消える
#-F INPUT
#初回追加のみ
#-I INPUT -j FILTERS
-A DOCKER-USER -m state --state RELATED,ESTABLISHED -j RETURN
-A DOCKER-USER -i docker_gwbridge -j RETURN
-A DOCKER-USER -s 172.18.0.0/16 -j RETURN
-A DOCKER-USER -i docker0 -j RETURN
-A DOCKER-USER -s 172.17.0.0/16 -j RETURN
#以下のDockerポートを全員にオープン、コメント解除した場合
#-A DOCKER-USER -p tcp -m tcp -m multiport --dports 8000,8001 -j RETURN
#-A DOCKER-USER -p udp -m udp -m multiport --dports 9000,9001 -j RETURN
-A DOCKER-USER -m set ! --match-set ip_allow src -j DROP
-A DOCKER-USER -j RETURN
-F FILTERS
#Docker Swarmの暗号化オーバーレイネットワークはINPUTにルールを追加。上部になければならない
-A FILTERS -p udp -m policy --dir in --pol ipsec -m udp --dport 4789 -m set --match-set ip_allow src -j RETURN
-A FILTERS -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FILTERS -p icmp -j ACCEPT
-A FILTERS -i lo -j ACCEPT
#以下OSポートを全員にオープン、コメント解除した場合
-A FILTERS -p tcp -m state --state NEW -m tcp -m multiport --dports 22 -j ACCEPT
#-A FILTERS -p udp -m udp -m multiport --dports 53,123 -j ACCEPT
-A FILTERS -m set ! --match-set ip_allow src -j DROP
-A FILTERS -j RETURN
COMMIT
EOF
上記のルールをiptablesに追加するためにiptables-restoreを使用します。「-n」フラグが非常に重要です。これにより、すでにDocker(またはDocker Swarm)にルールがある場合にiptablesルールがフラッシュされません。
iptables-restore -n < ansible_iptables_docker-iptables
次に、INPUTチェーンにルールを追加して、新しいルールをFILTERSで使用できるようにします。それは上部に追加される必要があり、一度だけ追加します:
iptables -I INPUT 1 -j FILTERS
iptablesルールを保存します:
/usr/libexec/iptables/iptables.init save
iptablesサービスを開始し、有効にする:
systemctl status iptables
systemctl start iptables
systemctl enable iptables
他の誰もがアクセスできるようにするためにiptablesルールをカスタマイズしたい場合は、iptablesファイルの適切なルールにポートを追加し、その後に上記のコマンドを再実行します:
iptables-restore -n < ansible_iptables_docker-iptables
/usr/libexec/iptables/iptables.init save
上記の警告をお見逃しなく!特にSELinuxに関するものです。
手動コマンド (Ubuntu 20.04)
現在のiptablesルールを確認します。万が一失われた場合に備えてメモします!
Ubuntu 18.04もほとんど同じです。ipset-persistent
パッケージはUbuntu 18.04にはないため、そのパッケージを省略し、files/ubuntu/iptables-persistent*/plugins/*-ipset
からファイルをコピーして/usr/share/netfilter-persistent/plugins.d/
に配置します。
iptables -nvL --line-numbers
必要なパッケージをインストール:
apt install iptables iptables-persistent netfilter-persistent ipset ipset-persistent
サーバーIPや他の信頼されたIPでipsetを設定します:
mkdir -p /etc/iptables
cat > /etc/iptables/ipsets << 'EOF'
create -exist ip_allow hash:ip family inet hashsize 1024 maxelem 65536
flush
add ip_allow 192.168.1.123
add ip_allow 192.168.101.0/24
add ip_allow 192.168.102.0/24
EOF
ipsetを再ロード:
/usr/sbin/netfilter-persistent reload
ipsetにローデッド設定があるか確認:
ipset list | head
追加されるiptablesルール(デフォルトではポート22は誰でもアクセス可能):
CentOS/RHELと同様のコマンドを使用します。
上記のルールをiptablesに追加するためにiptables-restoreを使用します。「-n」フラグが非常に重要です。これにより、すでにDocker(またはDocker Swarm)にルールがある場合にiptablesルールがフラッシュされません。
iptables-restore -n < ansible_iptables_docker-iptables
次に、INPUTチェーンにルールを追加して、新しいルールをFILTERSで使用できるようにします。それは上部に追加される必要があり、一度だけ追加します:
iptables -I INPUT 1 -j FILTERS
iptablesルールを保存します:
/usr/sbin/netfilter-persistent save
iptablesサービスを開始し、有効にする:
systemctl status netfilter-persistent
systemctl start netfilter-persistent
systemctl enable netfilter-persistent
他の誰もがアクセスできるようにするためにiptablesルールをカスタマイズしたい場合は、iptablesファイルの適切なルールにポートを追加し、その後に上記のコマンドを再実行します:
iptables-restore -n < ansible_iptables_docker-iptables
/usr/sbin/netfilter-persistent save
上記の警告をお見逃しなく!
TODO
- firewalldが実行中または有効な場合にチェックし、失敗する
- iptablesのDockerルール保存に関する問題?問題ないはずです。
- iptables_docker_ip_allow_setを空にできないようにする。空の場合、何もブロックされないためです!
- ネットワークアダプタの*にエラーを追加
- 許可リストにDockerのIPの自動的な追加
- 信頼されたDockerサーバーIPのオーバーライドを変更
- "when"と"tags"が適切か確認する
- Ubuntu?Ubuntuにはiptables-servicesやipset-serviceがありません。iptables-persistentとipset-?が必要です。UFWサポートなし
- ipv6?? これはipv4のみ
- TCP、UDPのDockerコンテナおよびOSポートが機能することを確認
- Dockerコンテナからのアウトバウンドトラフィックが機能することを確認
- テストを追加?モレキュール?単一ノードのSwarmモードのみ?「信頼されていない」IPからの接続が機能しないことをテストする方法?
著者
ライアン・ダニエルズ
Manage iptables configuration to secure Docker (including Docker Swarm).
ansible-galaxy install ryandaniels.iptables_docker