WireGuard imbricat: saltul din mijloc nu poate răspunde pe propriul port
Când saltul din mijloc trimite totul într-un singur tunel, răspunsurile către serviciul său de intrare pleacă pe calea greșită. O regulă de port sursă la interfață rezolvă asta.
Trei salturi WireGuard ajung la o rețea fără adresă publică, iar apoi mașina din mijloc nu mai răspunde pe portul pe care trebuia să asculte. Tunelul mai departe încă funcționează, gazda îndepărtată încă răspunde, și doar serverul din mijloc a tăcut din exterior. Combinația aceea este derutantă exact pentru că e auto-provovată: saltul din mijloc ascultă de propriul tabel de rutare, iar tabelul îi trimite răspunsurile în direcția greșită.
Un lanț care funcționează până vorbești cu cutia din mijloc
Luați un lanț ilustrativ: clientul A ajunge la serverul B pe UDP 500, iar B merge mai departe la serverul C pe UDP 500. C acceptă trafic doar de la adresa singurului său partener, ceea ce îl face o frunză și îi păstrează comportamentul simplu. B nu este o frunză, și tot ce e interesant la această defecțiune se întâmplă acolo.
Distincția contează pentru că rolurile de frunză și de mijloc trag în direcții opuse. O frunză poate trimite totul într-un singur tunel fără consecințe, deoarece nimic altceva nu depinde de ea. Un salt din mijloc este simultan un punct final pentru clienți și un client către partea îndepărtată, iar cele două sarcini vor rutare diferită.
De ce răspunsul iese prin tunel
Pe B, interfața orientată spre C poartă AllowedIPs = 0.0.0.0/0 împreună cu ruta implicită corespunzătoare. De aceea fiecare pachet creat de B intră în acel tunel, dacă ceva mai specific nu are prioritate.
Un pachet de la A adresat lui B sosește și este acceptat, pentru că B este destinația și livrarea locală se produce prima. Răspunsul este un pachet complet nou generat local și orientat spre adresa publică a lui A, iar acea destinație se potrivește cu ruta implicită în tunel. C îl primește, nu deține acea adresă și îl aruncă.
Simptomul se citește ca o problemă de firewall sau o nepotrivire de chei, și niciuna nu este adevărată aici. O captură pe tunelul spre C arată răspunsurile mergând în direcția greșită. Acea singură observație economisește mai mult timp decât orice cantitate de citire a configurației, așa că noteaz-o înainte să începi să schimbi chei. Cine nu a văzut captura va presupune că tunelul însuși este stricat.
Reparația îngustă: reguli de politică ce corespund unui port sursă
Răspunsurile din socketul de ascultare al lui B ies cu portul sursă 500. O regulă de politică poate corespunde acestui lucru și poate trimite pachetele înapoi în tabelul obișnuit:
ip rule add sport 500 table main priority 100
ip rule add sport 8080 table main priority 101
Regulile de politică sunt consultate în ordinea priorității, iar tabelul principal se află la 32766, deci o regulă la 100 este întrebată înainte ca ruta implicită a tunelului să aibă ceva de spus. Adaugă o singură intrare pentru fiecare port care trebuie să rămână accesibil din exterior și nimic mai mult, ca traficul redirecționat să folosească în continuare tunelul pentru care a fost construit.
Cunoaște limita acestui truc. Selectorul sport corespunde pachetelor generate local, ceea ce acoperă exact cazul răspunsului și în mod deliberat nu acoperă traficul pe care B doar îl redirecționează pentru altcineva. Un serviciu găzduit în spatele lui B are în continuare nevoie de propriul răspuns, iar a pretinde contrariul produce o reparație care funcționează pentru primul serviciu și eșuează pentru al doilea.
Dă regulii un proprietar
O regulă tastată într-un prompt dispare data viitoare când interfața este reconstruită. Într-o configurație WireGuard, pune-o acolo unde interfața este creată și eliminată:
PostUp = ip rule add sport 500 table main priority 100
PostDown = ip rule del sport 500 table main priority 100
Dacă interfața este deținută de NetworkManager, de un cârlig al distribuției sau de un punct de intrare al unui container, perechea aparține acelui proprietar și nu unui fișier editat manual. Verifică regulile efective după o reconectare, nu doar după prima pornire, pentru că prima pornire este cazul care funcționează mereu.
Compară alternativele sincer
Regula de port sursă este una din patru ieșiri și cea mai îngustă dintre ele. Are avantajul că nu atinge deloc politica tunelului, motiv pentru care de obicei supraviețuiește modificărilor ulterioare. Reparațiile înguste eșuează și ele zgomotos când nu mai sunt valabile, în timp ce o excepție largă continuă să funcționeze în tăcere mult timp după ce motivul a dispărut. Diferența aceea contează mai mult decât eleganța când cutia aparține altcuiva peste doi ani.
| Approach | What it costs | Where it breaks |
|---|---|---|
| Source-port policy rule | One rule per exposed port | Anything B forwards rather than originates |
| Destination exception route | A route pinned to specific client addresses | Clients whose address changes |
| Stop full-tunnelling on B | Loses the catch-all path toward C | Designs that depend on it |
| Split into two tunnels | More interfaces and more policy | Nothing, at the cost of complexity |
Verifică de la ambele capete după ce ai ales. Din A, pornește o conexiune către portul lui B și ține-o ocupată o vreme. Pe B, fă kernelul să justifice calea răspunsului cu ceva de genul ip route get <address-of-A> sport 500, și confirmă că numește interfața WAN și nu tunelul. După aceea, verifică dacă ceva din spatele lui C încă răspunde prin lanț, ceea ce dovedește că excepția a rămas îngustă în loc să devină o ocolire. În final, notează porturile în notele de proiectare, altfel următorul operator va redescoperi asta la alt serviciu.