Docker-composeで作成されるネットワークはデフォルトではIPv4アドレスしか割り当てられないため、Mastodonインスタンス間の通信ではIPv6を使えません。
とりあえずDockerコンテナにIPv6 ULA(RFC4193)を割り当ててNAT66(IP masquerade)を使用してコンテナから外部へIPv6で喋れるようにしました。
環境
$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 9.0 (stretch)
Release: 9.0
Codename: stretch
$ uname -r
4.9.0-3-amd64
$ docker -v
Docker version 17.06.0-ce, build 02c1d87
$ docker-compose -v
docker-compose version 1.14.0, build c7bdf9e
FORWARDとNATのルールを追加
ULAは例でfd91:7981:3415::/64としますが生成したものと全て置き換えてください。
# ip6tables -I FORWARD -i br+ -s fd91:7981:3415::/64 -m conntrack --ctstate NEW -j ACCEPT
# ip6tables -I FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# ip6tables -t nat -I POSTROUTING -s fd91:7981:3415::/64 -j MASQUERADE
docker-compose.yml
Compose file version 3 reference | Docker DocumentationのIPv6の部分にはCompose file version 2の設定例しかなくversion 3ではenable_ipv6が使えなかったので、予めdocker network createでネットワークを作成してからコンテナに接続することにしました。
$ docker network create --attachable --ipv6 --subnet=fd91:7981:3415::/64 mastodon_ipv6
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
becf15cc01a8 bridge bridge local
32bad14627e6 host host local
4e3ea6b2b0ba mastodon_ipv6 bridge local
cd479a9cd90a none null local
--ipv6を指定してネットワークを作成するとsysctlのnet.ipv6.conf.all.forwardingが有効になるためRAが無効になります。
RAを使用するインターフェースがある場合はaccept_raを2に設定してください。
net.ipv6.conf.eth0.accept_ra=2
@@ -7,6 +7,8 @@
### Uncomment to enable DB persistance
volumes:
- ./postgres:/var/lib/postgresql/data
+ networks:
+ - default
redis:
restart: always
@@ -14,6 +16,8 @@
### Uncomment to enable REDIS persistance
volumes:
- ./redis:/data
+ networks:
+ - default
web:
build: .
@@ -30,6 +34,9 @@
- ./public/assets:/mastodon/public/assets
- ./public/packs:/mastodon/public/packs
- ./public/system:/mastodon/public/system
+ networks:
+ - default
+ - mastodon_ipv6
streaming:
build: .
@@ -42,6 +49,8 @@
depends_on:
- db
- redis
+ networks:
+ - default
sidekiq:
build: .
@@ -54,3 +63,10 @@
- redis
volumes:
- ./public/system:/mastodon/public/system
+ networks:
+ - default
+ - mastodon_ipv6
+
+networks:
+ mastodon_ipv6:
+ external: true
コンテナから外部へIPv6で喋れるか確認
$ docker network inspect mastodon_ipv6
[
{
"Name": "mastodon_ipv6",
"Id": "af17c9b4af1732769e88165c988bba473c10d8a3af89cce6c1d2ca10884995d0",
"Created": "2017-05-28T08:06:18.985725893+09:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": true,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
},
{
"Subnet": "fd91:7981:3415::/64"
}
]
},
"Internal": false,
"Attachable": true,
"Ingress": false,
"Containers": {
"290105e8d2d8f851f2691cb48de1451f66165f0afa06991e3cb047e649693e6c": {
"Name": "mastodon_web_1",
"EndpointID": "c95a7cd9be5f4bfc58ffe7f76b28425b677728bbeb2f292f862a5db8f6a41e22",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": "fd91:7981:3415::2/64"
},
"fa12db023c789d5967d43731b4b9894d4a3056b9fa4dc27391309eec0f7b0b23": {
"Name": "mastodon_sidekiq_1",
"EndpointID": "5e86e412fbccf73ecee590facc252afca4e8e2e890566821a0d8bf7b6ea577a8",
"MacAddress": "02:42:ac:12:00:03",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": "fd91:7981:3415::3/64"
}
},
"Options": {},
"Labels": {}
}
]
$ docker exec -it mastodon_sidekiq_1 sh
/mastodon # ping6 google.co.jp
PING google.co.jp (2404:6800:4004:807::2003): 56 data bytes
64 bytes from 2404:6800:4004:807::2003: seq=0 ttl=55 time=0.938 ms
64 bytes from 2404:6800:4004:807::2003: seq=1 ttl=55 time=0.871 ms
64 bytes from 2404:6800:4004:807::2003: seq=2 ttl=55 time=0.932 ms
/mastodon # traceroute6 google.co.jp
traceroute to google.co.jp (2404:6800:4004:807::2003), 30 hops max, 24 byte packets
1 fd91:7981:3415::1 (fd91:7981:3415::1) 0.011 ms 0.005 ms 0.002 ms
2 2400:8500:1301:746::1 (2400:8500:1301:746::1) 28.441 ms 21.597 ms 2.113 ms
3 2400:8500:1300:1028::2 (2400:8500:1300:1028::2) 0.884 ms 0.818 ms 0.803 ms
4 2400:8500:1300:1027::1 (2400:8500:1300:1027::1) 2.424 ms 1.909 ms 1.954 ms
5 2400:8500:1300:1077::1 (2400:8500:1300:1077::1) 0.814 ms 1.059 ms 1.072 ms
6 ae-25.r00.tokyjp05.jp.bb.gin.ntt.net (2001:218:2000:5000::3c5) 1.475 ms 1.371 ms 1.374 ms
7 ae-6.r31.tokyjp05.jp.bb.gin.ntt.net (2001:218:0:2000::351) 1.982 ms 0.935 ms 0.907 ms
8 ae-6.r02.tokyjp03.jp.bb.gin.ntt.net (2001:218:0:2000::192) 1.714 ms 1.724 ms 1.575 ms
9 2001:218:2000:5000::47e (2001:218:2000:5000::47e) 1.314 ms 1.325 ms 1.332 ms
10 2001:4860:0:1000::1 (2001:4860:0:1000::1) 1.774 ms 1.715 ms 1.539 ms
11 2001:4860:0:1::1b9b (2001:4860:0:1::1b9b) 1.598 ms 1.530 ms 1.495 ms
12 nrt12s15-in-x03.1e100.net (2404:6800:4004:807::2003) 1.576 ms 1.601 ms 1.551 ms
とりあえずこれでIPv6 onlyのMastodonインスタンスとの通信は出来ているのですが、ULA(fc00::/7)を使っているためRFC6724で定義されているpolicy tableに従って、IPv4/IPv6 dual stackのインスタンスとの通信ではIPv4が優先されるようでした。
Dockerfileを書いてコンテナ内に/etc/gai.confを置いてみたりしたんですが、相変わらずIPv4が優先されてしまいます。
Alpine Linuxはglibcではなくmusl libcを使っているので、そもそも/etc/gai.confを参照していませんでした。
コンテナにGUAを割り当てる場合
例えばホスト側のネットワークを2001:db8:1111:aaaa::/64と想定してコンテナに2001:db8:1111:aaaa:1::/80を割り当てる場合は次のように設定します。
# ip6tables -I FORWARD -i br+ -s 2001:db8:1111:aaaa:1::/80 -m conntrack --ctstate NEW -j ACCEPT
# ip6tables -I FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# ip6tables -L FORWARD
Chain FORWARD (policy ACCEPT)
target prot opt source destination
ACCEPT all anywhere anywhere ctstate RELATED,ESTABLISHED
ACCEPT all 2001:db8:1111:aaaa:1::/80 anywhere ctstate NEW
REJECT all anywhere anywhere reject-with icmp6-adm-prohibited
$ docker network create --attachable --ipv6 --subnet=2001:db8:1111:aaaa:1::/80 mastodon_ipv6
$ ip -6 addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,ALLMULTI,UP,LOWER_UP> mtu 1500 qlen 1000
inet6 2001:db8:1111:aaaa::4/64 scope global
valid_lft forever preferred_lft forever
inet6 fe80::9ea3:baff:fe31:e19e/64 scope link
valid_lft forever preferred_lft forever
4: br-d797fe9ca63e: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500
inet6 2001:db8:1111:aaaa:1::1/80 scope global tentative
valid_lft forever preferred_lft forever
inet6 fe80::1/64 scope link tentative
valid_lft forever preferred_lft forever
ホストとコンテナのルーティングテーブルは次のように設定されます。
$ ip -6 route
2001:db8:1111:aaaa:1::/80 dev br-d797fe9ca63e proto kernel metric 256
2001:db8:1111:aaaa:1::/80 dev br-d797fe9ca63e metric 1024
2001:db8:1111:aaaa::/64 dev eth0 proto kernel metric 256
fe80::/64 dev eth0 proto kernel metric 256
fe80::/64 dev br-d797fe9ca63e proto kernel metric 256
default via fe80::1 dev eth0 metric 1024
$ docker exec -it mastodon_sidekiq_1 sh
/mastodon # ip -6 route
2001:db8:1111:aaaa:1::/80 dev eth0 metric 256
fe80::/64 dev eth0 metric 256
default via 2001:db8:1111:aaaa:1::1 dev eth0 metric 1024
unreachable default dev lo metric -1 error -101
ff00::/8 dev eth0 metric 256
unreachable default dev lo metric -1 error -101
NDP Proxyの使用
次の例では上流のルータからコンテナのIPv6アドレスに対してパケットを送信する際、マルチキャストアドレス宛てにMACアドレスを解決するためのNeighbor Solicitation(NS)が送信されていますが、このIPv6アドレスはホスト側のネットワークにいないためNeighbor Advertisement(NA)を応答せず通信に失敗しています。
14:44:37.764074 IP6 2001:db8:1111:aaaa:1::2 > nrt12s14-in-x0e.1e100.net: ICMP6, echo request, seq 0, length 64
14:44:37.771600 IP6 fe80::fac2:88ff:fe70:aebf > ff02::1:ff00:2: ICMP6, neighbor solicitation, who has 2001:db8:1111:aaaa:1::2, length 32
14:44:38.764905 IP6 2001:db8:1111:aaaa:1::2 > nrt12s14-in-x0e.1e100.net: ICMP6, echo request, seq 1, length 64
14:44:38.817944 IP6 fe80::fac2:88ff:fe70:aebf > ff02::1:ff00:2: ICMP6, neighbor solicitation, who has 2001:db8:1111:aaaa:1::2, length 32
14:44:39.765253 IP6 2001:db8:1111:aaaa:1::2 > nrt12s14-in-x0e.1e100.net: ICMP6, echo request, seq 2, length 64
14:44:39.905765 IP6 fe80::fac2:88ff:fe70:aebf > ff02::1:ff00:2: ICMP6, neighbor solicitation, who has 2001:db8:1111:aaaa:1::2, length 32
この場合は次のようにNDP Proxyを使用します。
net.ipv6.conf.eth0.proxy_ndp=1
$ docker network inspect mastodon_ipv6
"Containers": {
"085a4c201b795629e7447ac4fa019e2e36dbfd89a6166370b3ffba8e92f6f172": {
"Name": "mastodon_web_1",
"EndpointID": "6128ab4997af8ff3506ebe40f07fc44e86c19a30ad6ce32f24918ce3c2d1c681",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": "2001:db8:1111:aaaa:1::2/80"
},
"1f6e68fde888ef6b30be41667d9b4117227b466b5d31ba1f2a9d412698f1d803": {
"Name": "mastodon_sidekiq_1",
"EndpointID": "8d3a29b4c643d9841c4a6bbbb8f6bb259f8bb478a39d21b8f4f84135db9fdfee",
"MacAddress": "02:42:ac:12:00:03",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": "2001:db8:1111:aaaa:1::3/80"
}
},
# ip -6 neigh add proxy 2001:db8:1111:aaaa:1::2 dev eth0
# ip -6 neigh add proxy 2001:db8:1111:aaaa:1::3 dev eth0
# ip -6 neigh show proxy
2001:db8:1111:aaaa:1::2 dev eth0 proxy
2001:db8:1111:aaaa:1::3 dev eth0 proxy
2001:db8:1111:aaaa:1::2と2001:db8:1111:aaaa:1::3のNSに対する応答として、ホストのeth0のMACアドレスを格納したNAを送信し、このMACアドレスに対してパケットが送信されるようになります。
15:03:53.033531 IP6 2001:db8:1111:aaaa:1::2 > nrt12s14-in-x0e.1e100.net: ICMP6, echo request, seq 0, length 64
15:03:53.037057 IP6 fe80::fac2:88ff:fe70:aebf > ff02::1:ff00:2: ICMP6, neighbor solicitation, who has 2001:db8:1111:aaaa:1::2, length 32
15:03:53.542840 IP6 fe80::9ea3:baff:fe31:e19e > fe80::fac2:88ff:fe70:aebf: ICMP6, neighbor advertisement, tgt is 2001:db8:1111:aaaa:1::2, length 32
15:03:53.543872 IP6 nrt12s14-in-x0e.1e100.net > 2001:db8:1111:aaaa:1::2: ICMP6, echo reply, seq 0, length 64
15:03:53.542840 IP6 fe80::9ea3:baff:fe31:e19e > fe80::fac2:88ff:fe70:aebf: ICMP6, neighbor advertisement, tgt is 2001:db8:1111:aaaa:1::2, length 32
0x0000: 6000 0000 0020 3aff fe80 0000 0000 0000 `.....:.........
0x0010: 9ea3 baff fe31 e19e fe80 0000 0000 0000 .....1..........
0x0020: fac2 88ff fe70 aebf 8800 5644 4000 0000 .....p....VD@...
0x0030: 2001 0db8 1111 aaaa 0001 0000 0000 0002 ................
0x0040: 0201 9ca3 ba31 e19e .....1..
# ip link show dev eth0
2: eth0: <BROADCAST,MULTICAST,ALLMULTI,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 9c:a3:ba:31:e1:9e brd ff:ff:ff:ff:ff:ff
コンテナに割り当てている全てのアドレスでip -6 neigh add proxyコマンドを実行しましたが、docker-compose scaleを使う時に面倒なのでndppdを使った方がいいかもしれません。
# apt update
# apt install ndppd
# cat /etc/ndppd.conf
proxy eth0 {
rule 2001:db8:1111:aaaa:1::/80 {
static
}
}