Følg éin førespurnad frå ein telefon på eit tog til ein backend som berre finst inne i eit privat nettverk. Telefonen slår opp eit offentleg namn, koplar til ei offentleg adresse på port 443 og fullfører eit normalt TLS-handtrykk. Nginx godtek den tilkoplinga og opnar deretter ei andre, heilt separat, til ei adresse han berre når gjennom eit VPN-grensesnitt. Ingenting ved telefonen blir endra, og telefonen får aldri vita at ein tunnel var involvert.

Følg éin førespurnad gjennom to tilkoplingar

Det innkommande beinet er ein offentleg HTTP-transaksjon. Det har eit offentleg namn, eit offentleg sertifikat og ein svarveg som må fungere for den adressa den besøkjande tilfeldigvis har. Kvar endring som forstyrrar den vegen, bryt alle besøkjande på ein gong. Difor er det offentlege beinet det du minst vil eksperimentere med.

Det utgåande beinet er privat. Nginx blir klient, slår opp eit internt namn eller brukar ei fast adresse, og når ein teneste som ville avvise ei tilkopling frå internett.

Public client  -> frontend public address:443   (TLS, public name)
Frontend       -> VPN private backend 10.40.0.20:8443  (TLS, private name)

Hald dei skilde i hovudet, og konfigurasjonen blir openberr. Slå dei saman, og du endar opp med å prøve å få ein offentleg klient til å delta i ein tunnel, og det er der det meste av forvirringa i dette designet kjem frå. Det private beinet kan gjerne vere skjørt. Det offentlege beinet ikkje.

Før du byggjer noko av dette, må du stadfeste at publiseringa av applikasjonen er tilsikta. Ein fungerande proxy er også ei fungerande eksponering, og autentiseringsmodellen må vere eit val og ikkje ein biverknad.

Gateway-namnet må overleve tunnelen

Det ligg ei subtil felle i namneoppslag. Viss privat DNS overstyrer det same namnet som VPN-profilen brukar for sin eigen gateway, kan ein gjenoppkopling ende med å prøve å nå den gatewayen gjennom tunnelen han sjølv skal etablere. Klienten treng at namnet blir slått opp før tunnelen finst. Når det først blir slått opp inne i tunnelen, kan tilkoplinga aldri byggjast.

Bruk ulike godkjende namn for gatewayen og backenden, og avgrens resolverpolicyen slik at kvart namn blir slått opp til adressa brukaren treng. Å endre ein applikasjons Host-header hjelper ikkje her, fordi det er VPN-klienten som gjer oppslaget.

Tenk så på omfanget. Frontend treng ein rute til backend-adressa, og han må også halde fram med å svare offentlege klientar. Ein full tunnel på den verten kan oppfylle det første kravet og bryte det andre.

Der policyen tillèt det, er ein tunnel til eit bestemt mål det reinare designet. Der policyen krev ein full tunnel, høyrer frontend heime i ein separat nettverkskontekst i staden for å bli lappa med ad hoc-ruter for kjende besøkjande.

Skriv serverblokka slik at begge beina verifiserer

Anta at backenden presenterer eit sertifikat for backend.corp.example og svarar på ei privat adresse. Å kople til ei adresse medan ein verifiserer eit namn er nettopp det proxy-modulen sine SNI- og namneval er til for.

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;
    }
}

Kvar sti og adresse ovanfor er ein plasshaldar. Dei viktige delane er dei som held verifiseringa på: eit namn å kontrollere, ein tillitspakke å kontrollere det mot og ein djupne som passar den godkjende kjeda.

proxy_set_header Host app.example.com er medvite. Backenden bør konfigurerast til å godta det offentlege opphavet, og han bør berre stole på videresende headarar frå denne frontend. Å overskrive X-Forwarded-For med klientadressa er rett for éin proxy og gale for ei kjede, så ein utrulling med fleire proxyar treng si eiga ordning for betrudde adresser.

Få det offentlege opphavet til å oppføre seg som eit slikt

Applikasjonen bør sende absolutte URL-ar, omdirigeringar og informasjonskapslar for det offentlege opphavet. Viss han omdirigerer ein besøkjande til backend.corp.example, blir den besøkjande send til eit namn dei ikkje kan slå opp.

Ret det gjennom den støtta innstillinga for eksterne URL-ar i applikasjonen og ein gjennomgått omdirigeringspolicy, ikkje ved å skrive om vertsnamn i svartekstane. Strengerstatning i vilkårleg innhald bryt signerte payloads og skapar feil som er vanskelege å spore.

Informasjonskapslar fortener same omsut. Ein informasjonskapsel bunden til det private namnet blir aldri send av ein nettlesar som berre kjenner det offentlege.

Stadfest også at Nginx faktisk kan eige den offentlege socketen. Ei eksisterande webbeneste på 443 må flytte seg, eller så må frontend bu ein annan stad, fordi to prosessar ikkje kan binde same adresse og port.

Kontroller kvart bein der det faktisk køyrer

Valider konfigurasjonen med den installerte parsaren før kvar omlasting, slik at ein skrivefeil ikkje velter lyttaren som tener akkurat no.

Test deretter backend-beinet frå frontend-verten sjølv, i same nettverkskontekst som Nginx brukar. Ein ping gjennom tunnelen beviser nåbarheit og ingenting om TLS, autorisasjon eller applikasjonen.

Til slutt gjer ein fersk offentleg førespurnad til det ekte vertsnamnet med normal sertifikatverifisering, og observer svarvegen i staden for berre svarteksten.

Ta vare på begge observasjonane saman med den utrulla konfigurasjonen, og noter kva som skjer når tunnelen ikkje er tilgjengeleg. Frontend bør returnere ein kontrollert upstream-feil, ikkje stille falle tilbake til ein annan backend som ingen har dokumentert. Ein 502 med ei tydeleg logglinje er eit godt resultat. Ei buftra side frå eit utilsikta opphav er det ikkje.

Relaterte artiklar