Siga unha petición desde un teléfono nun tren ata un backend que só existe dentro dunha rede privada. O teléfono resolve un nome público, conéctase a un enderezo público no porto 443 e completa un handshake TLS normal. Nginx acepta esa conexión e despois abre unha segunda, completamente separada, cara a un enderezo ao que só chega por unha interface VPN. Nada do teléfono cambia, e o teléfono nunca chega a saber que houbo un túnel.

Seguir unha petición a través de dúas conexións

O tramo de entrada é unha transacción HTTP pública. Ten un nome público, un certificado público e un camiño de resposta que debe funcionar para calquera enderezo que teña o visitante nese momento. Calquera cambio que altere ese camiño rompe todos os visitantes á vez. Por iso o tramo público é o que menos convén experimentar.

O tramo de saída é privado. Nginx pasa a ser cliente, resolve un nome interno ou usa un enderezo fixo, e chega a un servizo que rexeitaría unha conexión desde internet.

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

Manteñaos separados na cabeza e a configuración tórnase obvia. Mestúreos e acabará tentando facer que un cliente público participe nun túnel, que é de onde vén a maior parte da confusión neste deseño. O tramo privado pode ser fráxil. O público non.

Antes de construír nada disto, confirme que publicar a aplicación é intencionado. Un proxy que funciona tamén é unha exposición que funciona, e o modelo de autenticación ten que ser unha decisión e non un efecto secundario.

O nome da pasarela ten que sobrevivir ao túnel

Hai unha trampa sutil na resolución de nomes. Se o DNS privado sobrescribe o mesmo nome que o perfil de VPN usa para a súa propia pasarela, unha reconexión pode acabar tentando chegar a esa pasarela a través do túnel que se supón que debe establecer. O cliente necesita que ese nome se resolva antes de que exista o túnel. Unha vez que se resolve dentro do túnel, a conexión non se pode construír en primeiro lugar.

Use nomes aprobados distintos para a pasarela e o backend, e limite a política do resolvedor para que cada nome se resolva no enderezo que necesita o seu consumidor. Cambiar a cabeceira Host dunha aplicación non axuda con este problema, porque é o cliente de VPN quen fai a consulta.

Despois pense no alcance. O frontend necesita unha ruta cara ao enderezo do backend e tamén debe seguir atendendo clientes públicos. Un túnel completo nese host satisfai o primeiro requisito e rompe o segundo.

Onde a política o permite, un túnel cara a un destino concreto é o deseño máis limpo. Onde a política impón un túnel completo, o frontend pertence a un contexto de rede separado en vez de ser remendado con rutas ad hoc para visitantes coñecidos.

Escribir o bloque de servidor para que ambos os tramos verifiquen

Supoña que o backend presenta un certificado para backend.corp.example e responde nun enderezo privado. Conectarse a un enderezo mentres se verifica un nome é exactamente para o que están as opcións SNI e de nome do módulo 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 camiño e enderezo de arriba é un marcador de posición. As partes importantes son as que manteñen a verificación activada: un nome que comprobar, un paquete de confianza co que comprobalo e unha profundidade que coincida coa cadea aprobada.

proxy_set_header Host app.example.com é deliberado. O backend debería configurarse para aceptar esa orixe pública, e debería confiar nas cabeceiras reenviadas só desde este frontend. Sobrescribir X-Forwarded-For co enderezo do cliente é correcto para un único proxy e erróneo para unha cadea, así que un despregamento con varios proxies necesita a súa propia disposición de enderezos de confianza.

Facer que a orixe pública se comporte como tal

A aplicación debería emitir URL absolutas, redireccións e cookies para a orixe pública. Se redirixe un visitante a backend.corp.example, ese visitante vai a un nome que non pode resolver.

Corrixa iso mediante o axuste de URL externa que admite a aplicación e unha política de redirección revisada, non reescribindo nomes de host nos corpos de resposta. A substitución de cadeas en contido arbitrario rompe cargas asinadas e crea erros difíciles de rastrexar.

As cookies merecen o mesmo coidado. Unha cookie limitada ao nome privado nunca a enviará un navegador que só coñece o público.

Confirme tamén que Nginx pode realmente posuír o socket público. Un servizo web existente no 443 ten que moverse ou o frontend ten que vivir noutro sitio, porque dous procesos non poden enlazar o mesmo enderezo e o mesmo porto.

Comprobar cada tramo onde realmente se executa

Valide a configuración co analizador instalado antes de calquera recarga, para que unha errata non derrube o oínte que está servindo nese momento.

Despois probe o tramo do backend desde o propio host do frontend, no mesmo contexto de rede que usa Nginx. Un ping a través do túnel demostra alcanzabilidade e nada sobre TLS, autorización ou a aplicación.

Por último faga unha petición pública nova ao nome de host real con verificación de certificado normal, e observe o camiño de resposta en vez de só o corpo.

Garde ambas as observacións coa configuración despregada e anote que pasa cando o túnel non está dispoñible. O frontend debería devolver un erro de upstream controlado, non recuar en silencio cara a outro backend que ninguén documentou. Un 502 cunha liña de rexistro clara é un bo resultado. Unha páxina en caché dunha orixe non pretendida non o é.

Artigos relacionados