WireGuard annidato: il salto intermedio non può rispondere alla propria porta
Quando il salto intermedio manda tutto in un solo tunnel, le risposte al suo servizio in ingresso escono dal percorso sbagliato. Una regola sulla porta di origine nell’interfaccia lo risolve.
Tre salti WireGuard raggiungono una rete senza indirizzo pubblico, e poi la macchina intermedia smette di rispondere sulla porta su cui avrebbe dovuto ascoltare. Il tunnel verso valle funziona ancora, l’host remoto risponde ancora, e solo il server intermedio stesso è diventato silenzioso dall’esterno. Quella combinazione è confusa proprio perché è autoinflitta: il salto intermedio obbedisce alla propria tabella di routing, e la tabella manda le sue risposte nella direzione sbagliata.
Una catena che funziona finché non parli con la scatola intermedia
Prendiamo una catena illustrativa: il client A raggiunge il server B su UDP 500, e B prosegue verso il server C su UDP 500. C accetta traffico solo dall’indirizzo del suo unico peer, il che lo rende una foglia e mantiene semplice il suo comportamento. B non è una foglia, e tutto ciò che c’è di interessante in questo guasto accade lì.
La distinzione conta perché i ruoli di foglia e di intermedio tirano in direzioni opposte. Una foglia può mandare tutto in un solo tunnel senza conseguenze, dato che nulla altro dipende da lei. Un salto intermedio è al tempo stesso un punto finale per i client e un client verso il lato remoto, e questi due compiti vogliono un routing diverso.
Perché la risposta esce dal tunnel
Su B, l’interfaccia rivolta verso C porta AllowedIPs = 0.0.0.0/0 insieme alla rotta predefinita corrispondente. Ogni pacchetto che B origina finisce quindi in quel tunnel, a meno che qualcosa di più specifico abbia la precedenza.
Un pacchetto da A indirizzato a B arriva e viene accettato, perché B è la destinazione e la consegna locale avviene per prima. La risposta è un pacchetto nuovo di zecca generato localmente e diretto all’indirizzo pubblico di A, e quella destinazione corrisponde alla rotta predefinita verso il tunnel. C lo riceve, non possiede quell’indirizzo e lo scarta.
Il sintomo si legge come un problema di firewall o un disallineamento di chiavi, e qui nessuna delle due cose è vera. Una cattura sul tunnel verso C mostra che le risposte vanno nella direzione sbagliata. Quella singola osservazione fa risparmiare più tempo di qualsiasi lettura di configurazione, quindi annotala prima di iniziare a cambiare chiavi. Chi non ha visto la cattura presumerà che il tunnel stesso sia rotto.
La riparazione stretta: regole di policy che corrispondono a una porta di origine
Le risposte dal socket in ascolto di B escono con la porta di origine 500. Una regola di policy può corrispondere a questo e riportare quei pacchetti nella tabella ordinaria:
ip rule add sport 500 table main priority 100
ip rule add sport 8080 table main priority 101
Le regole di policy vengono consultate in ordine di priorità, e la tabella principale sta a 32766, quindi una regola a 100 viene interrogata prima che la rotta predefinita del tunnel abbia voce in capitolo. Aggiungi una voce per ogni porta che deve restare raggiungibile dall’esterno e nulla di più, così il traffico inoltrato continua a usare il tunnel per cui è stato costruito.
Conosci il limite di questo trucco. Il selettore sport corrisponde ai pacchetti generati localmente, il che copre esattamente il caso della risposta e deliberatamente non copre il traffico che B si limita a inoltrare per qualcun altro. Un servizio ospitato dietro B ha ancora bisogno della propria risposta, e fingere il contrario produce una correzione che funziona per il primo servizio e fallisce per il secondo.
Dai un proprietario alla regola
Una regola digitata al prompt sparisce la prossima volta che l’interfaccia viene ricostruita. In un’installazione WireGuard, mettila dove l’interfaccia viene creata e rimossa:
PostUp = ip rule add sport 500 table main priority 100
PostDown = ip rule del sport 500 table main priority 100
Se NetworkManager, un hook della distribuzione o un entrypoint di container possiede l’interfaccia, la coppia appartiene a quel proprietario e non a un file modificato a mano. Controlla le regole effettive dopo una riconnessione, non solo dopo il primo avvio, perché il primo avvio è il caso che funziona sempre.
Confronta le alternative con onestà
La regola sulla porta di origine è una delle quattro vie d’uscita, ed è la più stretta di tutte. Ha il vantaggio di non toccare affatto la policy del tunnel, ed è per questo che tende a sopravvivere ai cambiamenti successivi. Anche le correzioni strette falliscono in modo rumoroso quando smettono di applicarsi, mentre un’eccezione ampia continua a funzionare in silenzio molto tempo dopo che la ragione è sparita. Quella differenza conta più dell’eleganza quando tra due anni la scatola appartiene a qualcun altro.
| 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 |
Verifica da entrambe le estremità una volta scelto. Da A, avvia una connessione alla porta di B e tienila occupata per un po’. Su B, fai giustificare al kernel il percorso della risposta con qualcosa come ip route get <address-of-A> sport 500, e conferma che nomina l’interfaccia WAN e non il tunnel. Dopodiché, controlla che qualcosa dietro C risponda ancora attraverso la catena, il che prova che l’eccezione è rimasta stretta invece di diventare un bypass. Infine, annota le porte nelle note di progetto, o l’operatore successivo lo riscoprirà su un altro servizio.