Passagem de proxy reverso Nginx do certificado TLS do cliente

2

Estou com um problema ao tentar usar o Nginx como um proxy reverso, no qual ele não parece estar enviando o certificado do cliente para o recurso de back-end.

Aqui está a configuração do bloco do servidor:

server {
listen 443;
server_name my.server.tld;

location / {
    proxy_pass https://my.realserver.tld;

    proxy_redirect off;

    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;

    proxy_pass_request_body on;
    proxy_pass_request_headers on;
}

include ssl_params;
ssl_client_certificate /etc/ssl/client-ca.pem;
ssl_verify_client optional;

}

ssl_params:

ssl on;
ssl_certificate /etc/ssl/private/fullchain.pem;
ssl_certificate_key /etc/ssl/private/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;

add_header Strict-Transport-Security max-age=15768000;

Os registros nginx mostram um 502 do recurso de back-end e o back-end mostra um erro: exception reported by IO thread: null cert chain Caused by javax.net.ssl.SSLHandshakeException: null cert chain

O back-end é um servidor Tomcat.

EDITAR:

Eu criei o nginx a partir do código-fonte para tentar usar o material ssl_preread, mas está falhando.

Estas são as informações de criação:

./nginx -V
nginx version: nginx/1.11.10
built by gcc 4.9.2 (Debian 4.9.2-10) 
built with OpenSSL 1.0.1t  3 May 2016
TLS SNI support enabled
configure arguments: --with-stream_ssl_preread_module --with-cc-opt='-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2' --with-ld-opt=-Wl,-z,relro --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_addition_module --with-http_dav_module --with-http_geoip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_sub_module --with-http_xslt_module --with-mail --with-mail_ssl_module

Estou usando o Debian Jessie 8.7.

Aqui está a falha ao tentar usar o exemplo de Alexey:

./nginx -t
nginx: [emerg] unknown directive "stream" in /etc/nginx/nginx.conf:11
nginx: configuration file /etc/nginx/nginx.conf test failed
    
por Andrew 02.03.2017 / 23:30

2 respostas

2

Esta configuração funciona, mas requer que o nginx seja compilado com ngx_stream_ssl_preread_module .

stream {
    upstream yandex {
        server 93.158.134.3:443;
    }

    upstream google {
        server 64.233.164.113:443;
    }

    map $ssl_preread_server_name $upstream {
        hostnames;
        default yandex;
        .google.com google;
    }

    server {
        listen 12345;

        ssl_preread on;
        proxy_pass $upstream;
    }
}

Teste:

$ openssl s_client -quiet -connect localhost:12345 -servername yandex.ru
depth=3 C = PL, O = Unizeto Sp. z o.o., CN = Certum CA
verify return:1
depth=2 C = PL, O = Unizeto Technologies S.A., OU = Certum Certification Authority, CN = Certum Trusted Network CA
verify return:1
depth=1 C = RU, O = Yandex LLC, OU = Yandex Certification Authority, CN = Yandex CA
verify return:1
depth=0 C = RU, O = Yandex LLC, OU = ITO, L = Moscow, ST = Russian Federation, CN = *.wfarm.yandex.net
verify return:1
^C

$ openssl s_client -quiet -connect localhost:12345 -servername google.com
depth=3 C = US, O = Equifax, OU = Equifax Secure Certificate Authority
verify return:1
depth=2 C = US, O = GeoTrust Inc., CN = GeoTrust Global CA
verify return:1
depth=1 C = US, O = Google Inc, CN = Google Internet Authority G2
verify return:1
depth=0 C = US, ST = California, L = Mountain View, O = Google Inc, CN = *.google.com
verify return:1
^C
    
por 07.03.2017 / 17:55
1

Neste momento, o Nginx configurou o Nginx para funcionar como um balanceador de carga camada 7 . O Nginx encerrará as conexões HTTPS e, em seguida, criará outra conexão para o servidor de backend. O Nginx tem acesso ao certificado do cliente, mas não há nenhuma razão para o Nginx escolher passar um certificado de cliente, a menos que seja solicitado, supondo que ele tenha essa capacidade.

O que você precisa é de um balanceador de carga da camada 4, para que a conexão TCP seja passada para o servidor de back-end. Isso pode ser feito em Nginx, HAProxy ou, sem dúvida, em outros. O Nginx será parecido com este (retirado de esta documentação )

stream {
  server {
    listen     127.0.0.1:12345;
    proxy_pass backend.example.com:12345;
  }
}

O artigo vinculado mostra como balancear a carga entre vários servidores de backend.

Atualizar

Você adicionou um novo requisito ao balanceamento de carga com base no nome de domínio. Você não pode usar o SNI porque esse é um recurso HTTP (camada 7). Nesse caso, uma opção é atribuir vários IPs ao servidor e fazer com que o cliente faça a solicitação para o IP correto.

stream {
  server {
    listen     127.0.0.1:12345;
    proxy_pass backend.example.com:12345;
  }
  server {
    listen     127.0.0.2:12345;
    proxy_pass backend2.example.com:12345;
  }
}

Solução alternativa

Uma resposta para esta pergunta sugere o seguinte pode funcionar, no seu bloco de proxy. Vale a pena tentar.

proxy_set_header X-SSL-CERT $ssl_client_cert;
    
por 03.03.2017 / 01:31

Tags