Error 502 del proxy inverso https de Nginx, proxy_ssl_server_name, proxy_ssl_verify

        Vi este artículo en línea y lo reimprimí aquí para registrarlo. Nota de error --- nombre_servidor e ip del proxy inverso de Nginx | ​​Notas de CQ

fondo

        ¿Alguna vez ha encontrado 502 Bad Gateway al usar Nginx para realizar un proxy inverso en un sitio web? Parece que no hay ningún problema con el proxy inverso normal, pero aparece 502 Bad Gateway cuando el proxy inverso. Verifique el registro de errores y mostrará:

        *6565 SSL_do_handshake() falló (SSL: error:14094438:rutinas SSL:ssl3_read_bytes:alerta tlsv1 error interno:alerta SSL número 80) mientras se realizaba el protocolo de enlace SSL en sentido ascendente

        La investigación preliminar sobre el problema encontró que debido a que el sitio web tiene SNI habilitado, Nginx no agrega SNI proxy_ssl_server_name de forma predeterminada , y Nginx no puede establecer exitosamente el protocolo de enlace SSL ascendente, lo que genera 502 Bad Gateway.

proxy_ssl_server_name

        Recientemente estaba escribiendo un backend de administración. Cuando me refería a CDNla interacción de Alibaba Cloud, uno de los elementos de parámetro llamado SNI de regreso al origen me llamó la atención.

imagen-20210513190524400

        Aunque sé que SNI y Nginx upstream también usan HTTPS en el trabajo, no he prestado atención a la necesidad de una configuración especial. Siento que puede desencadenar un punto ciego de conocimiento. Después de algunas investigaciones, descubrí que Nginx tiene un parámetro similar proxy_ssl_server_namea este.

Syntax:	proxy_ssl_server_name on | off;
Default:	
proxy_ssl_server_name off;
Context:	http, server, location
This directive appeared in version 1.7.0.

Enables or disables passing of the server name through TLS Server Name Indication extension (SNI, RFC 6066) when establishing a connection with the proxied HTTPS server.

        Cuando el servidor final es HTTPS, SNIel valor predeterminado es si se habilita el proxy inverso off. Es muy común que una IP vincule varios nombres de dominio, ¿por qué no está habilitado de forma predeterminada?

        Sentí que simplemente leer la documentación no era suficiente, así que también miré el código fuente: el procesamiento SNI debe llamar SSL_set_tlsext_host_namea una función y buscar SSL_set_tlsext_host_namela función puede localizar rápidamente la lógica relevante.

imagen-20210513194354048

        El parámetro está desactivado de forma predeterminada. Cuando se activa, se pasará al servidor ascendente durante el protocolo de enlace SSL HostNamepara que el servidor ascendente sepa qué certificado usar.

Ahora que sabes cómo usarlo, probémoslo.

verificar

        Usaré mi propio blog como servidor ascendente para realizar pruebas. La topología es la siguiente:

->  www.dianduidian.com  -> blog.dianduidian.com

        Utilice primero la configuración más sencilla

    server {
        listen       80;
        server_name  www.dianduidian.com;
        location / {
          proxy_pass https://blog.dianduidian.com;
        }
    }

        No hay problema, se puede abrir normalmente, capturemos el paquete para ver cómo Nginx establece una conexión con el servidor ascendente.

imagen-20220518160850500

Client HelloSe puede ver que durante la fase de protocolo de enlace TLS, Nginx no incluye información SNI         al enviar mensajes al servidor ascendente , lo que confirma que cuando se utiliza el proxy inverso de Nginx para solicitar el servidor ascendente mediante HTTPS, no está habilitado de forma predeterminadaSNI .

        Sigamos mirando la información del certificado devuelta por el servidor ascendente.

imagen-20220518160228427

        Puede ver que el servidor no devuelve el certificado para el nombre de dominio blog.dianduidian.com sino un certificado predeterminado configurado por nginx. Esto se debe a que SNI no está habilitado , por lo que durante el protocolo de enlace TLS, el servidor ascendente no sabe qué nombre de dominio certificado a utilizar y utiliza el certificado predeterminado. Certificado devuelto.

        Aunque el certificado devuelto es incorrecto, la solicitud no se ve afectada. Especulamos que cuando se utiliza el proxy inverso Nginx para solicitar el servidor ascendente a través de HTTPS, el certificado devuelto por el servidor ascendente no se verifica de forma predeterminada.

Al consultar la documentación, descubrí que el certificado ascendente no se verifica de forma predeterminada.

Syntax:	proxy_ssl_verify on | off;
Default:	
proxy_ssl_verify off;
Context:	http, server, location
This directive appeared in version 1.7.0.

Enables or disables verification of the proxied HTTPS server certificate.

El estado del certificado no se verifica de forma predeterminada.

Entonces, habilitémoslo y veamos qué sucede.

Obtenga el archivo primero CA,

 rizo https://curl.se/ca/cacert.pem -o /etc/nginx/conf.d/cacert.pem

Modifique el archivo de configuración de la siguiente manera:

    server {
        listen       80;
        server_name  www.dianduidian.com;
        location / {
          proxy_pass https://blog.dianduidian.com;
          proxy_ssl_verify on;
          proxy_ssl_trusted_certificate /etc/nginx/conf.d/cacert.pem;          
        }
    }

imagen-20220518165329294

502, verifique el registro e informe upstream SSL certificate verify errorun error.

2022/05/18 16:52:14 [error] 20325#1337445: *23 upstream SSL certificate verify error: (18:self signed certificate) while SSL handshaking to upstream, client: 127.0.0.1, server: sni.dianduidian.com, request: "GET / HTTP/1.1", upstream: "https://47.100.x.x:443/", host: "sni.dianduidian.com"


        Puede ver que después de habilitar la verificación del certificado ascendente, Nginx efectivamente certificará el certificado devuelto por el ascendente. Sin embargo, a través de la captura de paquetes anterior, podemos ver que el ascendente devolvió un certificado predeterminado configurado por Nginx. Este certificado fue autofirmado por nosotros, y la verificación de la CA naturalmente pasó. No, pero si la CA es legítima, ¿ CommonNamequé pasará si el certificado es inconsistente? ¿No será verificado? La prueba aquí requiere múltiples certificados legales, lo cual es problemático. Encontraremos la respuesta directamente en el código fuente.

static void
ngx_http_upstream_ssl_handshake(ngx_http_request_t *r, ngx_http_upstream_t *u,
    ngx_connection_t *c)
{
    long  rc;

    if (c->ssl->handshaked) {

        if (u->conf->ssl_verify) {
            rc = SSL_get_verify_result(c->ssl->connection);

            if (rc != X509_V_OK) {
                ngx_log_error(NGX_LOG_ERR, c->log, 0,
                              "upstream SSL certificate verify error: (%l:%s)",
                              rc, X509_verify_cert_error_string(rc));
                goto failed;
            }

            if (ngx_ssl_check_host(c, &u->ssl_name) != NGX_OK) {
                ngx_log_error(NGX_LOG_ERR, c->log, 0,
                              "upstream SSL certificate does not match \"%V\"",
                              &u->ssl_name);
                goto failed;
            }
        }

        c->write->handler = ngx_http_upstream_handler;
        c->read->handler = ngx_http_upstream_handler;

        ngx_http_upstream_send_request(r, u, 1);

        return;
    }

    if (c->write->timedout) {
        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
        return;
    }

failed:

    ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
}

imagen-20220518190512323

        Se puede ver en el código fuente que después de activar la verificación del certificado ascendente, no solo verificará la legitimidad de la autoridad emisora ​​del certificado, sino que también comparará los certificados , es decir, la coherencia de commonNamela verificacióncommonName .

Ábrelo a continuaciónSNI

    server {
        listen       80;
        server_name  www.dianduidian.com;
        location / {
          proxy_pass https://blog.dianduidian.com;
          proxy_ssl_verify on;
          proxy_ssl_trusted_certificate /etc/nginx/conf.d/cacert.pem;  
          proxy_ssl_server_name on;
        }
    }

El sitio web se abre normalmente, esto capturará los paquetes y echará un vistazo.

imagen-20220518171121667

        Puede ver que durante la fase de protocolo de enlace TLS, Nginx Client Hellotiene una extensión adicional en la información enviada al servidor ascendente server_name, que le indica al servidor ascendente qué información del certificado debe usarse para responder. Server NamePuede ver que lo que se pasa aquí es blog.dianduidian.com, pero en la configuración anterior no especificamos qué nombre de dominio pasar ¿Cuál es la lógica de esta pieza?

        Después de consultar la documentación, descubrí que en realidad existe otro proxy_ssl_namecontrol de parámetros.

Syntax:	proxy_ssl_name name;
Default:	
proxy_ssl_name $proxy_host;
Context:	http, server, location
This directive appeared in version 1.7.0.

Allows overriding the server name used to verify the certificate of the proxied HTTPS server and to be passed through SNI when establishing a connection with the proxied HTTPS server.

By default, the host part of the proxy_pass URL is used.

Puedes ver que el valor predeterminado es $proxy_host, intentemos modificarlo.

    server {
        listen       80;
        server_name  www.dianduidian.com;
        location / {
          proxy_pass https://blog.dianduidian.com;
          proxy_ssl_verify on;
          proxy_ssl_trusted_certificate /etc/nginx/conf.d/cacert.pem;  
          proxy_ssl_server_name on;
          proxy_ssl_name www.baidu.com;
        }
    }

Coge el paquete y echa un vistazo.

imagen-20220518174226031

Puede ver Server Nameque se ha cambiado a www.baidu.com.

Ejemplo

Ejemplo 1

http {
    upstream bff-app {
        server my-bff.azurewebsites.net:443;
    }

    server {
        listen 80 default_server;

        location /api {
            proxy_pass https:/bff-app;
            proxy_ssl_server_name on;
            # Manually set Host header to "my-bff.azurewebsites.net",
            # otherwise it will default to "bff-app".
            proxy_set_header Host my-bff.azurewebsites.net; 
        }
    }
}

Ejemplo 2

upstream abtest_management_api_backend {
        server 域名:443;
}

location ^~ /modules/abm/ {
        proxy_ssl_server_name on;
        proxy_ssl_name 域名;
        proxy_set_header Host 域名;
        proxy_pass https://abtest_management_api_backend/modules/abm/;
        proxy_read_timeout 1800s;
        proxy_set_header Origanization-Id qiancheng;
        proxy_set_header X-Real-IP $clientRealIp;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass_header    X-Accel-Buffering;
}

Resumir

Cuando Nginx actúa como un proxy inverso y establece una conexión con el servidor ascendente mediante HTTPS,

  1. No habilitado de forma predeterminada SNI, proxy_ssl_server_name on;habilítelo con parámetros;
  2. De forma predeterminada, el certificado devuelto por el servidor ascendente no está verificado, useproxy_ssl_verify on;
  3. Una vez habilitada la verificación del certificado ascendente, Nginx utilizará la CA especificada en el archivo de configuración para verificar la legitimidad del certificado devuelto por el servidor ascendente y también comparará la información de CommonName en el certificado.

referencia

Ideas de solución de problemas del sitio https 502 del proxy inverso de Nginx | ​​Un pequeño paso

Módulo ngx_http_proxy_module

c - Cómo implementar la indicación del nombre del servidor (SNI)

ssl - ¿Cómo actualizar el paquete cURL CA en RedHat? - Fallo del servidor

Blog-CSDN de OpenSSL-SNI_openssl sni_Remy1119

documentación nginx

Supongo que te gusta

Origin blog.csdn.net/yangyangye/article/details/132211707
Recomendado
Clasificación