第 1 节 概述
由于公网 IP 的获取比较困难,本人的 NAS 上的各种 Docker 应用都是通过路由器的公网 IPV6 地址进行代理的,使用的是在 OpenWrt 上部署到一款名为 Lucky 的应用,Github项目地址, 这款应用聚合了端口转发、动态域名、自动证书等功能,一个应用就能解决将服务部署到公网的全部流程。
因为 Lucky 集成了 socat 的功能,可以进行 IPV6 到 IPV4 的端口转发,所以通常 NAS 上的应用只要能通过 IPV4 地址访问到就可以部署到公网。
但是这样会存在一个问题:
信息
Docker 默认情况下不会配置 IPV6 功能,因而在 Docker 容器内无法通过域名(IPV6)去访问别的容器,只能使用本地的 IPV4 地址来访问。这样不方便,并且有的应用需要SSL或者设置了BaseURL,直接用 IPV4 地址访问可能会有问题。
不过,一直以来,本人并不知道 Docker 默认没有 IPV6 ,主要原因如下:
- 我在 OpenWrt 上安装了Clash,而 Clash 为了保证访问的私密性,配置了 FakeIP 功能。
- 我没有设置 FakeIP Filter,所以即便是本地网络之间的访问,地址也会被转成 FakeIP。
- 访问的 IPV6 地址也会被转成 FakeIP,并且 FakeIP 是保留 IP 段的 IPV4 地址。
- 路由器负责解析 FakeIP,并且 Lucky 将解析后的结果代理到了对应的 IPV4 地址。
所以,阴差阳错之下,本人一直在容器内正常使用着 IPV6 功能。

不过,也许是因为 Clash 时常可能访问不了,或者别的原因,容器内访问 IPV6 时常也会访问不了。并且用这种方式会增加通信之间的消耗,增加网络的复杂性,容易出现各种未知的问题,所以只好想办法来解决这一问题。
查看 OpenWrt 上的路由器网络,WAN6 IP 地址的掩码是 64 位的,这说明运营商只给我分配了这一个 IPV6 公网地址,我不可以通过前缀委派(Prefix Delegation,PD)给其他内网设备分配公网 IPV6 地址,所以从路由器开始,我的后续所有的设备的 IPV6 地址都只能通过网络地址转换(Network Address Translation,NAT)来分配。

本人的 Docker 容器基本全都是桥接的,于是解决这个问题有 3 个步骤:
- Nas 本身要配置 IPV6
- Docker 的虚拟网口和 NAS 之间要配置 NAT
- Docker 容器内要配置 IPV6 并且和 Docker 网口之间配置 NAT
在下文中,本人将依次讲述这些如何配置。
第 2 节 路由器网络
本人使用的是威联通的 NAS,登录 NAS 系统,来到控制台->网络与虚拟交换机->网络适配器,配置 IPV6 如下:

第 3 节 NAS 网络
通过 ssh 连接到 NAS,通过测试,威联通的 NAS 安装了iptables和ip6talbes,但是没有安装ip6tables_nat,所以不可以进行 IPV6 的 NAT。
Github 上有人编译了威联通的 ip6tables_nat 包,项目地址。去 release 里头下载modules.zip并解压得到ip6table_nat.ko即可。
通过 ssh 连接到 NAS,执行以下命令,将对应目录挂载到/tmp/config,具体参照网址:
1
| sudo -i mount $(/sbin/hal_app --get_boot_pd port_id=0)6 /tmp/config
|
在目录下新建文件autorun.sh,填入以下内容
1
2
3
4
| /sbin/modprobe ip6_tables
/sbin/modprobe nf_nat
/sbin/modprobe xt_MASQUERADE
insmod /「PATH」/ip6table_nat.ko
|
将文件修改为可执行,并解除挂载:
1
2
| chmod +x /tmp/config/autorun.sh
umount /tmp/config
|
登录 NAS 系统,来到控制台->硬件->常规,对应选项上打勾:

重启 NAS 或者手动执行autorun.sh中的内容。
在 Docker 中创建支持 IPV6 的网络:
1
2
3
4
5
6
7
8
| docker network create \
--ipv6 \
--subnet=xxxx:xxxx:xxxx::/64 \
--gateway=xxxx:xxxx:xxxx::1 \
--subnet=xx.xx.xx.xx/24 \
--gateway=xx.xx.xx.1 \
--opt com.docker.network.bridge.name=ipv6 \
ipv6
|
NAT分为 SNAT 和 DNAT 两种:
- DNAT 修改目标 IP 地址,主要用来进行端口转发,将公网IP端口映射到内网IP端口
- SNAT 修改源 IP 地址,用来解决 IP 地址不足的问题,将内网IP端口修改为公网IP端口
这里要实现的目标不是通过内网的 IPV6 地址访问内网的服务,因为 Docker 原本就有 IPV4 的端口转发,更加简单好记。
所以,只考虑配置 SNAT,使得容器内访问 IPV6 地址,修改 IP 端口,变为 NAS 访问 IPV6 地址,进而交由路由器来处理,方法如下:
1
2
3
| sudo ip6tables -A FORWARD -i ipv6 -o eth0 -j ACCEPT
sudo ip6tables -A FORWARD -i eth0 -o ipv6 -j ACCEPT
sudo ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
|
第 4 节 容器内部
Docker 在创建容器时,一般就自动创建好了容器内的防火墙和NAT规则,规则也比较简单,通常不需要进行任何设置就可以访问 NAS。
一个比较特殊的容器是 WireGuard,其内部可能存在多个网口,比如wg0、wg1、wg2等,需要配置相关的 NAT 规则,这种方式的目的是隔离多个网口,并且每个网口分别做防火墙,提供给不同的人使用。
Docker 部署方式如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| docker run -itd --name=WireGuard --restart always \
--network ipv6 \
--cap-add=NET_ADMIN \
-e PUID=「PUID」 \
-e PGID=「PGID」 \
-e TZ=Asia/Shanghai \
-e SERVERURL=「URL」 \
-e SERVERPORT=「POST」 \
-e PEERS=3 \
-e PEERDNS=「DNS」 \
-e INTERNAL_SUBNET=xx.xx.xx.0 \
-e INTERNAL_SUBNET6=xxxx:xxxx:xxxx:: \
-e ALLOWEDIPS=0.0.0.0/0,::/0 \
-e PERSISTENTKEEPALIVE_PEERS=25 \
-e LOG_CONFS=true \
-p 51820:51820/udp \
-p 51821:51821/udp \
-p 51822:51822/udp \
-v /「PATH」/config:/config \
--sysctl="net.ipv4.conf.all.src_valid_mark=1" \
--sysctl="net.ipv6.conf.all.disable_ipv6=0" \
--sysctl="net.ipv6.conf.all.forwarding=1" \
--sysctl="net.ipv6.conf.default.forwarding=1" \
linuxserver/wireguard:latest
|
需要在容器内使用 IPV6 时,需要添加的有:
1
2
3
| --sysctl="net.ipv6.conf.all.disable_ipv6=0" \
--sysctl="net.ipv6.conf.all.forwarding=1" \
--sysctl="net.ipv6.conf.default.forwarding=1" \
|
其他项不是必备的配置,可以创建以后手动进行修改。
服务器端的配置如下:
1
2
3
4
5
6
7
8
9
10
11
| [Interface]
Address = xx.xx.xx.1/24, xxxx:xxxx:xxxx::1/64
ListenPort = 51820
PrivateKey = xxxxx
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i %i -j ACCEPT; ip6tables -A FORWARD -o %i -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i %i -j ACCEPT; ip6tables -D FORWARD -o %i -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
PublicKey = xxxxx
PresharedKey = xxxxx
AllowedIPs = xx.xx.xx.2/32, xxxx:xxxx:xxxx::2/128
|