dockerネットワークのipv6対応方法と通信経路の整理

# 概要
**[squidでActiveDirectory連携とSSLインターセプトするProxyをdockerで手軽につくる](https://qiita.com/bashaway/items/6adc75265973de9b5fcc)** で、squid4.4の構築時に
squidがIPv6で自分自身にアクセスするため、dockerもIPv6を有効化する必要がありました。
手順と通信経路について整理します。


# dockerエンジン・デーモンのIPv6化

以下のファイルを作成し、デーモンを再起動します。
IPv6の表記は[ドキュメント用](https://www.nic.ad.jp/ja/translation/apnic/IPv6-Document-prefix.html)なので、適宜修正ください。

```json:/etc/docker/daemon.json
{
  "ipv6": true,
  "fixed-cidr-v6": "2001:db8:1::/64"
}
```

すでに `daemon.json` がある場合には、上記を追記してください。


```
systemctl restart docker
```

止めたくないから `reload` してみたけど、反映されなかったので、 `restart` しました。



参考：https://docs.oracle.com/cd/E77565_01/E87205/html/docker_install_upgrade_ipv6.html


# Dockerネットワークとコンテナ

IPv6を有効化したネットワークを作成し、そこに生成したコンテナを参加させる。

```
# docker network create --ipv6  --driver=bridge  --subnet=fd5a:ceb9:ed8d:1::/64 br_ipv6_nw \
   -o com.docker.network.bridge.name="br_ipv6_nw"
# docker run --network br_ipv6_nw --name cnt01 -it -d centos:centos8 /bin/bash
```

Dockerネットワークの状態

```
# docker network inspect br_ipv6_nw
[
    {
        "Name": "br_ipv6_nw",
        "Id": "ae1e90ac38bbdd208394922263053e8cfac4d31d6d6bbfecf50c345f18606e30",
        "Created": "2020-02-24T18:36:33.239261559+09:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": true,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.26.0.0/16",
                    "Gateway": "172.26.0.1"
                },
                {
                    "Subnet": "fd5a:ceb9:ed8d:1::/64"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "286e2d07ab7a0d04f061ffbe4451f2a5d0a6553ad4cf6063ab21f7cdb4f40441": {
                "Name": "cnt01",
                "EndpointID": "d057150483d194c0e95eca386189482b431cff3b351411b01cb0394b92dd1852",
                "MacAddress": "02:42:ac:1a:00:02",
                "IPv4Address": "172.26.0.2/16",
                "IPv6Address": "fd5a:ceb9:ed8d:1::2/64"
            }
        },
        "Options": {
            "com.docker.network.bridge.name": "br_ipv6_nw"
        },
        "Labels": {}
    }
]
```

# DockerホストとDockerコンテナのインターフェース


## Dockerホスト

インターフェース
ホストのインターフェースIDはsubnetの最若番になる。
と、リンクローカルも最若番のfe80::1になる。

```
# ip add
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:0c:29:62:2e:fa brd ff:ff:ff:ff:ff:ff
    inet 10.254.10.252/24 brd 10.254.10.255 scope global noprefixroute ens192
       valid_lft forever preferred_lft forever
    inet6 fd5a:ceb9:ed8d:fe0a:ed57:61d8:20eb:aba5/64 scope global dynamic noprefixroute
       valid_lft 2591966sec preferred_lft 604766sec
    inet6 fe80::f7f9:b2ce:3e89:7281/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
75: br_ipv6_nw: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:c1:de:82:5c brd ff:ff:ff:ff:ff:ff
    inet 172.26.0.1/16 brd 172.26.255.255 scope global br_ipv6_nw
       valid_lft forever preferred_lft forever
    inet6 fd5a:ceb9:ed8d:1::1/64 scope global tentative
       valid_lft forever preferred_lft forever
    inet6 fe80::1/64 scope link tentative
       valid_lft forever preferred_lft forever
```

ルーティング（関係ないdocker0は除外しています）
デフォルトはens192の対向先

```
# ip -6 route | grep -v docker0
::1 dev lo proto kernel metric 256 pref medium
fd5a:ceb9:ed8d:1::/64 dev br_ipv6_nw proto kernel metric 256 pref medium
fd5a:ceb9:ed8d:fe0a::/64 dev ens192 proto ra metric 100 pref medium
fe80::/64 dev ens192 proto kernel metric 100 pref medium
fe80::/64 dev br_ipv6_nw proto kernel metric 256 pref medium
fe80::/64 dev veth671b926 proto kernel metric 256 pref medium
default via fe80::1 dev ens192 proto ra metric 100 pref medium
```

## Dockerコンテナ

インターフェース
ホストのインターフェースIDはDockerホストから昇順での割り当ての模様。
と、リンクローカルはMACアドレスからの自動採番かな。

```
# docker exec -it cnt01 bash
[root@45b84c2182a5 /]# ip add
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
82: eth0@if83: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:1a:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.26.0.2/16 brd 172.26.255.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fd5a:ceb9:ed8d:1::2/64 scope global nodad
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe1a:2/64 scope link
       valid_lft forever preferred_lft forever
```

ルーティング
デフォルトはULAのDockerホストになるみたい。

```
# ip -6 route
fd5a:ceb9:ed8d:1::/64 dev eth0 proto kernel metric 256 pref medium
fe80::/64 dev eth0 proto kernel metric 256 pref medium
default via fd5a:ceb9:ed8d:1::1 dev eth0 metric 1024 pref medium
```


# 疎通確認

## ホスト → コンテナ の通信確認

```
# ping6 -c 1 fd5a:ceb9:ed8d:1::2 | grep icmp_seq
64 bytes from fd5a:ceb9:ed8d:1::2: icmp_seq=1 ttl=64 time=0.297 ms
# ping6 -c 1 fe80::1%br_ipv6_nw | grep icmp_seq
64 bytes from fe80::1%br_ipv6_nw: icmp_seq=1 ttl=64 time=0.195 ms
```

## コンテナ → ホスト の通信確認

```
# ping6 -c 1 fd5a:ceb9:ed8d:1::1 | grep icmp_seq
64 bytes from fd5a:ceb9:ed8d:1::1: icmp_seq=1 ttl=64 time=0.114 ms
# ping6 -c 1 fe80::1 | grep icmp_seq
64 bytes from fe80::1%eth0: icmp_seq=1 ttl=64 time=0.129 ms
```

## コンテナ → ホスト → 外部 の通信確認

```
# ping6 -c 1 dns.google | grep icmp_seq
64 bytes from dns.google (2001:4860:4860::8844): icmp_seq=1 ttl=52 time=6.74 ms
```


# 通信経路

## 概略

dns.google に ping6 したとき、以下のように通信を行います。

HOSTではIPマスカレードして、srcアドレスをHOSTのアドレスに置き換えます。
GATEWAYでは、fd5a:ceb9:ed8d:fe0a::/64 を ROUTINGPREFIX:ff66/64 にNPTv6 しています。

[![Screenshot from Gyazo](https://gyazo.com/4696e5e0751ae2e80afee14fba70ce20/raw)](https://gyazo.com/4696e5e0751ae2e80afee14fba70ce20)


## HOSTの設定と動作

DockerホストのIPマスカレードは `firewall-cmd --masquerade --zone=public --permanent` により以下のルールが追加されています。

```
# nft list chain ip6 firewalld nat_POST_public_allow
table ip6 firewalld {
        chain nat_POST_public_allow {
                oifname != "lo" masquerade
        }
}
```

これによりコンテナからのパケットはホストでSNATされます。
conntrackで確認すると、以下のようになっています。

```
# conntrack -E -p icmpv6
    [NEW] icmpv6   58 30 src=fd5a:ceb9:ed8d:1::2 dst=2001:4860:4860::8888 type=128 code=0 id=38 [UNREPLIED] src=2001:4860:4860::8888 dst=fd5a:ceb9:ed8d:fe0a:ed57:61d8:20eb:aba5 type=129 code=0 id=38
 [UPDATE] icmpv6   58 30 src=fd5a:ceb9:ed8d:1::2 dst=2001:4860:4860::8888 type=128 code=0 id=38 src=2001:4860:4860::8888 dst=fd5a:ceb9:ed8d:fe0a:ed57:61d8:20eb:aba5 type=129 code=0 id=38
```

## ROUTERの設定と動作

ROUTERのルーティングは以下のように設定されています。
（なんでfe80::1にしなかったんだっけ。。。）

```
Rdc01#sh run | in ::/0
ipv6 route ::/0 Vlan252 FD5A:CEB9:ED8D:FEFC::1
```

## GATEWAYの設定と動作

GATEWAYのインターフェースは以下のように設定されています。
（いまだにCLI使いこなせない。。。）

```
admin@FWdc01> show interface all
（～省略～）
ethernet1/1.999     271   1    zUntrust         vr:default               999    192.168.1.250/24
                                                                                fe80::b60c:25ff:fe0f:df10/64
                                                                                ROUTINGPREFIX:ffff:192:168:1:250/64
                                                                                fe80::192:168:1:250/128
（～省略～）
ethernet1/2.252     260   1    zTrust           vr:default               252    10.254.252.1/24
                                                                                fe80::b60c:25ff:fe0f:df11/64
                                                                                fd5a:ceb9:ed8d:fefc::1/64
                                                                                fe80::1/128
（～省略～）
```


GATEWAYのNPTv6は以下のように設定されています。
本当は Prefix64bit ごとに NPTv6のトランスレータを準備しないといけないのですが、手抜きで２セグをまとめています。

```
"nat66_NPTv6_to_internet; index: 1" {
        nat-type nptv6;
        from zTrust;
        source [ fd5a:ceb9:ed8d:fe0a:0:0:0:0/64  fd5a:ceb9:ed8d:fefc:0:0:0:0/64  ];
        to zUntrust;
        to-interface ethernet1/1.999 ;
        destination any;
        service 0:any/any/any;
        translate-to "src: ROUTINGPREFIX:ff66:0:0:0:0/64 (static-ip) (pool idx: 0)";
        terminal no;
}
```

これによりパケットはGATEWAYでSNATされます。
NAT後のトラフィックログは、以下のようになっています。
（CLIの確認方法がわからなかった。。。）

[![Screenshot from Gyazo](https://gyazo.com/149687fe585bab279ea4beb4d712b051/raw)](https://gyazo.com/149687fe585bab279ea4beb4d712b051)