Sek vieną užklausą nuo telefono traukinyje iki užpakalinio serverio, kuris egzistuoja tik privačiame tinkle. Telefonas išsprendžia viešą vardą, prisijungia prie viešo adreso 443 prievade ir atlieka įprastą TLS rankos paspaudimą. Nginx priima tą ryšį ir tada atidaro antrą, visiškai atskirą ryšį su adresu, kurį pasiekia tik per VPN sąsają. Telefone niekas nesikeičia, ir telefonas niekada nesužino, kad buvo įtrauktas tunelis.

Sekti vieną užklausą per du ryšius

Įeinanti dalis yra viešas HTTP sandoris. Jis turi viešą vardą, viešą sertifikatą ir atsakymo kelią, kuris turi veikti bet kokiam adresui, kurį lankytojas tuo metu turi. Bet koks pakeitimas, kuris trikdo tą kelią, vienu metu sugadina visus lankytojus. Todėl viešoji dalis yra ta, su kuria nori eksperimentuoti mažiausiai.

Išeinanti dalis yra privati. Nginx tampa klientu, išsprendžia vidinį vardą arba naudoja fiksuotą adresą, ir pasiekia paslaugą, kuri atmestų ryšį iš interneto.

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

Laikyk juos atskirai galvoje, ir konfigūracija taps akivaizdi. Sujunk juos, ir galų gale bandysi įtraukti viešą klientą į tunelį, ir būtent iš to kyla didžioji dalis šio dizaino painiavos. Privati dalis gali būti trapi. Viešoji ne.

Prieš ką nors iš to statydamas, patvirtink, kad programos paskelbimas yra tyčinis. Veikiantis tarpinis serveris taip pat yra veikiantis atskleidimas, ir autentifikavimo modelis turi būti sprendimas, o ne šalutinis poveikis.

Tinklų sietuvos vardas turi išgyventi tunelį

Vardų sprendime slypi subtilus spąstas. Jei privatus DNS perrašo tą patį vardą, kurį VPN profilis naudoja savo tinklų sietuvai, pakartotinis prisijungimas gali galų gale bandyti pasiekti tą sietuvą per tunelį, kurį jis pats turėtų sukurti. Klientui reikia, kad šis vardas būtų išspręstas prieš atsirandant tuneliui. Kai jis išsprendžiamas tunelio viduje, ryšio niekada negalima sukurti.

Naudok skirtingus patvirtintus vardus tinklų sietuvai ir užpakaliniam serveriui, ir apribok sprendiko politiką taip, kad kiekvienas vardas išspręstų adresą, kurio reikia jo vartotojui. Programos Host antraštės keitimas čia nepadės, nes užklausą atlieka VPN klientas.

Tada pagalvok apie apimtį. Priekinei daliai reikia maršruto į užpakalinio serverio adresą, ir ji taip pat turi toliau atsakinėti viešiems klientams. Visas tunelis tame hoste gali įvykdyti pirmą reikalavimą ir sugadinti antrą.

Kur politika leidžia, tunelis į konkretų tikslą yra švaresnis dizainas. Kur politika reikalauja viso tunelio, priekinė dalis priklauso atskiram tinklo kontekstui, o ne lopoma ad hoc maršrutais žinomiems lankytojams.

Rašyti serverio bloką taip, kad abi dalys tikrintų

Tarkime, kad užpakalinis serveris pateikia sertifikatą vardui backend.corp.example ir atsako privačiu adresu. Jungimasis prie adreso tikrinant vardą yra būtent tai, kam skirtos tarpinio serverio modulio SNI ir vardo parinktys.

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

Kiekvienas aukščiau esantis kelias ir adresas yra vietos žymeklis. Svarbios yra tos dalys, kurios palieka tikrinimą įjungtą: vardas, kurį tikrinti, pasitikėjimo paketas, su kuriuo jį tikrinti, ir gylis, atitinkantis patvirtintą grandinę.

proxy_set_header Host app.example.com yra sąmoningas. Užpakalinis serveris turėtų būti sukonfigūruotas priimti šią viešą kilmę, ir jis turėtų pasitikėti persiųstomis antraštėmis tik iš šios priekinės dalies. Perrašyti X-Forwarded-For kliento adresu yra teisinga vienam tarpiniam serveriui ir neteisinga grandinei, todėl diegimui su keliais tarpiniais serveriais reikia savo patikimų adresų tvarkos.

Leisti viešajai kilmei elgtis kaip viešajai kilmei

Programa turėtų generuoti absoliučius URL, peradresavimus ir slapukus viešajai kilmei. Jei ji peradresuoja lankytoją į backend.corp.example, tas lankytojas siunčiamas į vardą, kurio jis negali išspręsti.

Ištaisyk tai naudodamas programos palaikomą išorinio URL nustatymą ir peržiūrėtą peradresavimo politiką, o ne perrašydamas hostų vardus atsakymų turinyje. Eilučių pakeitimas savavališkame turinyje sugadina pasirašytas naudingąsias apkrovas ir sukuria klaidas, kurias sunku atsekti.

Slapukai nusipelno tokio paties rūpesčio. Slapukas, susietas su privačiu vardu, niekada nebus išsiųstas naršyklės, kuri žino tik viešąjį.

Taip pat patvirtink, kad Nginx iš tikrųjų gali valdyti viešą lizdą. Esama žiniatinklio paslauga 443 prievade turi persikelti, arba priekinė dalis turi gyventi kitur, nes du procesai negali susieti to paties adreso ir prievado.

Tikrinti kiekvieną dalį ten, kur ji iš tikrųjų veikia

Validuok konfigūraciją su įdiegtu analizatoriumi prieš kiekvieną perkrovimą, kad rašybos klaida nenugriautų klausytojo, kuris šiuo metu aptarnauja.

Tada išbandyk užpakalinę dalį iš pačio priekinio hosto, tame pačiame tinklo kontekste, kurį naudoja Nginx. Ping per tunelį įrodo pasiekiamumą ir nieko apie TLS, autorizaciją ar programą.

Galiausiai atlik naują viešą užklausą tikruoju hosto vardu su įprastu sertifikato tikrinimu, ir stebėk atsakymo kelią, o ne tik atsakymo turinį.

Išsaugok abu stebėjimus kartu su įdiegta konfigūracija ir užrašyk, kas nutinka, kai tunelis nepasiekiamas. Priekinė dalis turėtų grąžinti kontroliuojamą upstream klaidą, o ne tyliai pereiti prie kito užpakalinio serverio, kurio niekas nedokumentavo. 502 su aiškia žurnalo eilute yra geras rezultatas. Talpykloje saugomas puslapis iš nepageidaujamos kilmės nėra.

Susiję straipsniai