Container Docker inaccesibil prin VPN? Comparați subrețelele
Docker alocă o subrețea pe care o revendică și VPN-ul. Comparați cele două planuri de adrese, apoi alegeți o remediere care rezistă la o reconectare.
Containerele răspund în LAN-ul biroului, apoi încetează să răspundă în momentul în care se conectează VPN-ul companiei. În container nu s-a schimbat nimic: nici o repornire, nici o actualizare de imagine, nici o modificare de configurație. Ce s-a schimbat este tabela de rutare a gazdei, unde o rută adăugată de tunel acoperă acum adresa pe care a dat-o Docker. Înainte să modificați o subrețea sau să cereți o excepție de VPN, puneți ambele planuri de adrese una lângă alta, pentru că remedierile costă cantități foarte diferite de timp.
Citiți ruta înainte de a schimba configurația
Începeți cu rețeaua pe care Docker a construit-o efectiv. Blocul IPAM este planul de adrese, iar subrețeaua este linia care contează:
docker network inspect project_default --format '{{json .IPAM.Config}}'
# [{"Subnet":"172.18.0.0/16","Gateway":"172.18.0.1"}]Implicit, Compose alocă un /16 fiecărui proiect nou, așa că două servicii pot sta într-un interval de 65,536 adrese. Această lățime este motivul pentru care suprapunerile sunt frecvente: un /16 ales la întâmplare este destul de mare pentru a înghiți un site la care ajungeți prin tunel, și nimeni nu observă până când tunelul nu urcă.
Apoi comparați selecția rutei pentru o adresă de container, o dată cu tunelul oprit și o dată cu el pornit:
ip route get 172.18.0.7
# 172.18.0.7 dev br-3f2a9c src 172.18.0.1Două forme de răspuns duc în locuri diferite. Dacă ieșirea numește tun0, wg0 sau un alt dispozitiv de tunel, prefixul VPN este cel puțin la fel de specific ca ruta punții și pachetul nu ajunge niciodată la container. Dacă numește în continuare puntea, gazda poate ajunge la container, iar defectul dumneavoastră este filtrarea, DNS sau o cale de întoarcere care eșuează doar într-o direcție.
De ce o coliziune de /16 este ușor de produs
Docker ia rețelele de punte din rezervele private, iar rezerva implicită provine din blocul 172.16.0.0/12 în felii de /16. Rețelele companiilor folosesc adesea același spațiu privat, iar un VPN de acces la distanță împinge acele prefixe către fiecare client care se conectează. Când ambele părți cred că dețin 172.18.0.0/16, o singură tabelă de rutare conține două pretenții contradictorii.
Lungimea prefixului decide câștigătorul, nu ordinea rutelor. Un tunel care împinge 10.0.0.0/8 nu învinge o rută de punte de 172.18.0.0/16, pentru că /8 este mai puțin specific. Un tunel care împinge 172.18.0.0/16, sau 172.18.5.0/24 în interiorul ei, o învinge. De aceea un laptop funcționează în rețeaua biroului și eșuează pe Wi-Fi-ul hotelului cu același VPN: clientul primește prefixe diferite.
Aceeași tabelă guvernează și direcția opusă. Un container care are nevoie de o gazdă internă la 10.8.4.20 trimite pachetul către poarta sa de punte, iar gazda îl rutează apoi în tunel. Acea cale funcționează de obicei pentru că gazda face NAT, dar eșuează când partea companiei acceptă doar adresa VPN a laptopului, sau când filtrarea căii inverse aruncă răspunsul asimetric.
Comparați cele trei remedieri
Există doar trei ieșiri, și diferă prin cine trebuie să le aprobe și ce se strică în timp ce aplicați una.
| Repair | Who owns it | What it costs |
|---|---|---|
| Renumber the container network | You | Recreates the network, restarts services, invalidates literal addresses in configs and scripts |
| Exclude your range from the tunnel | Network team | Changes what every laptop sees, needs a change request and a client profile update |
| Reach the service through a published port | You and the client | One port only, no direct container-to-container routing across the tunnel |
Renumerotarea este singura remediere pe care o puteți termina fără un tichet, de aceea majoritatea textelor se opresc aici. Este ieftină pe un proiect tânăr și devine scumpă odată ce un instrument de migrare sau un șir de conexiune salvat indică o adresă literală. Numărați acele referințe înainte de a edita YAML, nu după.
O excepție din tunel este cel mai curat răspuns când suprapunerea se află într-un interval pe care compania dumneavoastră îl deține cu adevărat. Este și cea mai lentă, și este inutilă pentru un contractor care nu poate schimba profilul clientului. Un port publicat sau un mic proxy invers evită disputa: clientul vorbește cu o adresă pe care tunelul o rutează deja corect, și doar acel port trebuie să fie accesibil.
Fixați un interval în mod deliberat
Pentru un singur proiect, declarați subrețeaua în fișierul compose, astfel încât proiectul să nu mai ia din rezerva comună:
networks:
default:
ipam:
config:
- subnet: 10.201.7.0/24Luați acel interval dintr-un registru pe care îl ține echipa dumneavoastră, nu dintr-un exemplu găsit în timp ce căutați. Verificați-l față de prefixele pe care le împinge VPN-ul, rețelele de acasă de unde lucrează oamenii și celelalte rețele de containere de pe aceeași gazdă. Apoi planificați recrearea, pentru că o rețea existentă își păstrează vechea subrețea: docker compose down, apoi up -d. docker network rm refuză cât timp sunt atașate puncte finale, iar orice intrare ipv4_address din fișier trebuie să cadă în noul interval.
Pentru rețelele pe care Docker le creează singur, setați o rezervă de daemon în /etc/docker/daemon.json:
{
"default-address-pools": [
{ "base": "10.201.0.0/16", "size": 24 },
{ "base": "10.202.0.0/16", "size": 24 }
]
}Rezerva se aplică rețelelor create după repornirea daemonului, iar punțile care există deja își păstrează adresele. Este comod și ușor de uitat, așa că tratați un proiect vechi ca încă suprapus până când cineva îi recreează rețeaua. documentația de rețelistică a motorului Docker descrie regulile de alocare, dacă trebuie să apărați rezerva într-o revizuire.
Puntea Linux și Docker Desktop eșuează diferit
Pe o gazdă Linux, containerele stau pe punți în spațiul de nume al gazdei. O singură tabelă decide totul, așa că comparația de mai sus este întreaga poveste: citiți ruta punții, citiți ruta tunelului și vedeți care câștigă pentru o adresă reală de container.
Docker Desktop pune containerele în spatele unei VM cu NAT propriu. Gazda nu are nicio rută către adresa containerului; ajunge la serviciu printr-un port publicat, de obicei legat de 127.0.0.1. Așadar, un client VPN poate rupe serviciul fără nicio suprapunere de subrețele: fie instalând o rută pentru intervalul intern al VM-ului, fie blocând traficul de loopback cât timp tunelul este conectat. Dacă ip route get pe gazdă raportează că nu există rută pentru adresa containerului, aceasta este comportarea așteptată, nu eroarea.
Consecința practică este unde priviți mai întâi. Pe Linux, începeți cu tabela de rutare. Pe Desktop, începeți cu portul publicat și cu regulile de firewall ale clientului VPN, apoi verificați dacă subrețeaua internă a VM-ului se ciocnește cu ceva ce împinge tunelul.
Verificați cu tunelul pornit
Reconectați VPN-ul de două ori înainte de a considera problema rezolvată, pentru că un profil care împinge rute la conectare și le retrage la deconectare poate părea corect după prima încercare. Apoi rulați verificările pe care clientul le face efectiv:
ip route getde pe gazdă, folosind adresa reală a containerului.- O cerere din alt container, pentru a dovedi că serviciul în sine este sănătos.
curl -sv http://127.0.0.1:8080/de pe client cu tunelul pornit, apoitraceroute -ncătre adresa containerului de pe o mașină din LAN.sysctl net.ipv4.conf.all.rp_filterdacă cererea ajunge la gazdă, dar răspunsul dispare.
În final, scrieți intervalul ales în același registru din care l-ați ales, cu data și proiectul care îl deține. Următoarea suprapunere va veni dintr-un proiect pe care nimeni nu l-a documentat, iar singura apărare este că intervalul a fost rezervat intenționat, nu tras la întâmplare.