Een tweede OpenVPN-client op één Linux-host stapelen mislukt op een van twee plaatsen. Ofwel kan de binnenste client zijn eigen server niet bereiken omdat de buitenste tunnel dat transport heeft overgenomen, ofwel herschrijft de tweede client de routetabel en werkt de eerste tunnel niet meer. Beide problemen zitten in de routing, niet in de versleuteling, en beide zijn opgelost voordat de nieuwe tunnel enig applicatieverkeer draagt. De adressen hier zijn illustratief: tun0 is de buitenste tunnel, 203.0.113.9 is de binnenste server, en TCP 9999 is de stroom die het binnenste pad moet gebruiken.

Twee faalwijzen, één routetabel

Een binnenste client opent een UDP-socket naar zijn server, en die socket volgt de huidige routetabel zoals elke andere. Als de buitenste tunnel al de standaardroute bezit, gaan de handshake-pakketten de buitenste tunnel in. Dat werkt alleen als de buitenste server de binnenste server kan bereiken en de andere kant ermee instemt de stroom te dragen; anders stokt de handshake en probeert de client het opnieuw.

De tweede fout komt van de push van de server. De gebruikelijke push is redirect-gateway def1, die 0.0.0.0/1 en 128.0.0.0/1 installeert in plaats van de standaardroute te vervangen. Twee clients die elk hun eigen /1-paar installeren botsen daardoor, en het nieuwste paar wint voor bijna elke bestemming. Zoek die twee prefixes in de tabel wanneer een tunnel sterft nadat de tweede verbindt, want de standaardroute zelf is misschien nooit veranderd.

Markeer de stroom en geef hem een tabel

Stuur het geselecteerde verkeer door het in de mangle-tabel te markeren en de mark in een beleidsregel te matchen. De regel wijst naar een eigen tabel waarvan de standaardroute het binnenste apparaat is, zodat alleen gemarkeerde pakketten het binnenste pad nemen.

iptables -t mangle -A PREROUTING -p tcp -m multiport --dport 9999 -j MARK --set-mark 0x9999
ip rule add fwmark 0x9999 table 200 pref 1000
ip route add default dev tun1 table 200

De meeste fouten in deze regelset komen van twee vergissingen. De markwaarde die de firewall zet en de waarde die de regel matcht moeten identiek zijn, want een verschil levert geen fout en geen effect op. En PREROUTING ziet alleen pakketten die van buiten komen, dus een verbinding die op deze host begint heeft een OUTPUT-regel met dezelfde match nodig voordat hij überhaupt gemarkeerd wordt.

Neem het route-eigendom van de tweede client af

Twee opties weerhouden de tweede client ervan de tabel te bewerken, en ze werken op verschillende punten. route-nopull negeert de gepushte route-opties, dus er wordt niets geïnstalleerd. route-noexec accepteert de gepushte opties in de omgeving van de client maar laat de tabelwijziging aan een hook over, en dat is wat je wilt wanneer je moet zien wat de server aanbood.

De hook draait met de gepushte waarden in zijn omgeving, en hij hoort alleen de vermeldingen toe te voegen die je hebt goedgekeurd. Maak hem idempotent: leeg de tabel die hij bezit voordat je hem opnieuw opbouwt, en ga er niet van uit dat de vorige run een schone toestand heeft achtergelaten. Een herverbinding roept de hook opnieuw aan, en een hook die toevoegt zonder te legen hoopt dubbele routes op.

Kies één eigenaar en schrijf die op. Een profiel dat beide opties instelt, of een hook die concurreert met een handmatig script, levert een tabel op die niemand later kan uitleggen.

Leg beide serveradressen vast

De binnenste server heeft een hostroute via het buitenste apparaat nodig voordat de binnenste client verbindt. Die vermelding houdt het binnenste transport in de buitenste tunnel in plaats van het de standaardroute te laten volgen.

ip route add 203.0.113.9/32 dev tun0
ip route get 203.0.113.9

Voeg ook een bijpassende hostroute voor de buitenste server toe in de hoofdtabel, anders kan een herverbinding het transport van de buitenste client zelf in zijn eigen tunnel routeren. De volgorde is niet optioneel: voeg deze vermeldingen toe nadat de betreffende interface bestaat, wat een hook of script betekent dat na de verbinding draait. Een route naar een apparaat dat omlaag is wordt verwijderd of genegeerd, dus een vermelding die bij het opstarten is geschreven overleeft de volgorde die je aannam niet.

Controleer de opzoeking in plaats van de hele tabel te lezen. De uitvoer van de routeopzoeking noemt het apparaat en het bronadres dat de kernel zal gebruiken, en dat is precies wat de client krijgt wanneer hij zijn socket opent.

Test de keten en maak falen zichtbaar

Leg vast op de buitenste interface terwijl de binnenste client verbindt. De binnenste handshake hoort als versleutelde payload in de buitenste tunnel te verschijnen; als hij als gewone UDP naar 203.0.113.9 verschijnt, ontbreekt de hostroute en is de keten nooit gebouwd.

Een keten die stil terugvalt op de fysieke uplink is de slechtste uitkomst, omdat de interfacestatus niets aankondigt en het binnenste verkeer van het verkeerde bronadres vertrekt. Als de tweede client direct opnieuw kan verbinden wanneer de buitenste tunnel wegvalt, schakel dat pad dan uit of bind het aan de buitenste interface zodat het falen zichtbaar is.

Sluit af met twee stromen en één regeldump: een gemarkeerde en een ongemarkeerde verbinding horen verschillende paden te nemen, en ip rule show hoort de prioriteit te tonen die je bedoelde. Als beide stromen hetzelfde pad nemen, wordt de mark niet toegepast waar je denkt.

Gerelateerde artikelen