Permitir una conexión TLS passthrough en el contexto stream de NGINX
NGINX puede rechazar un relé TCP TLS sin terminar TLS. Pon la regla de admisión en stream, no en un location HTTP, y comprueba qué dirección de par llega.
Reenviar bytes cifrados sin tener nunca el certificado es lo que hace un relé TLS passthrough, y el arreglo es cómodo y algo confuso a la vez. Como no se descifra nada, no existe nada con forma de HTTP sobre lo que filtrar, así que el bloque location habitual y sus reglas de acceso no sirven aquí. La decisión de la que dispone NGINX se refiere a la propia conexión: qué par de la red llamó y si ese par debe reenviarse.
La decisión va en stream, no en HTTP
En el contexto stream NGINX trabaja con sesiones TCP. Ve la dirección del par de la conexión y puede aplicarle reglas allow y deny ordenadas antes de que ningún byte llegue al backend. Es una decisión de admisión completa y no requiere clave privada, ni certificado, ni análisis HTTP.
| Mechanism | What it decides | Where it lives |
|---|---|---|
| stream allow/deny | Whether this TCP peer is relayed | stream server block |
| ssl_preread map | Which backend a named connection goes to | stream, with the preread module |
| HTTP location rule | Whether an HTTP request is served | http server block |
Esas tres filas nunca se sustituyen entre sí. Copiar una directiva de acceso HTTP al archivo que mencione el puerto 443 no cambia nada, porque el servidor stream nunca lee el contexto http.
Admite la conexión antes de reenviarla
Supón una instalación existente con los módulos stream y stream-access disponibles, clientes VPN que mantienen direcciones de origen en 10.90.0.0/24, y un backend en 192.168.50.20:443 que ya sirve la identidad HTTPS prevista.
stream {
server {
listen 443;
allow 10.90.0.0/24;
deny all;
proxy_pass 192.168.50.20:443;
}
}
Léelo como un fragmento de nuevo listener, no como un reemplazo directo de una configuración en funcionamiento. Revisa primero los listeners e includes existentes, porque un segundo listener en un puerto ya ocupado falla al recargar, no en el momento de la petición.
Fíjate en lo que falta: no hay listen ... ssl, no hay directiva de certificado. NGINX reenvía el flujo cifrado, y el backend termina TLS y presenta el certificado que valida el cliente. Un cliente que no pasa la allowlist recibe su sesión TCP rechazada, no una página HTTP 403, y cualquier herramienta que espere un código de estado informará de algo inútil.
Qué cubre la allowlist y qué no
Si el relé ya elige backends por nombre de servidor, ssl_preread y un map pueden seguir haciendo ese trabajo, siempre que el módulo esté disponible. El nombre lo sigue aportando el cliente, y nada en presentar un nombre demuestra que el cliente se haya unido a la VPN.
La regla de ejemplo cubre todo el listener stream. Eso la hace inadecuada para un único punto de entrada que sirve una mezcla de sitios públicos y sitios solo para VPN, donde haría falta una excepción por nombre; los listeners separados suelen ser más claros que una política dependiente del nombre que nadie puede leer.
Merece la pena nombrar dos huecos más. Una coincidencia de prefijo no dice nada sobre por qué interfaz llegó el paquete, y una regla HTTP en otro lugar no puede restringir este listener. Ambas son razones para mantener la política de entrada a nivel de host en la imagen en lugar de tratar una línea allow como toda la frontera. Escribe la frontera como dos controles que deben coincidir, para que cualquier cambio en uno de ellos se revise.
¿Qué dirección ve realmente NGINX?
Un balanceador de carga, otro proxy o un source NAT pueden poner un intermediario delante de NGINX, así que el par que observa no es la dirección interna del cliente VPN. Inspecciona la ruta real antes de elegir un prefijo, porque una allowlist escrita para la dirección equivocada o bien admite a todos los que están detrás del traductor o bien a nadie.
El protocolo PROXY y el tratamiento de IP real son respuestas legítimas, y solo deben configurarse para remitentes compatibles y explícitamente de confianza. Ambos mecanismos creen lo que dice el remitente, y precisamente por eso la lista de remitentes tiene que ser corta. Confiar en una cabecera de identidad de cualquier cliente de internet no vuelve a hacer fiable la información de origen; convierte un rechazo correcto en una aceptación falsificada.
Prueba ambos resultados antes de recargar
Valida la configuración, recarga y luego prueba dos veces: una sesión nueva permitida que debe funcionar y una rechazada que no debe. Mantén la verificación normal del certificado del backend en la prueba permitida, porque un relé que conecta pero no puede validar el backend ha resuelto un problema y ha creado otro.
nginx -t
ss -tnp | grep ':443'
Comprueba también la ruta al backend y la respuesta por separado. Pasar la allowlist solo demuestra que NGINX aceptó la sesión; no puede hacer que un backend inalcanzable responda, ni puede reparar un camino de vuelta que faltaba antes de que empezaras.