Drie Linux-hosts, één ontwerp: een publiek bereikbare OpenVPN-hub, een consumentenlaptop en een uitgangsmachine achter een gewone thuisrouter die niets naar binnen doorstuurt. De uitgang belt zelf naar de hub. Daarna gaat het internetverkeer van de consument op de laptop de tunnel in en verlaat het via de eigen uplink van de uitgang. Alles wat verrassend is aan deze opzet volgt uit één detail: de uitgang begon de verbinding, dus het pad naar binnen bestaat al en er is geen port forwarding nodig om het te maken.

Het volgende volgt het pad van het pakket in plaats van de configuratievolgorde, omdat de configuratie pas zin heeft als je weet welke component welke beslissing neemt. Elke laag neemt precies één beslissing, en de fouten verzamelen zich waar twee lagen voor één worden aangezien.

Twee transportsessies, één innerlijk pad

Elke host houdt zijn eigen buitenste verbinding met de hub, en geen van die verbindingen weet van de andere. Het verkeer van de consument naar het internet is een aparte innerlijke stroom die toevallig in de tunnel van de consument wordt gedragen en daarna opnieuw uit de tunnel van de uitgang wordt uitgezonden. Die innerlijke stroom moet de hub routeren, en die moet de uitgang doorsturen.

outer: consumer --TLS--> hub
outer: exit     --TLS--> hub
inner: consumer --tun--> hub --internal--> exit --tun--> uplink

Omdat beide buitenste verbindingen vanaf de clientzijde naar buiten gaan, ziet de router van de uitgang nooit een inkomende SYN en heeft hij nooit een destination-NAT-regel nodig. Hij hoeft alleen het opgezette transport in leven te houden, wat hij al doet voor elke langdurige verbinding. Niets op het openbare internet hoeft eerst de uitgang te bereiken, daarom is een dynamisch adres op die uplink geen probleem. De router moet de uitgaande sessie nog steeds toestaan, maar dat is dezelfde toestemming die een browser al gebruikt.

Wat iroute werkelijk in de server installeert

De hub is het enige apparaat dat beslist welke peer welke bestemming bezit, en hij neemt die beslissing in zijn eigen userspace-routingtabel, niet in die van de kernel. Een client-config-dir-regel voor de uitgang met de naam exit-node kan daar het standaardprefix opeisen:

# /etc/openvpn/server/ccd/exit-node
iroute 0.0.0.0 0.0.0.0

Die regel zegt tegen het serverproces "innerlijke pakketten met welke bestemming dan ook horen bij deze client". Hij voegt geen standaardroute toe aan het besturingssysteem van de hub en maakt de hub geen internetgateway. OpenVPN 2.6-referentie houdt cliëntspecifieke interne routering en kernelroutering als aparte mechanismen, en die scheiding is de reden dat dit ontwerp werkt zonder de standaardroute van de hub aan te raken.

De consument heeft de spiegelinstructie nodig, geleverd als push, zodat zijn eigen verkeer de tunnel in gaat in plaats van via zijn lokale gateway naar buiten te gaan. Bij subnettopologie is de pool een verbonden route op het tun-apparaat van de uitgang, dus de terugrichting heeft geen extra push nodig; wat de uitgang niet mag ontvangen is de omleiding hieronder, die zijn eigen uplinkverkeer terug zou sturen naar de tunnel die hij probeert te gebruiken. Stuur hem alleen naar de consument en lees de toegepaste routes op de client in plaats van te vertrouwen op de keuze van het CCD-bestand.

# /etc/openvpn/server/ccd/consumer-01
push "redirect-gateway def1"

De kernel van de uitgang doet het doorsturen

Binnen de uitgang komt een pakket van de consument aan op de tun-interface, geadresseerd aan een of andere internethost. De kernel raadpleegt zijn routingtabel, vindt de echte uplink als de beste overeenkomst en past dan twee onafhankelijke beleidsregels toe: mag dit pakket überhaupt worden doorgestuurd, en mag het via die interface met dat bronadres naar buiten. Doorsturen is een sysctl, en hij staat bij veel distributies standaard uit.

De uitgang heeft dus permanent ingeschakeld IPv4-doorsturen nodig, een regel die de tunnelbronnen van de consument naar de uplink toelaat, en de antwoordweg terug door de tun. Doorsturen voor de hele host inschakelen verandert ook het gedrag van alles wat erdoorheen routeert, daarom moet de regel de bronnen en interfaces noemen in plaats van alles te accepteren.

Bronvertaling is wat de terugweg koopt

Een internethost heeft geen route naar een 10.90.0.x-adres, dus antwoorden zouden worden weggegooid voordat ze de uitgang ooit bereikten. De bron herschrijven terwijl het pakket de uplink verlaat lost dat op, en connection tracking draait de herschrijving op de terugweg terug.

sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -s 10.90.0.0/24 -o enp1s0 -j MASQUERADE
ip -4 route get 10.90.0.9   # run on the exit: must point at the tun

De regel hierboven is een uitgangspunt voor één uplink-interface, geen firewallbeleid. Vernauw de bron tot de toewijzingen die je echt hebt geautoriseerd, en houd het eigen invoerbeleid van de uitgang apart, zodat masquerading voor tunnelverkeer niet stilletjes lokale diensten blootlegt. Op een host met een dynamisch uplinkadres bespaart masquerading je ook het adres met de hand te volgen.

Waar de hub kan handhaven en waar niet

Met client-to-client en het traditionele userspace-gegevenspad geeft de hub innerlijke pakketten tussen peers door binnen het serverproces. Ze gaan nooit door de kernel-FORWARD-keten van de hub, dus een kernelregel daar kan het verkeer tussen peers niet beperken, hoe zorgvuldig die ook is geschreven. Handhaving moet in het eigen beleid van de server leven: wie mag verbinden, welk CCD-bestand hij krijgt en of het standaardbezit van de uitgang überhaupt wordt toegekend.

Daarom is een standaard-iroute een brede vertrouwensbeslissing. De uitgang moet rechtmatig terugverkeer van willekeurige internetadressen accepteren, en het moet een machine zijn die je beheert, patcht en monitort. Hetzelfde bezit geven aan een peer aan wie je geen router zou toevertrouwen is de fout waartoe dit ontwerp uitnodigt.

Lidmaatschap is de echte controle. Elke client die zijn eigen routes kan configureren, kan proberen de uitgang te gebruiken, dus beperken wie mag verbinden, of welke peers de uitgang bedient, hoort bij het ontwerp en is geen nabedacht.

Veilig falen wanneer de uitgang verdwijnt

Haal de uitgang offline en het interne standaardbezit verdwijnt met zijn sessie. Wat niet mag gebeuren is dat de hub stilletjes de vervangende uitgang wordt: geen standaardroute van de VPN-pool naar de uplink van de hub, en geen brede masquerade-regel op de hub die hetzelfde verkeer graag zou doorsturen. Test die storing opzettelijk, en test daarna het herstel met een nieuwe stroom in plaats van een pagina die al open was.

# on the consumer, with the exit up and then down
ip -4 route get 1.1.1.1
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com

Als het verkeer van de consument na het verdwijnen van de uitgang nog steeds het internet bereikt, draagt iets anders het en bestaat de grens die je bedoelde niet. Behandel DNS en IPv6 ook als aparte beslissingen: een IPv4-route plus masquerading zegt niets over AAAA-verkeer, en een resolver die de hub duwt, antwoordt vanaf waar de query werkelijk naar buiten gaat.

Gerelateerde artikelen