Nginx framför, privat backend bakom VPN:en
Webbläsaren talar med Nginx, och Nginx öppnar en andra anslutning in i tunneln. Routa båda, verifiera båda certifikaten och håll offentliga omdirigeringar offentliga.
Följ en förfrågan från en telefon på ett tåg till en backend som bara finns inne i ett privat nätverk. Telefonen slår upp ett offentligt namn, ansluter till en offentlig adress på port 443 och genomför en normal TLS-handskakning. Nginx tar emot den anslutningen och öppnar sedan en andra, helt separat, till en adress som den bara når via ett VPN-gränssnitt. Ingenting hos telefonen ändras, och telefonen får aldrig veta att en tunnel var inblandad.
Följ en förfrågan genom två anslutningar
Det inkommande benet är en offentlig HTTP-transaktion. Det har ett offentligt namn, ett offentligt certifikat och en svarsväg som måste fungera för vilken adress besökaren än har just då. Varje ändring som stör den vägen bryter alla besökare på en gång. Därför är det offentliga benet det du minst vill experimentera med.
Det utgående benet är privat. Nginx blir klient, slår upp ett internt namn eller använder en fast adress, och når en tjänst som skulle neka en anslutning från internet.
Public client -> frontend public address:443 (TLS, public name)
Frontend -> VPN private backend 10.40.0.20:8443 (TLS, private name)
Håll dem åtskilda i huvudet och konfigurationen blir självklar. Slå ihop dem och du slutar med att försöka få en offentlig klient att delta i en tunnel, och det är där det mesta av förvirringen i den här designen kommer från. Det privata benet får vara bräckligt. Det offentliga benet får inte.
Innan du bygger något av detta, bekräfta att publiceringen av applikationen är avsedd. En fungerande proxy är också en fungerande exponering, och autentiseringsmodellen måste vara ett beslut och inte en bieffekt.
Gateway-namnet måste överleva tunneln
Det finns en subtil fälla i namnuppslagningen. Om privat DNS skriver över samma namn som VPN-profilen använder för sin egen gateway, kan en återanslutning till slut försöka nå den gatewayen genom tunneln den själv ska etablera. Klienten behöver att namnet slås upp innan tunneln finns. När det väl slås upp inne i tunneln kan anslutningen aldrig byggas.
Använd olika godkända namn för gatewayen och backenden, och avgränsa resolverpolicyn så att varje namn slås upp till den adress som dess användare behöver. Att ändra en applikations Host-header hjälper inte här, eftersom det är VPN-klienten som gör uppslaget.
Tänk sedan på omfattningen. Frontend behöver en rutt till backend-adressen, och den måste också fortsätta svara offentliga klienter. En full tunnel på den värden kan uppfylla det första kravet och bryta det andra.
Där policyn tillåter det är en tunnel till ett specifikt mål den renare designen. Där policyn kräver en full tunnel hör frontend hemma i en separat nätverkskontext i stället för att lappas med ad hoc-rutter för kända besökare.
Skriv serverblocket så att båda benen verifierar
Anta att backenden presenterar ett certifikat för backend.corp.example och svarar på en privat adress. Att ansluta till en adress medan man verifierar ett namn är precis vad proxy-modulens SNI- och namnalternativ är till för.
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;
}
}
Varje sökväg och adress ovan är en platshållare. De viktiga delarna är de som håller verifieringen påslagen: ett namn att kontrollera, ett förtroendepaket att kontrollera det mot och ett djup som matchar den godkända kedjan.
proxy_set_header Host app.example.com är avsiktligt. Backenden bör konfigureras att acceptera det offentliga ursprunget, och den bör bara lita på vidarebefordrade headers från den här frontend. Att skriva över X-Forwarded-For med klientadressen är rätt för en enda proxy och fel för en kedja, så en distribution med flera proxys behöver sin egen ordning för betrodda adresser.
Få det offentliga ursprunget att bete sig som ett sådant
Applikationen bör skicka absoluta URL:er, omdirigeringar och cookies för det offentliga ursprunget. Om den omdirigerar en besökare till backend.corp.example, skickas den besökaren till ett namn som de inte kan slå upp.
Åtgärda det via applikationens stödda inställning för externa URL:er och en granskad omdirigeringspolicy, inte genom att skriva om värdnamn i svarskroppar. Strängutbyte över godtyckligt innehåll bryter signerade payloads och skapar buggar som är svåra att spåra.
Cookies förtjänar samma omsorg. En cookie som är knuten till det privata namnet skickas aldrig av en webbläsare som bara känner till det offentliga.
Bekräfta också att Nginx verkligen kan äga den offentliga socketen. En befintlig webbtjänst på 443 måste flytta sig, eller så måste frontend bo någon annanstans, eftersom två processer inte kan binda samma adress och port.
Kontrollera varje ben där det faktiskt körs
Validera konfigurationen med den installerade parsern före varje omladdning, så att ett stavfel inte fäller den lyssnare som tjänar just nu.
Testa sedan backend-benet från frontend-värden själv, i samma nätverkskontext som Nginx använder. En ping genom tunneln bevisar nåbarhet och ingenting om TLS, auktorisering eller applikationen.
Gör till sist en färsk offentlig förfrågan till det riktiga värdnamnet med normal certifikatverifiering, och observera svarsvägen i stället för bara svarskroppen.
Spara båda observationerna tillsammans med den utrullade konfigurationen och anteckna vad som händer när tunneln inte är tillgänglig. Frontend bör returnera ett kontrollerat upstream-fel, inte tyst falla tillbaka på en annan backend som ingen dokumenterat. En 502 med en tydlig logglinje är ett bra resultat. En cachad sida från ett oavsiktligt ursprung är det inte.