I have Nginx which proxy request from client to IBM DataPower with mutual TLS.
I have an error when message is sent from Nginx to IBM DP:
sll server (SERVER) ssl peer did not send a certificate during the handshake datapower
Cut from my Nginx config
location ~ path {
proxy_pass https://HOST:PORT; # DataPower
proxy_ssl_trusted_certificate /opt/nginx/ssl/tr/ca-chain.cert.pem;
proxy_ssl_certificate /opt/nginx/ssl/client/client-nginx_cert.pem;
proxy_ssl_certificate_key /opt/nginx/ssl/client/client-nginx_key.pem;
proxy_http_version 1.1;
proxy_ssl_server_name on;
proxy_ssl_name HOST;
proxy_set_header Host HOST;
proxy_ssl_verify off;
proxy_ssl_verify_depth 2;
}
Message goes from client directly to IBM DP without errors.
You could try adding proxy_set_header X-SSL-CERT $ssl_client_cert;
Related
I have a Client Y running under Windows Server 2003 that only supports TLS 1.0 (.NET Framework 3.5) and I have a Service X running under Linux (Java 1.8) with NGINX, that accepts connection only with TLS 1.2.
Client Y (TLS 1.0) ---> calls ---> Service X (TLS 1.2 over Nginx).
This is my NGINX config:
# HTTPS server
server {
listen 80;
listen 443 ssl;
server_name myapi.com.br;
ssl_certificate /etc/pki/tls/certs/myfile.cer;
ssl_certificate_key /etc/pki/tls/private/myfile.key;
ssl_session_timeout 5m;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://localhost:8080/;
proxy_ssl_session_reuse off;
proxy_set_header Host $host;
proxy_redirect off;
}
}
But it didn't work, the Client Y can't call the Service X, but another Client Z using TLS 1.2 can call the Service X without problem.
This is the error in nginx:
2019/08/06 11:23:18 [crit] 25199#0: *253185 SSL_shutdown() failed (SSL: error:140E0197:SSL routines:SSL_shutdown:shutdown while in init) while SSL handshaking, client: 10.31.68.186, server: 0.0.0.0:443
I want to use nginx 1.15.12 as a proxy for tls termination and authentication. If a valid client certificate is shown, the nginx server will forward to the respective backend system (localhost:8080 in this case) The current configuration does that for every request.
Unfortunately it is not possible to configure one certificate per location{} block. Multiple server blocks could be created, which each check for another certificate, but I have also the requirement to just receive requests via one port.
nginx.conf: |
events {
worker_connections 1024; ## Default: 1024
}
http{
# password file to be moved to seperate folder?
ssl_password_file /etc/nginx/certs/global.pass;
server {
listen 8443;
ssl on;
server_name *.blabla.domain.com;
error_log stderr debug;
# server certificate
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
# CA certificate for mutual TLS
ssl_client_certificate /etc/nginx/certs/ca.crt;
proxy_ssl_trusted_certificate /etc/nginx/certs/ca.crt;
# need to validate client certificate(if this flag optional it won't
# validate client certificates)
ssl_verify_client on;
location / {
# remote ip and forwarding ip
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# certificate verification information
# if the client certificate verified against CA, the header VERIFIED
# will have the value of 'SUCCESS' and 'NONE' otherwise
proxy_set_header VERIFIED $ssl_client_verify;
# client certificate information(DN)
proxy_set_header DN $ssl_client_s_dn;
proxy_pass http://localhost:8080/;
}
}
}
Ideally I would like to achieve something like that:
(requests to any path "/" except "/blabla" should be checked with cert1, if "/blabla" is matching, another key should be used to check the client certificate.
nginx.conf: |
events {
worker_connections 1024; ## Default: 1024
}
http{
# password file to be moved to seperate folder?
ssl_password_file /etc/nginx/certs/global.pass;
server {
listen 8443;
ssl on;
server_name *.blabla.domain.com;
error_log stderr debug;
# server certificate
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
# CA certificate for mutual TLS
ssl_client_certificate /etc/nginx/certs/ca.crt;
proxy_ssl_trusted_certificate /etc/nginx/certs/ca.crt;
# need to validate client certificate(if this flag optional it won't
# validate client certificates)
ssl_verify_client on;
location / {
# remote ip and forwarding ip
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# certificate verification information
# if the client certificate verified against CA, the header VERIFIED
# will have the value of 'SUCCESS' and 'NONE' otherwise
proxy_set_header VERIFIED $ssl_client_verify;
# client certificate information(DN)
proxy_set_header DN $ssl_client_s_dn;
proxy_pass http://localhost:8080/;
}
location /blabla {
# Basically do the same like above, but use another ca.crt for checking the client cert.
}
}
}
Im on a kubernetes cluster but using ingress auth mechanisms is no option here for reasons. Ideal result would be a way to configure different path, with different certificates for the same server block in nginx.
Thank you!
Edit:
The following nginx.conf can be used to check different certificates within nginx. Therefore 2 independent server{} blocks are needed with a different server_name. The URI /blabla can now only be accessed via blabla-api.blabla.domain.com.
events {
worker_connections 1024; ## Default: 1024
}
http{
server_names_hash_bucket_size 128;
server {
listen 8443;
ssl on;
server_name *.blabla.domain.com;
error_log stderr debug;
# password file (passphrase) for secret keys
ssl_password_file /etc/nginx/certs/global.pass;
# server certificate
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
# CA certificate for mutual TLS
ssl_client_certificate /etc/nginx/certs/ca.crt;
proxy_ssl_trusted_certificate /etc/nginx/certs/ca.crt;
# need to validate client certificate(if this flag optional it won't
# validate client certificates)
ssl_verify_client on;
location / {
# remote ip and forwarding ip
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# certificate verification information
# if the client certificate verified against CA, the header VERIFIED
# will have the value of 'SUCCESS' and 'NONE' otherwise
proxy_set_header VERIFIED $ssl_client_verify;
# client certificate information(DN)
proxy_set_header DN $ssl_client_s_dn;
proxy_pass http://localhost:8080/;
}
location /blabla {
return 403 "authorized user is not allowed to access /blabla";
}
}
server {
listen 8443;
ssl on;
server_name blabla-api.blabla.domain.com;
error_log stderr debug;
# password file (passphrase) for secret keys
ssl_password_file /etc/nginx/certs/global-support.pass;
# server certificate
ssl_certificate /etc/nginx/certs/server-support.crt;
ssl_certificate_key /etc/nginx/certs/server-support.key;
# CA certificate for mutual TLS
ssl_client_certificate /etc/nginx/certs/ca-support.crt;
proxy_ssl_trusted_certificate /etc/nginx/certs/ca-support.crt;
# need to validate client certificate(if this flag optional it won't
# validate client certificates)
ssl_verify_client on;
location /blabla {
# remote ip and forwarding ip
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# certificate verification information
# if the client certificate verified against CA, the header VERIFIED
# will have the value of 'SUCCESS' and 'NONE' otherwise
proxy_set_header VERIFIED $ssl_client_verify;
# client certificate information(DN)
proxy_set_header DN $ssl_client_s_dn;
proxy_pass http://localhost:8080/blabla;
}
}
}
I guess SNI is the answer.
With that in the ssl handshake a server with one IP and one port can provide multiple certificates
But in my understanding server_name attribute has to be different for the two servers. Not sure if this is needed for top and second level domain, or if you can do it simply with the path.
SNI extends the handshake protocol of TLS. This way before the connection is established during the ssl handshake the server can know what certificate to use.
Newer nginx versions should have SNI enabled by default. Can be checked: nginx -V
Look at this how to structure the nginx.conf
I am having two VM, on hosts Nginx and the other is also a standalone server.
I will call the VMs as follows;
a standalone = Cash serving https
the one hosting the Nginx= LOCAL serving http
In order for LOCAL to communicate with CASH, we use a NGINX reverse proxy proxy to redirect HTTP traffic to HTTPS and handle the TLS handshakes and in case the CASH makes a call to LOCAL the NGINX again accepts this HTTPS traffic and redirecting it to LOCAL's HTTP as shown;
upstream api_http_within_this_vm {
server 127.0.0.1:9001; #LOCAL VM caal it HOST VM application
}
# SENDING HTTP TRAFFIC TO OUR HTTPS ENDPOINT Call CASH
server {
listen 80;
listen [::]:80;
server_name 10.0.0.13;
location / {
proxy_pass https:// api_https_to_another_vm;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ssl_certificate /etc/nginx/sites-available/signed_by_CASH.pem;
proxy_ssl_certificate_key /etc/nginx/sites-available/local_key_used_to_generate_csr_for_CASH_to_sign.key;
proxy_ssl_protocols TLSv1.2;
proxy_ssl_ciphers HIGH:!aNULL:!MD5;
proxy_ssl_trusted_certificate /etc/nginx/sites-available/CASH_CA.crt;
proxy_ssl_verify on;
proxy_ssl_verify_depth 2;
proxy_ssl_session_reuse on;
}
}
upstream api_https_to_another_vm {
server 10.0.0.13:8080; # CASH's VM IP and PORT
}
# RECIEVING HTTPS TRAFFIC ENDPOINT from CASH TO OUR LOCAL HTTP ENDPOINT
server {
listen 5555 ssl http2;
listen [::]:5555 ssl http2;
server_name 1270.0.0.1;
location / {
proxy_pass http://api_http_within_this_vm;
proxy_set_header X_CUSTOM_HEADER $http_x_custom_header;
proxy_buffering off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass_request_headers on;
}
ssl_certificate /etc/nginx/sites-available/signed_by_CASH.pem;
ssl_certificate_key /etc/nginx/sites-available/local_key_used_to_generate_csr_for_CASH_to_sign.key;
ssl_verify_client off;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
}
MY SUCCESS
The traffic from CASH to LOCAL work well.
MY CHALLENGE
The traffic from LOCAL to CASH does NOT work. I get 502 Bad Request yet when I use curl https://10.0.0.13:8080/ directly without Reverse proxyLOCAL to CASH I see some output even if no handshake happens.
Anywhere am going wrong, please advise.....
Secondly, does Nginx only redirect traffic to IPs within the VM or even to other VMs?
I majorly want to achieve this kind of leg that has failed on my side.
I have tested this configuration over time, I had to trace with a tcpdump and even checked my logs because I suspected the problem is network driven. I found out the actually the client CASH was dropping the connection before the TLS handshake completes.
2019/03/02 06:54:58 [error] 27569#27569: *62 peer closed connection in SSL handshake (104: Connection reset by peer) while SSL handshaking to upstream, client: xx.xx.xx.xx, server: 1270.0.0.1, request: "GET / HTTP/1.1", upstream: "https://xx.xx.xx.xx:1000/", host: "xx.xx.xx.xx:80"
Thanks to all that viewed, but the script is correct.
I'm using nginx as a proxy to a backend server.
The backend server is also using nginx and enforcing client certificate authentication using the ssl_client_certificate and ssl_verify_client directives.
In my nginx server I set the following:
location /proxy {
proxy_pass https://www.backend.com;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ssl_certificate /etc/nginx/cert/client.crt;
proxy_ssl_certificate_key /etc/nginx/cert/client.key;
}
according to the nginx docs.
However, the backend is still responding with a 400 reponse code "No required SSL certificate was sent".
Note that when issuing requests to the backend server using wget with the client certificate, I get a valid 200 OK response:
wget --certificate=/etc/nginx/cert/client.crt --private-key=/etc/nginx/cert/client.key https://www.backend.com
What am I missing in my nginx configuration?
This post seemed to solve this problem for me.
I had to add the following setting to get things working for me.
proxy_ssl_server_name on;
I have a master nginx server deciding on the incoming server name where to route requests to. For two secondary servers this master nginx server is also holding ssl certificates and keys. The 3rd server is holding his own certificates and keys because there is a frequent update process for those.
My question is now how I can configure the master nginx server to forward all requests to server 3 which are coming in for this server. I cannot copy the certificates and keys from server 3 to the master server as they change too often.
Try to proxy the tcp traffic instead of the http traffic
stream {
server {
listen SRC_IP:SRC_PORT;
proxy_pass DST_IP:DST_PORT;
}
}
for more details refer to the nginx documentation
https://docs.nginx.com/nginx/admin-guide/load-balancer/tcp-udp-load-balancer/
Here's a configuration that might work. Proxy through the master and forward everything to Server3. Use the ssl port but turn ssl off.
server {
listen 443;
server_name myserver.mydomain.whatever;
ssl off;
access_log /var/log/nginx/myserver.access.log;
error_log /var/log/nginx/myserver.error.og;
keepalive_timeout 60;
location / {
set $fixed_destination $http_destination;
if ( $http_destination ~* ^https(.*)$ )
{
set $fixed_destination http$1;
}
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Destination $fixed_destination;
# Fix the “It appears that your reverse proxy set up is broken" error.
# might need to explicity set https://localip:port
proxy_pass $fixed_destination;
# force timeout if backend died.
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_read_timeout 90;
proxy_redirect http:// https://;
}
}