Proxy Nginx para back-end con autenticación de certificado de cliente SSL

14

Tengo dos servidores, ambos tienen nginx. El servidor A está escuchando 443 y está configurado para autenticarse con un certificado SSL de cliente.

El servidor B tiene un proceso interno que necesita comunicarse con el servidor A a través de nginx.

Me gustaría configurar Nginx en el servidor B que escuchará 8080 (sin cifrado, ya que es toda la comunicación local) y proxy_pass al servidor A: 443.

La pregunta es ¿cómo puedo inyectar un certificado de cliente? No encontré ninguna función proxy_xxxx que hiciera eso.

Sí sé hacer un equivalente a eso con socat, pero mi requisito es usar nginx.

Bastien974
fuente
2
Al observar las directivas en el módulo proxy de nginx, parece que no es posible hacer que un servidor nginx use un certificado para autenticar: nginx.org/en/docs/http/ngx_http_proxy_module.html Apache sí admite esa característica.
NuTTyX
De eso tenía miedo ... ¿alguna idea de si hay un módulo personalizado o algo que pueda hacer que esto funcione? ¡Ese tipo de característica tiene que existir!
Bastien974
He encontrado una utilidad para migrar archivos de configuración de apache a nginx ( github.com/nhnc-nginx/apache2nginx ), así que lo descargué, creé un simulado apache.conf y lo pasé por la herramienta, pero obtuve este resultado :### Section 2: Unconverted directives ### # Flag Description # [S] Unsupported directives. # In conf file: dummy.conf # Line 32: SSLProxyMachineCertificateFile /path/to/cert (mod_ssl.c) # [S] SSLProxyMachineCertificateFile: No relevant directive in Nginx.
NuTTyX

Respuestas:

21

¿Es suficiente que se pasen los detalles del certificado del cliente?

Puedes añadir

proxy_set_header X-SSL-CERT $ssl_client_escaped_cert;

a su configuración y luego la información del certificado está disponible para el servidor B a través de un encabezado X-SSL-Cert.

jwilkins
fuente
1
Como comented aquí , tenga en cuenta si su back-end puede sustituir \tcon \nde esta cabecera, una vez leido.
lucasvc
3
Según la documentación de nginx , la $ssl_client_certvariable está en desuso; la $ssl_client_escaped_certvariable debe usarse en su lugar.
dubek
1
@dubek buena captura, actualizaría la respuesta directamente en casos como este.
Chris Stryczynski
He estado probando esta solución, pero solo parece estar pasando el certificado si la validación del certificado está activada. No quiero encenderlo, ¿hay alguna manera de hacerlo funcionar sin activar la validación?
juhako
5

Aparentemente, esto es lo que está buscando: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate Disponible desde la versión 1.7.8.

location / {
    ...
    proxy_pass     the_other_nginx;
    proxy_ssl_certificate  the_certificate.pem;
    ...
}
Nicolas Malbran
fuente
1
Esto está mal. Esto asigna un certificado de cliente al proxy que se utilizará para las solicitudes al backend. Pero el preguntado dijo que esta comunicación no está encriptada solo localmente, por lo que no hay verificación de certificado de cliente. La respuesta de jwilkins funciona bien.
Kenyakorn Ketsombut
@KenyakornKetsombut Creo que leíste mal la pregunta. El servidor B no tiene cifrado (escucha en 8080) pero tiene que comunicarse con el servidor A (escucha en 443, con cifrado). Entonces B necesita enviar el certificado del cliente a A para autenticarse. Puede usar proxy_ssl_certificate para lograr eso. jwilkins responde, enviará a A el certificado otorgado a B. Ambos pueden funcionar según lo que necesite.
Nicolas Malbran
Hola nicolas Estaba tratando de decir: si el Servidor B no está usando encriptación (en el puerto 8080), no usa nada como HTTPS / SSL o como lo llame. Los certificados de cliente son parte de SSL, por lo que el servidor B no puede hacer esta parte. B no puede enviar ni recibir ningún certificado de cliente.
Kenyakorn Ketsombut
1
Obtengo nginx: [emerg] no se define "proxy_ssl_certificate_key" para el certificado "certs / Roro_Client.pem" nginx: la prueba del archivo de configuración /etc/nginx/nginx.conf falló cuando intento esto con 1.8.0
Wolfgang Fahl el
4

El problema parece ser en gran medida dependiente de la versión. En Ubuntu 14.04 LTS, el nginx predeterminado es un 1.4 obsoleto. Primero necesitas instalar una versión basada en PPA

https://leftshift.io/upgrading-nginx-to-the-latest-version-on-ubuntu-servers

muestra cómo hacer esto con:

sudo add-apt-repository ppa:nginx/stable
sudo aptitude safe-upgrade

deberías terminar con:

nginx -v
nginx version: nginx/1.8.0

La configuración de @ xatr0z responde https://serverfault.com/a/636455/162693 que apunta a http://www.senginx.org/en/index.php/Proxy_HTTPS_Client_Certificate no funciona:

propuesta que no funciona

backend {
    server some-ip:443;
}

server {
    listen 80;


    location / {
        proxy_ssl_certificate        certs/client.crt;
        proxy_ssl_certificate_key    certs/client.key;


        proxy_pass https://backend;
    }
}

no funciona fuera de la caja con 1.8.0. Probablemente sea solo una pista y no se use como un archivo de configuración como tal o dependa de otra versión.

Estoy probando con un servidor backend A basado en apache2 con SSL y certificados de cliente autofirmados habilitados. Las SSLOptions de configuración de Apache están configuradas en:

SSLOptions +ExportCertData +FakeBasicAuth + StdEnvVars

Esto facilita la depuración de la situación, ya que un script phpinfo () en el lado del servidor mostrará la información del lado del servidor y del cliente.

Para verificar esto, utilicé:

https: // backend / test / phpinfo

con el certificado SSL instalado en el navegador y obtengo secciones como: SSL_SERVER_S_DN_CN para el certificado del servidor y SSL_CLIENT_S_DN_CN para el certificado del cliente.

Como primer inicio, utilicé (complete las partes entre paréntesis) para configurar nginx en el servidor frontend B:

server {
  listen 8080;
  server_name <frontend>;

  location / {
    proxy_buffering off;
    proxy_pass https://<backend>;
    #proxy_ssl_certificate      certs/<SSL Client Certificate>.crt;
    #proxy_ssl_certificate_key  certs/<SSL Client Certificate>.key;
  }
}

no comentar la parte específica del Certificado de cliente SSL solo para verificar que el proxy inverso funcione.

nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
service nginx restart
nginx stop/waiting
nginx start/running, process 8931

Ahora http: // frontend: 8080 / test / phpinfo.php funciona.

Se muestra SSL_SERVER_S_DN_CN para el certificado del servidor y SSL_CLIENT_S_DN_CN para el certificado del cliente (todavía) no se muestra

Ahora después de descomentar:

server {
  listen 8080;
  server_name <frontend>;

  location / {
    proxy_buffering off;
    proxy_pass https://<backend>;
    proxy_ssl_certificate      certs/<SSL Client Certificate>.crt;
    proxy_ssl_certificate_key  certs/<SSL Client Certificate>.key;
  }
}

y comprobando / reiniciando

nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
service nginx restart
nginx stop/waiting
nginx start/running, process 8931

http: // frontend: 8080 / test / phpinfo.php funciona y

SSL_SERVER_S_DN_CN para el certificado de servidor se muestra y SSL_CLIENT_S_DN_CN del certificado de cliente se visualiza

así que ahora tenemos las cosas funcionando según lo solicitado.

Tenga en cuenta el error https://trac.nginx.org/nginx/ticket/872#ticket

Wolfgang Fahl
fuente
Es posible que desee tener cuidado con el problema de renegociación ruby-forum.com/topic/6875137 que podría estropear el espectáculo
Wolfgang Fahl el
1

Hay un artículo bastante bueno sobre los certificados de cliente nginx y SSL; usa PHP con FastCGI como ejemplo, pero creo que se puede adaptar a una configuración de proxy inverso:

server {
    listen        443;
    ssl on;
    server_name example.com;

    ssl_certificate      /etc/nginx/certs/server.crt;
    ssl_certificate_key  /etc/nginx/certs/server.key;
    ssl_client_certificate /etc/nginx/certs/ca.crt;
    ssl_verify_client optional;

    location / {
        root           /var/www/example.com/html;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_param  SCRIPT_FILENAME /var/www/example.com/lib/Request.class.php;
        fastcgi_param  VERIFIED $ssl_client_verify;
        fastcgi_param  DN $ssl_client_s_dn;
        include        fastcgi_params;
    }
}

Fuente http://nategood.com/client-side-certificate-authentication-in-ngi

Erik Kaplun
fuente