特定子网的路由容器到 wireguard 容器无法按预期工作

特定子网的路由容器到 wireguard 容器无法按预期工作

!请参阅末尾的更新帖子!

我正在尝试在远程主机上使用 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 规则。也许我会在完成此操作后立即编辑此答案。

相关内容