はじめに
Docker上で開発や検証を行っている時に、後からこのポート使いたかったのに既にコンテナをrun
しちゃったから削除して新規に立ち上げなきゃみたいな経験がおありでしょうか。自分は何度もありまして、Dockerは裏側ではポートフォワーディングしているだけだから、後から追加で設定が出来てもおかしく無いのにと思いながらも、恥ずかしい話、毎回毎回コンテナを作り直しておりました。結論から言うとポートフォワード を後から追加設定する方法はあります。
※ホスト8001をコンテナ8888に転送する場合
# docker inspect --format '{{ .NetworkSettings.IPAddress }}' 【コンテナ名】
172.17.0.2
# NAT設定
# iptables -t nat -A DOCKER ! -i docker0 -p tcp -m tcp --dport 8001 -j DNAT --to-destination 172.17.0.2:8888
# Filtering設定
# iptables -A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 8001 -j ACCEPT
# 確認
# iptables-save > iptables.txt
最近改めて調べてみたところiptables
を使うことで可能でした。しかし恥の上塗りなことにiptablesについての理解も浅かったので、上を解説する前に今回はfirewallの基本的な話からまとめてみたいと思います。
iptables
iptablesとはCentOSに標準で備わっているFirewallのこと。パケットをフィルタリングしたりNATしたり出来ます。普段触っているfirewalldはiptablesのフロントエンドの事です。
チェイン
通過するパケットの許可や拒否を行うわけですが、その経路のことをチェインと呼びます。
- INPUT
- 入ってくるパケット
- OUTPUT
- 出て行くパケット
- FORWARD
- 転送されるパケット
- PREROUTING
- 受信時に宛先アドレスを変換
- filter前
- POSTROUTING
- 送信時に送信元アドレスを変換
- filter後、送信前
テーブル
iptablesは該当するチェイン、タイミング別に4つのテーブルに分けて設定を行います。
- filter
- パケットの通過/遮断
- 省略時のデフォルトテーブル
- INPUT, OUTPUT, FORWARD
- nat
- NAT変換
- POSTROUTING, PREROUTING, OUTPUT
- mangle
- TOS(Type Of Service)フィールド値変更
- 優先度付け
- POSTROUTING, PREROUTING, INPUT, OUTPUT, FORWARD
- raw
- firewall以外への装置転送用のパケットマーク
- PREROUTING, OUTPUT
設定確認方法は以下の通りです。
$ iptables -L -t filter
target prot opt source destination
$ iptables -L -v -n -t filter
今回の試験はAWS環境で行っており、セキュリティグループで管理しているためINPUTはsshを許可、OUTPUTはフルオープンとなっていますがiptablesには何も入っていません。とはいえAWSのセキュリティグループには上限がありますので、細かい設定はiptablesで行うという人も多いようです。また本来何も入っていなければ何も評価しないので、全てオープンとなるのですが、セキュリティグループも影響するので両方を考える必要があります。
- -t
- 参照テーブル指定
- 省略時はfilterテーブルを参照する
- -v
- 詳細情報表示
- -n
- 名前解決をしない
- -A
- ルール追加
- -j
- ターゲット指定、ACCEPT/DROPなど
書式は次の通りです。
# iptables [-t テーブル] コマンド [マッチ] [ターゲット/ジャンプ]
# iptables -t filter -A INPUT -p tcp -j ACCEPT
上の例を実行するとtcpプロトコルの通信を全て許可します。
Dockerを起動したiptables
dockerデーモンを起動すると、iptablesが少し変化します。まずはfilterテーブルを見ていきます。
# iptables -L -t filter -nv --line-number
Chain INPUT (policy ACCEPT 24383 packets, 78M bytes)
num pkts bytes target prot opt in out source destination
Chain FORWARD (policy DROP 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 4534 53M DOCKER-ISOLATION all -- * * 0.0.0.0/0 0.0.0.0/0
2 2450 53M DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0
3 2450 53M ACCEPT all -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
4 2084 121K ACCEPT all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
5 0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0 0.0.0.0/0
Chain OUTPUT (policy ACCEPT 24345 packets, 4846K bytes)
num pkts bytes target prot opt in out source destination
Chain DOCKER (1 references)
num pkts bytes target prot opt in out source destination
Chain DOCKER-ISOLATION (1 references)
num pkts bytes target prot opt in out source destination
1 4534 53M RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
主にFORWARDチェインにルールが追加されています。
- まず全通信がDOCKER-ISOLATIONチェインで評価される
- 何もせず
RETURN
で元のチェインに戻る
- 何もせず
- docker0 IF行のパケットはDOCKERチェインで評価される
- 何もしない
- docker0 IF行の
ctstate RELATED,ESTABLISHED
状態のパケットは許可 - docker0 IFから来てdocker0 IF以外行のパケットは全て許可
- コンテナ→外部など
- 別docker NW行の場合はDOCKER-ISOLATIONチェインで評価
- docker0 IFから来てdocker0 IF行のパケットは全て許可
- コンテナ間通信など
続いてnatテーブルを見ていきます。
# iptables -L -t nat -nv --line-number
Chain PREROUTING (policy ACCEPT 1273 packets, 75163 bytes)
num pkts bytes target prot opt in out source destination
1 1196 70261 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT 1196 packets, 70261 bytes)
num pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 1731 packets, 143K bytes)
num pkts bytes target prot opt in out source destination
1 0 0 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT 1731 packets, 143K bytes)
num pkts bytes target prot opt in out source destination
1 77 4902 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
Chain DOCKER (2 references)
num pkts bytes target prot opt in out source destination
1 0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
- PREROUTING
ADDRTYPE match dst-type LOCAL
の時にDOCKERチェインで評価する
- OUTPUT
ADDRTYPE match dst-type LOCAL
でループバックアドレス行ではない場合にDOCKERチェインで評価する
- POSTROUTING
172.17.0.0/16
はDockerのブリッジネットワーク- このNWから外部NW行のパケットに
MASQUERADE
を実施する
NATには送信元アドレスを変換するSNATと宛先アドレスを変換するDNATがあるかと思いますが、MASQUERADEはSNATの事です。SNATはPREROUTING、DNATはPOSTROUTINGで定義します。
コンテナを起動したiptable
ホストの80番ポートをコンテナの8080番ポートにフォワーディングする設定でコンテナを起動してみます。
# docker run -itd -p 80:8080 centos:7 /bin/bash
iptablesのfilterテーブルを見てみます。
# iptables -L -t filter -nv --line-number
Chain INPUT (policy ACCEPT 104 packets, 7292 bytes)
num pkts bytes target prot opt in out source destination
Chain FORWARD (policy DROP 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 4534 53M DOCKER-ISOLATION all -- * * 0.0.0.0/0 0.0.0.0/0
2 2450 53M DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0
3 2450 53M ACCEPT all -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
4 2084 121K ACCEPT all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
5 0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0 0.0.0.0/0
Chain OUTPUT (policy ACCEPT 77 packets, 6544 bytes)
num pkts bytes target prot opt in out source destination
Chain DOCKER (1 references)
num pkts bytes target prot opt in out source destination
1 0 0 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.2 tcp dpt:8080
Chain DOCKER-ISOLATION (1 references)
num pkts bytes target prot opt in out source destination
1 4534 53M RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
DOCKERチェインにルールが追加されています。docker0 IF以外からdocker0 IFへのパケットで172.17.0.2:8080宛ての場合に許可しています。
続いてnatテーブルを見てみます。
# iptables -L -t nat -nv --line-number
Chain PREROUTING (policy ACCEPT 7 packets, 384 bytes)
num pkts bytes target prot opt in out source destination
1 1236 72609 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT 7 packets, 384 bytes)
num pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 5 packets, 623 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT 5 packets, 623 bytes)
num pkts bytes target prot opt in out source destination
1 77 4902 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
2 0 0 MASQUERADE tcp -- * * 172.17.0.2 172.17.0.2 tcp dpt:8080
Chain DOCKER (2 references)
num pkts bytes target prot opt in out source destination
1 0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
2 0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:8080
DOCKERチェインにルールが追加されています。docker0 IF以外から来たパケットで宛先ポートが80番の場合、172.17.0.2:8080にDNAT変換を行っています。
今までiptablesを特に意識する事なくdocker run
を行ってきましたが、裏では結構色々なことをしていたんすね..
最後に
ここで改めて8001→8888のポート変換を行ったコマンドを見てみます。(抜粋)
# iptables -t nat -A DOCKER ! -i docker0 -p tcp -m tcp --dport 8001 -j DNAT --to-destination 172.17.0.2:8888
# iptables -A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 8001 -j ACCEPT
一行目はnatテーブルにポートフォワーディングの設定をDOCKERチェインに入れています。docker0 IF以外から来て宛先ポート番号が8001番の時、宛先を172.17.0.2:8888にDNAT変換しています。
二行目ではfilterテーブルに通信を許可する設定をDOCKERチェインに追加しています。docker0 IF以外からdocker0 IF行で172.17.0.2:8001宛のパケットを許可しています。
これでコンテナを消さずにポートフォワーディングの設定を追加する事が出来ます。以上です。