Маршрутизация контейнера для защиты контейнера для определенной подсети не работает должным образом

! Смотрите обновленный пост в конце!

Я пытаюсь запустить Wireguard в контейнере, используя docker Compose на удаленном хосте в конфигурации «сеть-сеть» с моей домашней интрасетью, которая сама по себе работает безупречно.

Конфигурация 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

      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

После этого я могу подключить оболочку к контейнеру и пропинговать все конечные точки в моей интрасети (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

Docker создает файл с помощью HomeAssistant

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Я могу прикрепить оболочку к «homeassistant» и пропинговать контейнер Wireguard. Но я также хочу иметь возможность пинговать свою интрасеть (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

Теперь я теоретически должен иметь возможность пропинговать свою интрасеть, но это не работает.

      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

Докер-хост

Однако если я создам тот же маршрут на хосте докера, я смогу получить доступ к своей интрасети с этого хоста, но все равно не из контейнера «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? Почему он работает с хоста докера, а не изнутри контейнера?

Если вам нужна дополнительная информация, пожалуйста, спросите.

Обновлять

Наконец-то я нашел виновника. Когда докер создает сеть, он также создает следующие правила 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-wireguard). Имейте в виду, что максимальная длина составляет 15 символов или 16 байт, включая нулевой символ (см. if.h ).

      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создаст новую сеть докеров с именем и именем интерфейса на уровне ядра.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

Теперь я могу легко создать два правила iptables вDOCKER-USERцепочка, которая разрешает трафик для двух моих подсетей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которая будет новой точкой входа, и смонтируйте ее в контейнер: 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, которая автоматически создает правила iptables после запуска службы докеров. Возможно, я отредактирую этот ответ, как только сделаю это.

Другие вопросы по тегам