Pi som VPN-klient: en hanterare eller ett eget skript?
Jämför en NetworkManager-profil med ett handskrivet klientskript på en Raspberry Pi, inklusive full tunnel, DNS, IPv6 och beteendet när tunneln faller.
En Raspberry Pi som bara behöver skydda sin egen utgående trafik har två realistiska konstruktioner: låt nätverkshanteraren äga tunneln, eller behåll ett skript som äger den. Båda kan bära trafik. De misslyckas på olika ställen, och det fel du kan diagnostisera på distans klockan två på natten är det som är värt att välja. Jämför dem på ägarskap, på vad full tunnel betyder när IPv6 och DNS är inblandade, och på vad som händer i det ögonblick tunneln faller.
Två ägare för en tunnel
Konstruktion A är en profil inuti systemets nätverkshanterare: en importerad OpenVPN-profil eller en inbyggd WireGuard-anslutning, aktiverad vid start av samma tjänst som reser Ethernet och Wi-Fi. Konstruktion B är en egen enhet som kör klienten direkt, redigerar resolverkonfigurationen och installerar sina egna rutter och brandväggsregler. Båda är legitima; de skiljer sig i vem som får ändra routingtabellen.
Felen skiljer sig eftersom ägarna skiljer sig. Med A lämnar en profil som inte aktiveras gränssnittet uppe och tunneln nere, och hanterarens loggar säger varför. Med B lämnar ett skript som lyckas halvvägs rutter som överlever processen som skapade dem. Att köra båda är det sämsta utfallet: två processer skriver standardrutten, två resolverredigeringar tävlar vid start och två uppsättningar hemligheter måste roteras.
Välj en ägare per tunnel och inaktivera den andra innan du testar något. Två ägare betyder också två ställen att titta på när tunneln är uppe och ingenting löses. Den enda regeln hindrar de flesta av de förvirrande tillstånd som beskrivs i Pi VPN-trådar.
Vad en import skapar, och vad den inte skapar
På Raspberry Pi OS Bookworm och senare är standardverktyget för nätverkskonfiguration NetworkManager, så en profil är det naturliga hemmet för en klienttunnel. Importen kräver rätt insticksmodul installerad; utan den misslyckas importen med ett meddelande om en anslutningstyp som inte stöds, vilket är den ärliga signalen att stanna och installera insticksmodulen.
nmcli connection import type openvpn file ./provider.ovpn
nmcli connection show
nmcli --ask connection up id provider-name
Import sparar en profil. Det bevisar inte att andra sidan är nåbar, och det garanterar inte att varje direktiv i filen överlevde konverteringen. Läs tillbaka den resulterande anslutningen och kontrollera delarna som bär identitet och tillit: certifikatet, fjärrvärden och porten, och om ett lösenord efterfrågas eller lagras.
Aktivering med --ask håller en lösenfras utanför skalkommandohistoriken på en delad maskin. För en Pi utan tillsyn vill du så småningom att profilen reser sig vid start, vilket är ett separat beslut från att resa den en gång för hand.
Full tunnel, jämförd över båda konstruktionerna
En full tunnel är ett påstående om rutter, inte om avsikt, och de två konstruktionerna uttrycker det olika. Profilmetoden ber hanteraren installera en standardrutt och sina resolverinställningar; skriptmetoden gör vad skriptet säger, inklusive ingenting. Ingen av konstruktionerna täcker IPv6 om den inte får besked, och en orörd IPv6-standardrutt bär gärna trafik utanför tunneln medan IPv4 ser perfekt ut.
ip -4 route get 198.51.100.23
ip -6 route show default
resolvectl query example.org
Använd en dokumentationsadress för ruttuppslagningen och ersätt den med en värd du får undersöka. Det första kommandot visar vilket gränssnitt Pi skulle använda för ett vanligt mål; det andra visar om en IPv6-standardrutt fortfarande finns; det tredje bevisar vilken resolver som svarade. Läs alla tre innan du tror på något påstående om en full tunnel.
Om leverantörens instruktioner lovar en full tunnel men IPv6-standardrutten överlever, läcker Pi:n. Inaktivera IPv6 på upplänken eller be profilen att kräva det, och besluta medvetet i stället för att upptäcka det senare.
Fail-open mot fail-closed
När tunneln faller faller en routingtabell som förlorar sin standardrutt helt enkelt tillbaka till den fysiska upplänken, och trafiken fortsätter öppet. Det är fail-open, och det är standardbeteendet för båda konstruktionerna om inget hindrar det. De flesta upptäcker det veckor senare genom att läsa en brandväggslogg.
Fail-closed kostar dig fjärråtkomst när tunneln är nere, så det behöver ett medvetet undantag för hanteringsvägen. Ett sätt är en regel som bara tillåter VPN-partnerns adress utanför tunneln:
sudo nft add table inet vpnkill
sudo nft 'add chain inet vpnkill out { type filter hook output priority 0; policy accept; }'
sudo nft add rule inet vpnkill out oifname != "tun0" ip daddr != 203.0.113.24 reject
Jämför driftskostnaden ärligt. En profil kan oftast inte uttrycka den här regeln, så fail-closed trycker dig mot skriptkonstruktionen eller mot en liten enhet som installerar regeln oberoende av tunneln. Hur som helst bör du logga avvisningarna i en vecka: en tyst avvisning ser identisk ut med en trasig tjänst, och du kommer att vilja ha räknaren när en användare rapporterar att en webbplats slutade laddas.
Att välja för en Pi utan skärm
Om avbildningen redan kör NetworkManager, tunneln inte behöver något exotiskt och du vill att den ska följa upplänken automatiskt, ta profilen. Om du behöver policyrouting, flera tunnlar valda per destination, eller en fail-closed-regel som måste finnas innan tunneln reser sig, ta skriptet och äg varje del av det, inklusive startordning och städning. Att blanda de två konstruktionerna kostar mer än var och en för sig.
Migrering mellan dem är mest en fråga om ordning: inaktivera den gamla ägaren, bekräfta att ingen föråldrad rutt eller resolverpost överlever, aktivera sedan den nya och starta om två gånger. En enda omstart kan dölja ett race som en andra avslöjar. Avsluta med att testa Pi:n i stället för tunneln: lös upp ett namn, hämta något över IPv4, hämta över IPv6 om du behöll det, släpp sedan tunneln och bekräfta att beteendet du valde faktiskt inträffar. Ett gränssnitt i ip link är början på testet, inte slutet.