RobertMe schreef op maandag 16 januari 2023 @ 16:56:
Wellicht dat ik hier met mijn, wat meer high level, vraag terecht kan.
Momenteel heb ik een Proxmox machine met daarop enkele LXC containers waarin vervolgens Docker draait. De reden dat ik dit zo heb is omdat ik in mijn netwerk meerdere VLANs heb en daarmee cintainers in het juiste VLAN kan plaatsen. Zo heb ik bv een LXC "controller.iot" waar dan Docker in draait met bv Home Assistant, een SQL Database, DSMR Reader, .... Waarbij alles dus in het IoT VLAN zit en poorten die ik open zet ook alleen in het IoT VLAN bereikbaar zijn. En hetzelfde idee heb ik dan gedaan met meerdere zaken. Een LXC vanuit Proxmox waarbij de LXC aan één specifiek VLAN gekoppeld is en port forwards etc alleen vanuit dat VLAN bereikbaar zijn en het spul in de LXC / docker dus ook niet met andere VLANs kan communiceren.
An zich werkt dit prima, alleen valt dat extra laagje mij tegen. In principe heb ik nu niet één OS dat ik moet onderhouden (Proxmox / dat wat bare metal draait), maar een stuk of 10 (Proxmox + de LXCs). Waarbij dus elk geupdate moeten worden, ik soms 2 keer zaken moet mappen (Proxmox => LXC => Docker), etc. Qua onderhoud niet ideaal dus.
Intussen heb ik een nieuw mini PCtje gekocht en wil ik hier nog eens goed naar kijken. En dus of ik niet bv gewoon Debian kan draaien met Proxmox en alles overal juist in prop. Daarnaast wil ik ook "Docker Docker" evalueren of ik dat wil blijven gebruiken, of bv de overstap maken naar Podman (lees: een alternatieve OCI compliant runtime).
Voordeeltje wat ik daarbij zie bij Podman is dat dit een "pod" concept heeft, net zoals Kubernetes. Waarbij je meerdere containers in dezelfde namespace kunt plaatsen en bv zaken als networking gewoon "localhost" kunnen gebruiken binnen dezelfde pod (bv Home Assistant die via localhost met Mosquito verbind, omdat ze dezelfde netwerk namespace / netwerk interface delen). Alleen zie ik dus niet hoe ik bv een complete pod dan aan een eigen interface / VLAN kan koppelen. Maar bij Docker lijkt dit ook nogal complex te regelen te zijn (voorbeeld dat ik een tijdje terug gevonden heb zet volledige iptables / firewall management van Docker uit. Waardoor zelf de regels aangemaakt kunnen worden en dus ook SNAT toegepast kan worden om alle verkeer vanaf een Docker bridge uit te sturen / te NATen over een specifieke andere interface (/VLAN)).
Heeft iemand hier dus (toevallig) kennis van en/of ervaring met dit soort zaken? Dus in het kort "specifieke containers v.w.b. het netwerk specifieke VLANs laten gebruiken, zowel voor inkomende als uitgaande verbindingen".
De reden dat ik overigens wel in de "Docker" (/Podman / ...) hoek wil blijven is, uiteraard, het gemak. Gewoon een pull + "up" / recreate en de boel is bijgewerkt. Alternatief zou in principe LXC kunnen zijn, zonder Docker er in dan, maar dan heeft elk "programma" natuurlijk weer zijn eigen installatie & update methode, dependencies die allemaal moeten kloppen, etc.
Intussen heb ik hier een stapje in weten ze zetten en iets dat waarschijnlijk een oplossing is gevonden.
Note: intussen ben ik terug naar Docker nadat ik met Podman tegen wat issues aan liep en daarnaast een waarschijnlijk foute configuratie doodleuk het hele systeem deed nuken met een rm -rf achtige situatie die ik ook, uiteindelijk meermaals, na herinstallaties, in / heb uitgevoerd. Dus zeg maar rm -rf / en voordat ik het in de gaten had waren letterlijk alle bestanden foetsie.
In essentie is het idee / de werking eigenlijk vrij simpel. Een container die naar buiten gaat doet dit via de bridge en vervolgens doet Linux een gewone lookup in de routing tabel v.w.b. hoe het verkeer naar buiten gaat. Daarna wordt door de firewall in postrouting een masquarade regel toegepast zodat het verkeer afkomstig is/lijkt vanaf de uitgaande "fysieke" interface (door dus het source IP te veranderen naar dat van de uitgaande interface). De simpele fix is dus policy based routing inregelen waarbij verkeer vanaf de bridge(s) een andere routing tabel gebruikt met daarbij een andere default route het internet op.
Het inregelen van dit komt vervolgens wat ik zo zie altijd neer op 3 zaken:
- De routing tabel aanmaken en vullen
- Het verkeer vanaf de bridge (/verkeer dat je gebruik wilt laten maken van deze routing tabel) markeren, in de firewall
- Een regel (in de routing) die ervoor zorgt dat het verkeer gemarkeerd met X wordt verwerkt door routing tabel Y
Echter lijkt mij dit ook te lukken in 2 stappen. Wellicht een recentere toevoeging. In ieder geval is het (tegenwoordig?) mogelijk om 3 te doen op basis van een interface.
Zelf maak ik gebruik van systemd-networkd voor het configureren van de interfaces, aanmaken van de VLANs, etc. En die kan dat allemaal voor mij inregelen zo blijkt.
Hierbij kom ik dan uit op onderstaande snippet in het reeds bestaande
/etc/systemd/network/....network bestand:
INI:
1
2
3
4
5
6
7
8
9
10
11
| [RoutingPolicyRule]
IncomingInterface=br-iot
Table=500
[Route]
Gateway=192.168.20.1
Table=500
[Route]
Gateway=_ipv6ra
Table=500 |
Te beginnen met de twee
[Route] blokken. Deze voegen de regels toe aan de routing tabel en maken deze eventueel aan. Uiteraard opletten dat je geen ids gebruikt die al bestaan (default sowieso 255 voor local, 254 voor main en 253 voor broadcast). In dit geval geef ik dus hardcoded een (default) gateway op voor IPv4. En daarnaast wordt de gateway voor IPv6 ingesteld maar komt deze uit de router advertisement (ik gebruik geen statisch IPv6 adres, maar wel een statische "token" / suffix). Het resultaat hiervan kan worden gecontroleerd met ip route:
robert@server:~$ ip -4 route show table 500
default via 192.168.20.1 dev iot proto static
robert@server:~$ ip -6 route show table 500
default via fe80::822a:a8ff:fecd:c7e6 dev iot proto ra metric 1024 expires 1500sec pref high
robert@server:~$
Als uitleg: dev iot verwijst naar de netwerk interface die ik ook lan, prive, iot etc heb genoemd i.p.v. ethX of enpXsY. Het voorbeeld snippet staat dan ook in /etc/systemd/network/iot.network met een [Match] op Name=iot
Het
[RoutingPolicyRule] blok zorgt er vervolgens voor dat deze routing tabel wordt toegepast op verkeer met als inkomende interface de
br-iot interface. Dit is te controleren met
ip rule:
robert@server:~$ ip rule
0: from all lookup local
32762: from all iif br-iot lookup 500 proto static
32766: from all lookup main
32767: from all lookup default
En dan hoor ik jullie denken "waar komt
br-iot vandaan?". Dat is de naam van de bridge die Docker aanmaakt voor het aangemaakte netwerk, middels:
docker network create --opt com.docker.network.bridge.name=br-iot br-iot
Met de opt geef ik dus zelf een naam aan het netwerk zodat ik deze makkelijk kan herkennen en kan opnemen in configuraties. Dit i.p.v. de random naam die Docker gebruikt.
De inspiratie voor de oplossing komt uit deze blogpost:
https://stewartadam.io/bl...ecific-outgoing-interface . Hoe het in systemd-networkd te regelen heb ik zelf uitgezocht op basis van de man pages.
Mocht dit niet werken omdat de
IncomingInterface toch ander gedrag vertoont dan werken met een firewall mark kan ook dat. Dit simpelweg door
FirewallMark= te gebruiken met dan het nummertje van de mark. Dit uiteraard gecombineerd met een firewall mark dat geset wordt door de kernel, in te stellen via iptables/firewalld/nftables.
Om het mijzelf extea moeilijk te maken heb ik zelf iptables volledig uit gezet in Docker. Zodat ik vervolgens alles, met de hand, kan/moet doen in nftables..
Daarnaast kan elke
Table= i.p.v. met een nummertje ook met een naam. Deze naam moet je echter wel in systemd-networkd instellen en werkt ook alleen daarbinnen. Dit kan door aan
/etc/systemd/networkd.conf onder het
[Network] blok een of meerdere
RouteTable=<naam>:<nummer> regels toe te voegen, bv:
CC @PuijkeN omdat ik hierover toentertijd ook wat in het zuinige server topic heb gepost en hij ook interesse had in een oplossing hiervoor.