LoginSignup
1
1

IPv6アドレスを十分な数もらえないときでも、コンテナ内にIPv6を通す

Last updated at Posted at 2023-09-22

はじめに

IPv6がきた! IPv6をコンテナでも使うぞ! え、/48とか貰えないの!? ケチ...orz

基本的には以下のサイトに書いてあるのをつぎはぎしたようなものです。感謝。

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"
  • 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対応しないんですか?

参考資料

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1