Маршрутизация контейнера для защиты контейнера для определенной подсети не работает должным образом
! Смотрите обновленный пост в конце!
Я пытаюсь запустить 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 после запуска службы докеров. Возможно, я отредактирую этот ответ, как только сделаю это.