Dockerのファイアウォール設定(ufwとiptables)

# 背景

Dockerコンテナの各サービスポートを外部からフィルター制限するファイアウォール設定についてまとめてみました。

# 動作環境

## ホスト側

- OS:Ubuntu Server 22.04LTS
- Docker CE version 20.10.17

# ファイアウォール設定する場所

全体のネットワーク構成としてサービスポートをフィルターする場所を決めます。

## ホストの外側

クラウドのAWSではセキュリティグループ設定、オンプレ環境だとファイアウォール装置やルーター機器の機能が該当します。
この場合はDockerコンテナやホスト自体の管理外なので省略します。

## ホスト自体

Ubuntuの場合は通常ufwが利用されますが、Dockerではiptableが使用されてufwの設定は反映されません。
ufwで設定したつもりがサービスポートが外部に開いていてハッキング攻撃を受けてしまうことがよくあります。

## Dockerコンテナ

Dockerコンテナの設定でサービスポートを開放する範囲を変更出来ます。

# サービスポートの場所

各DockerコンテナのサービスポートはDocker Network側とホスト側に存在します。
また各Dockerコンテナは個別の内部IPアドレス番号を持つので同じサービスポート番号が利用できます。
Dockerコンテナ同士のサービス連携ではDocker Network同士で接続が可能でホスト側にポートフォワードする必要はありません。
ホスト外部からDockerコンテナのサービスポートを利用する場合にホスト側にポートフォワード設定します。

# Dockerコンテナのサービスポート設定

## -pオプション
ホスト側にポートフォワードします。この場合はiptablesで設定される為、ufwの制御に関係なくサービスポートは開放されます。
```:docker composeの例
mysql-server:
    image: mysql:8.0
    container_name: mysql-server
    ports:
      - "3306:33060"
```

上記の例の場合、Dockerコンテナ側のサービスポート番号は33060でホスト側に3306番でポートフォワードしています。

## --exposeオプション
Docker Network間だけで通信してホスト外側にサービスを提供しない場合。この時はiptables設定は変更されません。

```:docker composeの例
mysql-server:
    image: mysql:8.0
    container_name: mysql-server
    expose:
      - "3306"
```

上記の例の場合、Dockerコンテナ側のサービスポート番号は3306番で設定されホスト側には提供されません。

# ホスト側iptables設定

さて本題です。
クラウドのAWSだとセキュリティグループで設定すれば良いのですがオンプレ環境などでDockerを利用する時にホスト側でファイアウォール設定します。
前に述べたようにufwの設定はDockerでは制御されずiptablesで設定します。

## 「iptables-persistent」インストール

ubuntuの場合iptablesの設定内容を再起動後も反映させる為に「iptables-persistent」パッケージをインストールします。

```:設定永続化パッケージのインストール
$ sudo apt install iptables-persistent
```

## Chain DOCKER-USER

iptablesではChainのグループで順番にルールが適用されます。
Dockerのユーザー設定は「DOCKER-USER」のChainに設定します。

## Dockerコンテナのネットワーク情報確認

iptables設定を行う為に必要な情報を確認します。

### docker network ls

Docker Networkの一覧が表示されます。

```
$ docker network ls
NETWORK ID     NAME                             DRIVER    SCOPE
95e77afXXXXX   bridge                           bridge    local
8b80a10XXXXX   host                             host      local
9bd3b4fXXXXX   mysql_default                    bridge    local
```

### docker network inspect

Docker Networkの詳細を表示します。
ここで各DockerコンテナのIPアドレスが確認出来ます。

```
$ docker network inspect mysql_default
        "Containers": {
                "Name": "mysql-server",
                "EndpointID": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                "MacAddress": "01:02:03:04:05:06",
                "IPv4Address": "172.20.0.2/16",
                "IPv6Address": ""
            }
        }
```

## iptablesルール設定

Chain DOCKER-USERにルール設定する時はホスト側インターフェースに対してDockerコンテナの内部IPアドレス番号とサービスポート番号を指定します。ホスト側では無いので注意。

- iptables -I DOCKER-USER -i [ホスト側インターフェース名] -p tcp --dport 3306 -d 172.20.0.2 -s 0.0.0.0/0 -j DROP

上記の例ではMySQLのDockerコンテナのサービスポートの3306番を内部IPアドレスとポート番号で指定してDROPで全てブロックしています。

- iptables -I DOCKER-USER -i [ホスト側インターフェース名] -p tcp --dport 3306 -d 172.20.0.2 -s 192.168.1.0/24 -j ACCEPT

次に特定の外部ネットワークだけを許可するルールを設定します。
この順番でルールを設定する事でファイアウォール設定が出来ます。

## iptablesの設定確認

Chainのルールが複数あるので「DOCKER-USER」関連だけ表示させて確認してみます。

```:iptables設定確認
$ sudo iptables -L -n -v | grep -A7 "DOCKER-USER"
Chain DOCKER-USER (1 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 ACCEPT     tcp  --  eth0   *       192.168.1.0/24       172.16.20.2         tcp dpt:3306
    0     0 DROP       tcp  --  eth0   *       0.0.0.0/0            172.16.20.2         tcp dpt:3306
```

## iptablesの設定保存

iptablesの設定と動作が確認出来たらホストの再起動後でも有効になるように保存します。

```:設定の保存
$ sudo /etc/init.d/netfilter-persistent save
```