[UPDATE]
This has been raised a a bug with NGINX support.
I have used default config file from the NGINX website (but without two way auth) in order to load balance between four upstream app servers over SSL with the the proxy_ssl_verify set to on. Yes, All upstream servers have valid certificates with CN matching their hostnames, and CA has been placed on the NGINX server with proper permissions and set in the nginx.conf file. Here are my findings (removed the rest of the config as it is not relevant):
OK - Works with proxy_ssl_verify off:
stream {
upstream upstream_appsrv{
server serverA.domain.com:443;
}
server {
listen 8443;
proxy_pass https://upstream_appsrv;
(...)
proxy_ssl off;
proxy_ssl_verify off;
}
}
NOT OK - As soon as I set proxy_ssl_verify on + proxy_ssl on:
stream {
upstream upstream_appsrv{
server serverA.domain.com:443;
}
server {
listen 8443;
proxy_pass https://upstream_appsrv;
(...)
proxy_ssl on;
proxy_ssl_verify on;
}
}
NGINX started throwing errors about upstream SSL certificate not matching the backend:2 upstream SSL certificate does not match "serverB.domain.com" while SSL handshaking to upstream...Lets change some things and go to the third step.
OK! - Now everything works fine:
stream {
upstream serverA.domain.com{
server serverA.domain.com:443;
}
server {
listen 8443;
proxy_pass https://serverA.domain.com;
(...)
proxy_ssl on;
proxy_ssl_verify on;
}
}
Turns out, that not only the server definition has to match the CN used in the certificate on the upstream server (obviously) but also the upstream upstream_appsrv needs to match the CN! Ok so next step, lets add another upstream server:
NOT OK...
stream {
upstream serverA.domain.com{
server serverB.domain.com:443;
}
server {
listen 8443;
proxy_pass https://serverA.domain.com;
(...)
proxy_ssl on;
proxy_ssl_verify on;
}
}
As soon as NGINX connects to the serverB we start seeing same errors again... Even though serverB has got proper CN set matching it's hostname (and is using same CA as serverA) it turns out that NGINX tries to match the CN against the upstream definition. And that's the issue I am trying to resolve.
Related
I am using a self signed certificate in the upstream. The upstream is reachable from the cURL but not from NGinX. Here is the process I followed.
I changed hosts file and add upstream IP with a domain name.
10.0.1.2 xxx.yyy.com
Then I used below command to access the application and it was successful.
curl GET "https://xxx.yyy.com/test" --cacert /etc/upstream.ca-cert.crt -v
Then I wanted to access the application through a NGinX. So I want to create secure connection between client and NGinX server and also between NGinX server and the application. The connection between client and NGinX works fine but the handshake between NGinX server and the application not works properly.
These are the configuration.
listen 443 ssl;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
server_name xxx.yyy.com;
location / {
include /etc/nginx/proxy_params;
proxy_pass https://backend-server;
proxy_ssl_certificate /etc/nginx/ssl/upstream.ca-cert.crt;
proxy_ssl_certificate_key /etc/nginx/ssl/upstream.ca-cert.key;
proxy_ssl_server_name on;
rewrite ^(.*):(.*)$ $1%3A$2;
}
upstream backend-server {
ip_hash;
zone backend 64k;
server 10.0.1.2:443 max_fails=1000 fail_timeout=30s;
}
Below is the error log in NGinX.
2019/12/05 06:46:40 [error] 5275#0: *2078 peer closed connection in SSL handshake while SSL handshaking to upstream, client: xxx.xxx.xxx.xxx, server: xxx.yyy.com, request: "GET /test HTTP/1.1", upstream: "https://10.0.1.2:443/carbon", host: "xxx.yyy.com"
My program on Golang can't work with user certificate (http client) due unsupported old tls algorithms. I want to solve it with reverse proxy using nginx. Here is a pic describes thoughted scheme https://ibb.co/Jxcy52G .
[client] ----> [NGINX:80] ----(proxy pass using cert,privkey)----> [TOMCAT:8443]
https://TOMCAT:8443 requires authentication with a client certificate. I want to hide this fact from my app. App must not be required to provide a client certificate. Instead, I would like NGINX to use a certificate that's stored on the server.
My nginx.conf config:
server {
listen 80;
# server_name _;
location / {
proxy_ssl_certificate "cert.pem";
proxy_ssl_certificate_key "key.pem";
proxy_ssl_server_name on;
# proxy_ssl_verify off;
proxy_ssl_name "iscs.telecomtest.ru";
proxy_read_timeout 300;
proxy_send_timeout 300;
proxy_pass https://195.11.xx.16:8443;
proxy_buffering off;
}
}
When i try to open page http://my-nginx:80/, I get:
2019/06/18 15:16:22 [error] 25896#14556: *133 peer closed connection in SSL handshake while SSL handshaking to upstream, client: 127.0.0.1, server: , request: "GET /? HTTP/1.1", upstream: "https://195.11.xx.16/?", host: "127.0.0.1:19101"
Thank you in advance.
I have configured Nginx as reverse proxy and each client calls are validated using the certificates. but when I browse in the client machine I get "400 Bad Request No required SSL certificate was sent"
I enabled error log and it says "client sent no required SSL certificate while reading client request headers, client: x.x.x.x, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "y.y.y.y", referrer: "https://y.y.y.y/"
I am not able to make out what is the problem it is trying to say.
my Nginx config changes
server {
error_log "C:/Error/error.log" debug;
listen 443 ssl;
server_name localhost;
#ssl_protocols TLSv1 TLSv1.1;
ssl_certificate "C:/Test/server.crt";
ssl_certificate_key "C:/Test/server.key";
ssl_client_certificate "C:/Test/ca.crt";
ssl_verify_client on;
#ssl_session_cache off;
#proxy_ssl_server_name on;
#proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
#proxy_ssl_session_reuse off;
location / {
root html;
index index.html index.htm;
proxy_pass https://10.10.10.10/webservice;
}
Thanks,
Vinod G
Your configuration tries to authenticate a client using it's certificate and it looks like the client is not sending it.
** ssl_client_certificate** is to indicate you want to validate client certificate against the trusted CAs you're pointing to. The server would then ask the client to send a certificate and must be failing when it doesn't receive it.
A pictorial guide of the process can be read here for a better understanding:
https://comodosslstore.com/blog/what-is-ssl-tls-client-authentication-how-does-it-work.html
To debug further:
Tools like wireshark can be used to examine if client is sending a cert
https://www.linuxbabe.com/security/ssltls-handshake-process-explained-with-wireshark-screenshot
Use a tool like Postman to set the client certificate and check if the server responds as expected
https://blog.getpostman.com/2017/12/05/set-and-view-ssl-certificates-with-postman/
common issues in this area and how to resolve them
https://www.thesslstore.com/blog/tls-handshake-failed/
I have a spring boot service configured for two way ssl to verify clients using certificates. It is behind nginx proxy server. So requirements are to configure nginx to provide transparent https connection from the client and forward client certificate to the webservice(backend) to be verified. Also to configure one way ssl for other services that don't require client authentication.
Something like:
|Client| -->httpS + Client Cert--->|NGINX|--->httpS + Client Cert--->|Service 1|
|Client| ------------>httpS----------->|NGINX| ------------>http------------>|Service 2|
My nginx config:
server {
listen 443;
server_name xx.xx.xx.xxx;
ssl on;
ssl_certificate /path/to/server/cert.crt;
ssl_certificate_key /path/to/server/key.key;
ssl_client_certificate /path/to/ca.crt;
ssl_verify_client optional;
location /service1/ {
proxy_pass https://service1:80/;
#Config to forward client certificate or to forward ssl connection(handshake) to service1
}
location /service2/ {
proxy_pass http://service2:80/;
#http connection
}
}
Also, is there a way to get the common name from the certificate to verify the client and take decisions in nginx? as using the CA is not enough.
Thanks..
This is not possible. What you are attempting to do is make the nginx proxy into a "man in the middle" and this will not be allowed by TLS by design.
I have some reason to use two nginx servers before the application server.
Both nginx servers using an SSL connection.
Nginx1 (SSL 443 and ssl_verify_client on) -> Nginx2 (SSL 443) -> App (9000).
On the first Nginx1 server I use the option: proxy_set_header client_cert $ssl_client_cert;
On the second server Nginx2 I use the option: underscores_in_headers on;
The problem is that the second Nginx2 server is sent only the first line of the certificate - "----- BEGIN CERTIFICATE -----".
How to pass a client certificate to the application server?
Nginx terminates SSL with no exception, so if you want this config anyway - you will need to have SSL config again and keep certificates on the server (here is relevant SO answer) or based on Nginx support discussion to use HAProxy in TCP mode. Here is the sample configuration article.
I found a Workaround for proxy client certificate
# NGINX1
...
map $ssl_client_raw_cert $a {
"~^(-.*-\n)(?<1st>[^\n]+)\n((?<b>[^\n]+)\n)?((?<c>[^\n]+)\n)?((?<d>[^\n]+)\n)?((?<e>[^\n]+)\n)?((?<f>[^\n]+)\n)?((?<g>[^\n]+)\n)?((?<h>[^\n]+)\n)?((?<i>[^\n]+)\n)?((?<j>[^\n]+)\n)?((?<k>[^\n]+)\n)?((?<l>[^\n]+)\n)?((?<m>[^\n]+)\n)?((?<n>[^\n]+)\n)?((?<o>[^\n]+)\n)?((?<p>[^\n]+)\n)?((?<q>[^\n]+)\n)?((?<r>[^\n]+)\n)?((?<s>[^\n]+)\n)?((?<t>[^\n]+)\n)?((?<v>[^\n]+)\n)?((?<u>[^\n]+)\n)?((?<w>[^\n]+)\n)?((?<x>[^\n]+)\n)?((?<y>[^\n]+)\n)?((?<z>[^\n]+)\n)?((?<ab>[^\n]+)\n)?((?<ac>[^\n]+)\n)?((?<ad>[^\n]+)\n)?((?<ae>[^\n]+)\n)?((?<af>[^\n]+)\n)?((?<ag>[^\n]+)\n)?((?<ah>[^\n]+)\n)?((?<ai>[^\n]+)\n)?((?<aj>[^\n]+)\n)?((?<ak>[^\n]+)\n)?((?<al>[^\n]+)\n)?((?<am>[^\n]+)\n)?((?<an>[^\n]+)\n)?((?<ao>[^\n]+)\n)?((?<ap>[^\n]+)\n)?((?<aq>[^\n]+)\n)?((?<ar>[^\n]+)\n)?((?<as>[^\n]+)\n)?((?<at>[^\n]+)\n)?((?<av>[^\n]+)\n)?((?<au>[^\n]+)\n)?((?<aw>[^\n]+)\n)?((?<ax>[^\n]+)\n)?((?<ay>[^\n]+)\n)?((?<az>[^\n]+)\n)*(-.*-)$"
$1st;
}
server {
...
location / {
...
proxy_set_header client_cert $a$b$c$d$e$f$g$h$i$j$k$l$m$n$o$p$q$r$s$t$v$u$w$x$y$z$ab$ac$ad$ae$af$ag$ah$ai$aj$ak$al$am$an$ao$ap$aq$ar$as$at$av$au$aw$ax$ay$az;
...
}
...
}
# NGINX 2
server {
...
underscores_in_headers on;
...
location / {
proxy_pass_request_headers on;
proxy_pass http://app:9000/;
}
...
}