!请参阅末尾的更新帖子!
我正在尝试在远程主机上使用 docker compose 在容器中运行 Wireguard,该容器采用站点到站点配置,并且我家里的内部网可以自行完美运行。
Wireguard 配置
wg0.conf
[Interface]
PrivateKey = ***
Address = 10.6.0.4/24
ListenPort = 12345
DNS = 208.67.222.222, 208.67.220.220
PostUp = iptables -t nat -A POSTROUTING -o wg+ -j MASQUERADE
PreDown = iptables -t nat -D POSTROUTING -o wg+ -j MASQUERADE
[Peer]
PublicKey = LXSxUv5lp9A2WOz5mV33GQa5jpJYJ04j4Rl6FWlnczA=
PresharedKey = ***
Endpoint = vpn.example.com:12345
AllowedIPs = 192.168.178.0/24, 10.6.0.0/24
Docker 撰写文件
docker-compose.yml
version: '3.*'
networks:
outside:
external: true
wireguard_ghf68:
internal: true
driver: "bridge"
ipam:
config:
- subnet: 10.7.3.0/24
services:
wireguard:
image: lscr.io/linuxserver/wireguard:latest
container_name: wireguard
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
volumes:
- './wireguard:/config'
- '/lib/modules:/lib/modules:ro'
networks:
wireguard_ghf68:
ipv4_address: 10.7.3.3
outside: {}
ports:
- target: 51820
published: 51820
protocol: "udp"
mode: "host"
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv4.ip_forward=1
restart: unless-stopped
之后,docker compose up
我可以将 shell 附加到容器并 ping 我内联网中的所有端点(192.168.178.0/24)。
root@wireguard:/# ping -c1 192.168.178.25
PING 192.168.178.25 (192.168.178.25) 56(84) bytes of data.
64 bytes from 192.168.178.25: icmp_seq=1 ttl=63 time=47.6 ms
--- 192.168.178.25 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 47.640/47.640/47.640/0.000 ms
使用 homeassistant 的 Docker 编写文件
docker-compose.yml
:
services:
homeassistant:
container_name: "homeassistant"
image: "ghcr.io/home-assistant/home-assistant:stable"
depends_on:
- "wireguard"
cap_add:
- NET_ADMIN
volumes:
- ./homeassistant:/config
- /etc/localtime:/etc/localtime:ro
environment:
- PUID=1000
- PGID=1000
- "TZ=Europe/Berlin"
restart: unless-stopped
networks:
wireguard_ghf68:
ipv4_address: 10.7.3.2
之后,docker compose up
我可以将 shell 附加到“homeassistant”并 ping wireguard 容器。但我还希望能够从那里 ping 我的内联网 (192.168.178.0/24)。因此,我在容器内创建了一条新路由:
ip -4 route add 192.168.178.0/24 via 10.7.3.3
其结果是这些路线:
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.7.3.1 0.0.0.0 UG 0 0 0 eth0
10.7.3.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
192.168.178.0 10.7.3.3 255.255.255.0 UG 0 0 0 eth0
现在理论上我应该能够 ping 通我的内联网但却不行。
root@homeassistant:/# ping -c1 10.7.3.3
PING 10.7.3.3 (10.7.3.3): 56 data bytes
64 bytes from 10.7.3.3: seq=0 ttl=64 time=0.147 ms
--- 10.7.3.3 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.147/0.147/0.147 ms
root@homeassistant:/# ping -c1 192.168.178.1
PING 192.168.178.1 (192.168.178.1): 56 data bytes
--- 192.168.178.1 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss
Docker 主机
但是,如果我在 docker 主机上创建相同的路由,我就能够从该主机访问我的内联网,但仍然不能从“homeassistant”容器内访问。
root@dockerhost:~$ ip -4 route add 192.168.178.0/24 via 10.7.3.3
root@dockerhost:~$ ping -c1 192.168.178.25
PING 192.168.178.25 (192.168.178.25) 56(84) bytes of data.
64 bytes from 192.168.178.25: icmp_seq=1 ttl=62 time=43.8 ms
--- 192.168.178.25 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 43.826/43.826/43.826/0.000 ms
问题
我做错了什么?我是否缺少一些路由或 sysctl 配置或其他内容cap_add
?为什么它在 docker 主机上可以工作,但在容器内却不行?
如果您需要更多信息,请询问。
更新
我终于找到了罪魁祸首。当docker创建网络时,它还会创建以下iptables规则:
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
...
-A DOCKER-ISOLATION-STAGE-1 ! -s 10.7.3.0/24 -o br-a575507e42d8 -j DROP
-A DOCKER-ISOLATION-STAGE-1 ! -d 10.7.3.0/24 -i br-a575507e42d8 -j DROP
...
-A DOCKER-USER -j RETURN
删除这两条DROP
规则后,路由就可以按预期工作了。
我认为更好的方法是向链中添加一些规则,DOCKER-USER
这些规则在丢弃数据包之前会接受它们。
如果我找到一种优雅的方法来动态添加这些规则,我会自己回答我的问题。
答案1
我现在将回答我自己的问题,因为我找到了一种目前对我来说似乎最好的方法。
首先,我为 docker 网络指定一个静态网桥名称,在本例中d-wg
为(docker-wireguard 的缩写)。请记住,最大长度为 15 个字符或 16 个字节(包括空字符)(请参阅如果)
networks:
wireguard:
driver: "bridge"
internal: true
name: "wireguard"
ipam:
driver: "default"
config:
- subnet: 10.7.3.0/24
driver_opts:
com.docker.network.bridge.name: d-wg
现在docker compose up
将创建一个新的docker网络,其wireguard
内核级别的接口名称为d-wg
:
$ docker network ls -f name=wireguard
NETWORK ID NAME DRIVER SCOPE
648c3d39638b wireguard bridge local
$ ip link show d-wg
289: d-wg: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:43:xx:yy:zz brd ff:ff:ff:ff:ff:ff
现在我可以轻松地在DOCKER-USER
链中创建两个 iptables 规则,允许10.7.3.0/24
本地和192.168.178.0/24
远程两个子网的流量。然后确保已经存在的规则-A DOCKER-USER -j RETURN
再次遵循这些规则:
$ iptables -A DOCKER-USER -d 192.168.178.0/24 -i d-wg -o d-wg -j ACCEPT
$ iptables -A DOCKER-USER -d 10.7.3.0/24 -i d-wg -o d-wg -j ACCEPT
# Add a new return rule
$ iptables -A DOCKER-USER -j RETURN
# Delete the old one (deletes always the first if there are multiple rules)
$ iptables -D DOCKER-USER -j RETURN
现在,我还必须homeassistant
在容器启动时在容器内创建新路由。它的原始入口点是/init
,我想wireguard
在此之前创建到容器的新路由。因此,我创建了一个小脚本init-script.sh
,它将是新的入口点,并将其安装到容器中:
初始化脚本
#!/bin/bash
ip route add 192.168.178.0/24 via 10.7.3.3
# 'exec' is necessary to not spawn a new process with an other PID than 1.
exec /init
这是我docker-compose.yml
为家庭助理提供的新产品。
services:
homeassistant:
container_name: "homeassistant"
image: "ghcr.io/home-assistant/home-assistant:stable"
cap_add:
- NET_ADMIN
volumes:
- ./homeassistant:/config
- ./init-script.sh:/init-script.sh:ro
- /etc/localtime:/etc/localtime:ro
environment:
- PUID=1000
- PGID=1000
- "TZ=Europe/Berlin"
entrypoint:
- /init-script.sh
restart: unless-stopped
networks:
traefik:
wireguard:
ipv4_address: 10.7.3.2
另一个想法是创建一个新的 systemd 服务,该服务在 docker 服务启动后自动创建 iptables 规则。也许我会在完成此操作后立即编辑此答案。