Nginx al davant, backend privat darrere la VPN
El navegador parla amb Nginx, i Nginx obre una segona connexió cap al túnel. Encamineu totes dues, verifiqueu tots dos certificats i manteniu públiques les redireccions públiques.
Seguiu una petició des d’un telèfon en un tren fins a un backend que només existeix dins d’una xarxa privada. El telèfon resol un nom públic, es connecta a una adreça pública al port 443 i completa un handshake TLS normal. Nginx accepta aquesta connexió i després n’obre una segona, completament separada, cap a una adreça que només assoleix per una interfície VPN. Res del telèfon no canvia, i el telèfon no arriba a saber mai que hi va haver un túnel.
Seguir una petició a través de dues connexions
El tram entrant és una transacció HTTP pública. Té un nom públic, un certificat públic i un camí de resposta que ha de funcionar per a qualsevol adreça que tingui el visitant en aquell moment. Qualsevol canvi que alteri aquest camí trenca tots els visitants alhora. Per això el tram públic és el que menys convé experimentar.
El tram sortint és privat. Nginx passa a ser client, resol un nom intern o fa servir una adreça fixa, i arriba a un servei que rebutjaria una connexió des d’internet.
Public client -> frontend public address:443 (TLS, public name)
Frontend -> VPN private backend 10.40.0.20:8443 (TLS, private name)
Manteniu-los separats al cap i la configuració es torna òbvia. Barregeu-los i acabareu intentant fer que un client públic participi en un túnel, que és d’on ve la major part de la confusió d’aquest disseny. El tram privat pot ser fràgil. El públic no.
Abans de construir res d’això, confirmeu que publicar l’aplicació és intencionat. Un proxy que funciona també és una exposició que funciona, i el model d’autenticació ha de ser una decisió i no un efecte secundari.
El nom de la passarel·la ha de sobreviure al túnel
Hi ha un parany subtil en la resolució de noms. Si el DNS privat sobreescriu el mateix nom que el perfil de VPN fa servir per a la seva pròpia passarel·la, una reconnexió pot acabar intentant arribar a aquesta passarel·la a través del túnel que se suposa que ha d’establir. El client necessita que aquest nom es resolgui abans que existeixi el túnel. Un cop es resol dins del túnel, la connexió no es pot construir mai.
Feu servir noms aprovats diferents per a la passarel·la i el backend, i limiteu la política del resolutor perquè cada nom es resolgui a l’adreça que necessita el seu consumidor. Canviar la capçalera Host d’una aplicació no ajuda aquí, perquè és el client de VPN qui fa la consulta.
Després penseu en l’abast. El frontend necessita una ruta cap a l’adreça del backend i també ha de continuar atenent clients públics. Un túnel complet en aquest host satisfà el primer requisit i trenca el segon.
On la política ho permet, un túnel cap a una destinació concreta és el disseny més net. On la política imposa un túnel complet, el frontend pertany a un context de xarxa separat en lloc de ser apedaçat amb rutes ad hoc per a visitants coneguts.
Escriure el bloc de servidor perquè tots dos trams verifiquin
Suposeu que el backend presenta un certificat per a backend.corp.example i respon en una adreça privada. Connectar-se a una adreça mentre es verifica un nom és exactament per a què serveixen les opcions SNI i de nom del mòdul de proxy.
upstream private_app {
server 10.40.0.20:8443;
}
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /etc/nginx/tls/app-fullchain.pem;
ssl_certificate_key /etc/nginx/tls/app-key.pem;
location / {
proxy_pass https://private_app;
proxy_ssl_server_name on;
proxy_ssl_name backend.corp.example;
proxy_ssl_trusted_certificate /etc/nginx/trust/backend-ca.pem;
proxy_ssl_verify on;
proxy_ssl_verify_depth 3;
proxy_set_header Host app.example.com;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
Cada camí i adreça de dalt és un marcador de posició. Les parts importants són les que mantenen la verificació activada: un nom que comprovar, un paquet de confiança amb què comprovar-lo i una profunditat que coincideixi amb la cadena aprovada.
proxy_set_header Host app.example.com és deliberat. El backend s’hauria de configurar per acceptar aquest origen públic, i hauria de confiar en les capçaleres reenviades només des d’aquest frontend. Sobreescriure X-Forwarded-For amb l’adreça del client és correcte per a un sol proxy i incorrecte per a una cadena, així que un desplegament amb diversos proxys necessita la seva pròpia disposició d’adreces de confiança.
Fer que l’origen públic es comporti com a tal
L’aplicació hauria d’emetre URL absolutes, redireccions i galetes per a l’origen públic. Si redirigeix un visitant a backend.corp.example, aquest visitant va a un nom que no pot resoldre.
Corregiu-ho mitjançant el paràmetre d’URL externa que admet l’aplicació i una política de redirecció revisada, no reescrivint noms d’amfitrió als cossos de resposta. La substitució de cadenes en contingut arbitrari trenca càrregues signades i crea errors difícils de rastrejar.
Les galetes mereixen la mateixa cura. Una galeta limitada al nom privat no l’enviarà mai un navegador que només coneix el públic.
Confirmeu també que Nginx pot posseir realment el sòcol públic. Un servei web existent al 443 s’ha de moure o el frontend ha de viure en un altre lloc, perquè dos processos no poden enllaçar la mateixa adreça i el mateix port.
Comprovar cada tram on realment s’executa
Valideu la configuració amb l’analitzador instal·lat abans de qualsevol recàrrega, perquè una errata no faci caure l’oient que està servint en aquell moment.
Després proveu el tram del backend des del mateix host del frontend, en el mateix context de xarxa que fa servir Nginx. Un ping a través del túnel demostra abastabilitat i res sobre TLS, autorització o l’aplicació.
Finalment feu una petició pública nova al nom d’amfitrió real amb verificació de certificat normal, i observeu el camí de resposta en lloc de només el cos.
Conserveu les dues observacions amb la configuració desplegada i anoteu què passa quan el túnel no està disponible. El frontend hauria de retornar un error d’upstream controlat, no recular en silenci cap a un altre backend que ningú no ha documentat. Un 502 amb una línia de registre clara és un bon resultat. Una pàgina en memòria cau d’un origen no volgut no ho és.