はじめに
IPv6がきた! IPv6をコンテナでも使うぞ! え、/48とか貰えないの!? ケチ...orz
基本的には以下のサイトに書いてあるのをつぎはぎしたようなものです。感謝。
- robbertkl/docker-ipv6nat: Extend Docker with IPv6 NAT, similar to IPv4
- Ubuntu18.04でDockerコンテナのIPv6を有効にする方法
IPv6なのにNAPTって正気?
NAPTを廃止することはIPv6の目的の一つだったわけですが、今の段階でdockerで制限された数のIPv6アドレスをうまく取り扱うにはNAPTが一番手っ取り早い解決策となります。
IPv6でNAPTなんて私も気持ち悪いと思いますし、正気の沙汰ではないと私も思いますが、世の中綺麗事だけではうまくいきません。
時には毒を以て毒を制することも必要です...
いやほんと、普通に/64くださいよ...
1個だけIPv6アドレスが使える場合
- ホストで使えるIPv6は
2001:db8:1234:abcd:111:222:333:444/128
のみ
準備
- まずホストでちゃんとipv6が通ることを確認
ping6 -n -c 4 www.google.com
- dockerdをipv6有効で起動
- 必要箇所のみ抜粋
/etc/docker/daemon.json
{
"ipv6": true,
"fixed-cidr-v6": "fd00::/80"
}
- ip6tablesで-t natする
ip6tables -t nat -A POSTROUTING -s fd00::/80 ! -o docker0 -j MASQUERADE
ip6tables -t nat -A POSTROUTING -s fd00:1::/80 ! -o docker0 -j MASQUERADE
コンテナ内側から外側へのIPv6アクセス
- ping6をしてみる
- daemon.jsonに書いてある
fd00::/80
から割り当てられる docker run --rm -t busybox sh -c "ip addr ; ping6 -n -c 4 www.google.com"
- daemon.jsonに書いてある
- docker-composeでnetworks:を指定する場合の例
- たとえば
fd00:1::/80
を明示的に指定する場合
- たとえば
docker-compose.yaml
networks:
default:
enable_ipv6: true
ipam:
config:
- subnet: fd00:1::/80
- いずれもip6tablesのMASQUERADEで出ていく
- うまくいかないときは、docker container ls, docker network ls等でゴミが残っていないことを確認
外側からコンテナ内側へのIPv6アクセス
- docker-ipv6natの起動
docker run -d --name ipv6nat \
--privileged \
--network host \
--restart unless-stopped \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v /lib/modules:/lib/modules:ro \
robbertkl/ipv6nat
- コマンドラインの場合: 省略
- docker-composeの場合:
docker-compose.yaml
services:
httpd6:
image: busybox
container_name: httpd6
ports:
- 8080:80
command:
- sh
- -c
- 'echo Hello world > index.html && httpd -f -v'
- テスト
curl -v [2001:db8:1234:abcd:111:222:333:444]:8080
-
docker logs test
でアクセス元が見える
複数だけど/xxと指定できるほど多くない数のIPv6アドレスが使える場合
凡例
-
2001:db8:1234:abcd:111:222:333:444/128
がホストのIPv6アドレス -
2001:db8:1234:abcd:a111:222:333:444{0,1,2,3...f}/128
が使用可能で、これをコンテナで使いたい -
2001:db8:1234:abcd:a111:222:333::/112
をダミーのsubnetとする - IPv4とMACアドレスは一つだけのまま
- macvlanは使えない
- ipvlanは頑張ればいけるかも
- 要するに美雲このはちゃん
docker network connect版
- ip -6つなげる
ip -6 addr add 2001:db8:1234:abcd:a111:222:333:4440/128 dev eth0
- daemon.json, MASQUERADE, docker-ipv6natは前節と同じ
- network作成
docker network create --ipv6 --subnet=2001:db8:1234:abcd:a111:222:333::/112 --gateway=2001:db8:1234:abcd:a111:222:333:4440 ipv6_exposed_hosts
- gatewayで指定したのが外に露出する
-
2001:db8:1234:abcd:a111:222:333:dead
をダミーとして使う- これはダミーなので、外からの経路はなくてもいい
- コマンドラインの場合:
docker network connect --ip6 2001:db8:1234:abcd:a111:222:333:dead ipv6_exposed_hosts httpd6
- docker-composeの場合:
docker-compose.yaml
networks:
ipv6_exposed_hosts:
driver: bridge
enable_ipv6: true
ipam:
driver: default
config:
- subnet: 2001:db8:1234:abcd:a111:222:333::/112
gateway: 2001:db8:1234:abcd:a111:222:333:4440
services:
httpd6:
image: busybox
container_name: httpd6
ports:
- 8080:80
command:
- sh
- -c
- 'echo "Hello world!" > index.html && httpd -f -v'
networks:
ipv6_exposed_hosts:
ipv6_address: 2001:db8:1234:abcd:a111:222:333:dead
- テスト
curl -6 [2001:db8:1234:abcd:a111:222:333:4440]:8080
ip6tables -t nat -j DNAT版
- ip -6つなげる
ip -6 addr add 2001:db8:1234:abcd:a111:222:333:4440/128 dev eth0
- daemon.json, MASQUERADEは前節と同じ
- コンテナを普通に立ち上げる
- そのコンテナのIPv6アドレスを取得(上記でいう末尾dead、無指定ならfd00::から選ばれるので調べる)
docker container inspect httpd6 | jq .[0].NetworkSettings.Networks[].GlobalIPv6Address
- ip6tables -j DNATで繋げる
ip6tables -t nat -I DOCKER -i eth0 -d 2001:db8:1234:abcd:a111:222:333:4440 -p tcp -m tcp --dport 8080 -j DNAT --to-destination [${上記アドレス}]:80
- テスト
curl -6 [2001:db8:1234:abcd:a111:222:333:4440]:8080
nd proxy版
- ホストのeth0にip aliasを追加するのではなく、nd proxyを繋げる
sysctl -w net.ipv6.conf.all.forwarding=1
sysctl -w net.ipv6.conf.eth0.proxy_ndp=1
ip -6 neigh add proxy 2001:db8:1234:abcd:a111:222:333:4441 dev eth0
- docker-composeであげる
docker-compose.yaml
networks:
ipv6_exposed_hosts:
driver: bridge
enable_ipv6: true
ipam:
config:
- subnet: 2001:db8:1234:abcd:a111:222:333::/112
services:
httpd6:
image: busybox
container_name: httpd6
command:
- sh
- -c
- 'echo "Hello world!" > index.html && httpd -f -v'
networks:
ipv6_exposed_hosts:
ipv6_address: 2001:db8:1234:abcd:a111:222:333:4441
- ipv6_exposed_hostsのidを調べる
docker network ls
- ufwで穴をあけて経路繋げる
ufw route allow in on eth0 out on br-${上記netowrkidの先頭}
- テスト
curl -6 [2001:db8:1234:abcd:a111:222:333:4441]:80
コンテナ宛通信のip6tablesによるフィルタリング
- 通常、IPv4のコンテナ宛はFORWARD(の子のDOCKER-USER)でフィルタリングしますが、この記事のnd proxy版以外の方法の場合INPUT chainになります
おわりに
- IPv6アドレスもっとください
- awsさん、IPv4は捨てるんで別に有償でも構わないけどEIPはIPv6対応しないんですか?
参考資料
- IPv6 Subnet Calculator - subnettingpractice.com
- DockerのコンテナにIPv6で接続すると接続元IPアドレスがコンテナネットワーク内のIPv4アドレスになる - Qiita
- robbertkl/docker-ipv6nat: Extend Docker with IPv6 NAT, similar to IPv4
- Ubuntu18.04でDockerコンテナのIPv6を有効にする方法
- docker-composeでnginxをIPv6化 | mahori ブログ 本館
- RPi4 - Docker with IPv6 and Docker Compose