Sleduj jeden požadavek z telefonu ve vlaku až k backendu, který existuje jen v privátní síti. Telefon přeloží veřejné jméno, připojí se k veřejné adrese na portu 443 a dokončí běžný TLS handshake. Nginx to spojení přijme a pak otevře druhé, zcela oddělené, k adrese, kterou dosáhne jen přes síťové rozhraní VPN. Na telefonu se nic nemění a telefon se nikdy nedozví, že v tom byl tunel.

Sledovat jeden požadavek přes dvě spojení

Vstupní úsek je veřejná transakce HTTP. Má veřejné jméno, veřejný certifikát a cestu odpovědi, která musí fungovat pro jakoukoli adresu, kterou návštěvník zrovna má. Jakákoli změna, která tuto cestu naruší, rozbije všechny návštěvníky najednou. Proto je veřejný úsek ten, na kterém chceš experimentovat nejméně.

Výstupní úsek je privátní. Nginx se stane klientem, přeloží interní jméno nebo použije pevnou adresu a dosáhne služby, která by spojení z internetu odmítla.

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

Drž je v hlavě oddělené a konfigurace se stane samozřejmou. Slij je a skončíš tím, že se snažíš přimět veřejného klienta účastnit se tunelu, a odtud pochází většina zmatku v tomto návrhu. Privátní úsek může být křehký. Veřejný ne.

Než cokoli z toho postavíš, potvrď, že zveřejnění aplikace je zamýšlené. Fungující proxy je také fungující vystavení a model autentizace musí být rozhodnutí, ne vedlejší efekt.

Jméno brány musí tunel přežít

V překladu jmen je nenápadná past. Pokud privátní DNS přepíše stejné jméno, které profil VPN používá pro svou vlastní bránu, může opětovné připojení nakonec zkoušet dosáhnout této brány tunelem, který má sám vytvořit. Klient potřebuje toto jméno přeložené dřív, než tunel existuje. Jakmile se přeloží uvnitř tunelu, spojení už nelze vůbec sestavit.

Použij odlišná schválená jména pro bránu a backend a omez politiku resolveru tak, aby každé jméno přeložilo na adresu, kterou jeho uživatel potřebuje. Změna hlavičky Host aplikace tady nepomůže, protože dotaz dělá klient VPN.

Pak mysli na rozsah. Frontend potřebuje trasu k adrese backendu a také musí dál odpovídat veřejným klientům. Plný tunel na tomto hostiteli může splnit první požadavek a rozbít druhý.

Kde to politika dovoluje, je tunel ke konkrétnímu cíli čistší návrh. Kde politika nařizuje plný tunel, patří frontend do samostatného síťového kontextu, místo aby byl látán ad hoc trasami pro známé návštěvníky.

Napsat blok serveru tak, aby oba úseky ověřovaly

Předpokládej, že backend předkládá certifikát pro backend.corp.example a odpovídá na privátní adrese. Připojovat se k adrese a přitom ověřovat jméno je přesně to, k čemu slouží volby SNI a jména v modulu 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;
    }
}

Každá cesta a adresa výše je zástupný symbol. Důležité jsou části, které drží ověřování zapnuté: jméno ke kontrole, svazek důvěry, proti kterému ho kontrolovat, a hloubka odpovídající schválenému řetězu.

proxy_set_header Host app.example.com je záměrné. Backend by měl být nastaven tak, aby přijímal tento veřejný původ, a měl by důvěřovat předávaným hlavičkám jen z tohoto frontendu. Přepisovat X-Forwarded-For adresou klienta je správné pro jedinou proxy a špatné pro řetěz, takže nasazení s více proxy potřebuje vlastní uspořádání důvěryhodných adres.

Přimět veřejný původ, aby se choval jako veřejný

Aplikace by měla vydávat absolutní URL, přesměrování a cookies pro veřejný původ. Pokud přesměruje návštěvníka na backend.corp.example, je tento návštěvník poslán na jméno, které nedokáže přeložit.

Oprav to pomocí podporovaného nastavení externí URL v aplikaci a prověřené politiky přesměrování, ne přepisováním jmen hostitelů v tělech odpovědí. Nahrazování řetězců v libovolném obsahu rozbíjí podepsané datové části a vytváří chyby, které se těžko dohledávají.

Cookies si zaslouží stejnou péči. Cookie vázaná na privátní jméno nebude nikdy odeslána prohlížečem, který zná jen veřejné.

Potvrď také, že Nginx může skutečně vlastnit veřejný socket. Stávající webová služba na 443 se musí přesunout, nebo musí frontend žít jinde, protože dva procesy nemohou svázat stejnou adresu a port.

Zkontrolovat každý úsek tam, kde skutečně běží

Ověř konfiguraci nainstalovaným parserem před každým reloadem, aby překlep nesrazil listener, který právě obsluhuje.

Pak otestuj backendový úsek z hostitele frontendu samotného, ve stejném síťovém kontextu, který používá Nginx. Ping tunelem dokazuje dosažitelnost a nic o TLS, autorizaci nebo aplikaci.

Nakonec proveď nový veřejný požadavek na skutečné jméno hostitele s běžným ověřením certifikátu a sleduj cestu odpovědi, ne jen tělo.

Uchovej obě pozorování spolu s nasazenou konfigurací a zapiš, co se stane, když tunel není dostupný. Frontend by měl vrátit řízenou chybu upstreamu, ne tiše přejít na jiný backend, který nikdo nedokumentoval. 502 s jasnou řádkou v logu je dobrý výsledek. Stránka z mezipaměti z nechtěného původu není.

Související články