nginx - reverse proxy certificate authentication - ssl

I´m trying to use nginx as a reverse proxy to an internal webserver running Tomcat, which hosts a front-end to our ERP system.
It is already working fine: I can perfectly connect to the nginx server (which is locked up on our network, different VLAN, firewall, etc etc etc) and then reverse proxy to my ERP server.
However, I want do add an extra layer of protection, by requiring users to have a digital certificate on their computer, so they can access the first (nginx) server. The certificate is not used/necessary to the back-end server.
I´ve been through this tutorial
http://nategood.com/client-side-certificate-authentication-in-ngi
which allowed me to generate my self-signed certificates and everything else.
When using ssl_verify_client optional on nginx configuration, I can connect normally to my back-end server, but no certificate is asked/required.
When I switch it to ssl_verify_client on , all access are then blocked by a
400 Bad Request
No required SSL certificate was sent
No matter which browser I am using (Chrome, IE, Edge, Firefox). Of course I´ve put all certificates/chain on my client computer, but no certificate is asked on any browsers. What I am missing?
Here is my full nginx config:
server {
listen 443;
ssl on;
server_name 103vportal;
ssl_password_file /etc/nginx/certs/senha.txt;
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 on;
location / {
proxy_pass http://10.3.0.244:16030;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300;
proxy_send_timeout 300;
}
}

Assuming you have generated a private key and a certificate request for your user and signed it with your client CA. You need to get the private key and the signed certificate into the list of personal certificates in the browser.
I have found that the best way is to create a password protected PKCS#12 (as some browsers insist on password protection). I use the following OpenSSL command:
cat user.key user.crt | openssl pkcs12 -export -out user.p12

Related

Avoid adding exception on website if ca-certificate is not installed

I have created my own ca certificates with openssl. I have four files :
ca.key (private ca key)
ca.crt (public ca certificate)
server.key (private key)
server.crt (public certificate signed by the ca files)
I use nginx with ssl configuration.
ssl_certificate server.crt;
ssl_certificate_key server.key;
If I install the ca public certificate on my computer, my browser let me go on the website without adding exception which is good. But if I have not the ca certificate installed on my computer, I have to add exception to pass. I want to avoid adding exception and force user to have the ca certificate installed on their computer.
I configure nginx with hsts (http strict transport security) :
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate ca.crt;
Unfortunately I have again the possibility to add an exception to pass.
I'm wondering if it is possible to do this ?
I realize that my certificates doesn't work with chrome missing_subjectAltName. So I fixed it with this link. And now I have this message with chrome if my ca certificate is not installed on my computer :
You cannot visit localhost right now because the website uses HSTS. Network errors and attacks are usually temporary, so this page will probably work later.
This is exactly what I want. However it doesn't work on firefox et let me the possibility to add an exception.
Why chrome block it and not firefox ? How can I do that on firefox too ?

Nginx certbot SSL not working with Cloudflare

Have recently moved to CloudFlare as I wanted a DNS service that provided DNS credentials for certbot to generate a wildcard SSL certificate.
However, I am struggling to get a basic SSL Nginx setup running.
Background:
DNS resolution works fine. (When I just have an Nginx HTTP server block, the website loads insecurely over HTTP)
Can connect to website via direct IP with HTTPS address (not via CloudFlare IP). It gives an invalid cert warning but still connects.
openssl s_client -connect property-connect.co.uk:443 -servername property-connect.co.uk seems to indicate the SSL certificate is fine
Steps:
Ran certbot --nginx specified include both domains (www.property-connect.co.uk property-connect.co.uk)
sudo nginx -t was successful
sudo nginx -s reload
nginx.conf
events {
}
http {
server {
server_name www.property-connect.co.uk property-connect.co.uk;
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
location / {
proxy_pass http://localhost:700;
}
ssl_certificate /etc/letsencrypt/live/property-connect.co.uk-0001/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/property-connect.co.uk-0001/privkey.pem; # managed by Certbot
}
}
Any thoughts as to what I am missing with this basic config?
Your current certificate for this domain issued and managed by Cloudflare itself, not by your CertBot/Nginx:
$ openssl s_client -connect property-connect.co.uk:443 -servername property-connect.co.uk </dev/null 2>&1 | grep ^issuer
issuer=C = US, ST = CA, L = San Francisco, O = "CloudFlare, Inc.", CN = CloudFlare Inc ECC CA-2
I guess this is because you have proxied traffic via Cloudflare to your host
You should switch your domain to DNS only and then you will able to connect directly to your Nginx without Cloudflare reverse-proxying.
Turns out I just needed to change the Cloudflare SSL setting from 'Flexible' (Browser -> HTTPS -> Cloudflare -> HTTP -> Web server) to 'Full' (Browser -> HTTPS -> Cloudflare -> HTTPS -> Web server).

Nginx; how to use OCSP to verify the SSL client certificate

I am using Nginx to create a secure connection; when I revoked the client certificate, I also can connect to Nginx by https, I know I should config the ssl_crl directives, but I want to use OCSP to verify the client certificate, How should I do? I found Nginx use OpenSSL library to establish ssl connection, Is there something I should do with openssl.cnf file?
Client certificate validation with OCSP feature has been added to nginx 1.19.0+.
For example:
ssl_verify_client on;
ssl_ocsp on;
resolver 192.0.2.1;
ssl_ocsp enables OCSP validation of the client certificate chain.
ssl_ocsp leaf; enables validation of the client certificate only. By default ssl_ocsp is set to off.
ssl_verify_client directive should be set to on or optional for the OCSP validation to work
resolver should be specified to resolve the OCSP responder hostname.
Update
Nginx added support for client certificate validation with OCSP in version 1.19.0, released 26 May 2020. See ssl_ocsp and related directives.
Original answer
Nginx does not support OCSP validation of client certificates. The only option of validating client certificates is to use CRLs, update them and reload Nginx to apply the changes.
In this thread one of the leading Nginx developers confirms that and says that nobody is working on it as of 2014:
https://forum.nginx.org/read.php?2,238506,245962
Prerequirements:
running pki with OCSP configured
NginX Server config
# Specifies a file with trusted CA certificates in the PEM format used to verify client certificates and OCSP responses if ssl_stapling is enabled.
# The list of certificates will be sent to clients. If this is not desired, the ssl_trusted_certificate directive can be used.
ssl_client_certificate /etc/nginx/client_certs/ca.crt;
ssl_verify_client on;
ssl_stapling on; #Yes this has to be configured to use OCSP
resolver 192.0.2.1;
information on ssl_verify_client
informations on ssl_client_certificate
This is just a sample of how the code should look like in your server block:
server {
# Listen on port 443
listen 443 default_server;
server_name example.com;
root /path/to/site-content/;
index index.html index.htm;
# Turn on SSL; Specify certificate & keys
ssl on;
ssl_certificate /etc/nginx/ssl/example.com/my_certificate.crt;
ssl_certificate_key /etc/nginx/ssl/example.com/example.key;
# Enable OCSP Stapling, point to certificate chain
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/full_chain.pem;
}
make sure the certificates match your paths, and then Save your work.
Test your configuration before reloading...
and last, restart or reload Nginx by either of the following commands:
sudo service nginx reload
or
sudo service nginx restart
Final step, test your OCSP Stapling through this link to make sure your SSL is working or not:
OCSP Stapling SSL Checker

Nginx SSL Certificate failed SSL: error:0B080074:x509 (Google Cloud)

My server was hosted in Bluehost (Apache), the certificate was working fine. Now, I'm using Google Cloud for multiple pages in NodeJS on different port using proxy_pass. I am trying to configure the SSL but I have problems. I was looking for similar questions, but it still shows the same error. I created the key file following this link
/var/log/nginx/error.log:
2015/07/08 10:47:20 [emerg] 2950#0: SL_CTX_use_PrivateKey_file("/etc/nginx/ssl/domain_com/domain_com.key") failed (SSL: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch)
When I put on console:
openssl rsa -noout -modulus -in domain_com.key shows me this:
Modulus=D484DD1......512 characters in total......5A8F3DEF999005F
openssl x509 -noout -modulus -in ssl-bundle.crt:
Modulus=B1E3B0A.......512 characters in total......AFC79424BE139
This is my Nginx setup:
server {
listen 443;
server_name www.domain.com;
ssl_certificate /etc/nginx/ssl/domain_com/ssl-bundle.crt;
ssl_certificate_key /etc/nginx/ssl/domain_com/domain_com.key;
ssl on;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/domain_com.access.log;
location / {
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_pass http://localhost:8086;
proxy_read_timeout 90;
proxy_redirect http://localhost:8086 https://www.domain.com;
}
}
The problem may occur in case of wrong concatenation order. You tried:
cat www_example_com.crt COMODORSADomainValidationSecureServerCA.crt COMODORSAAddTrustCA.crt AddTrustExternalCARoot.crt > ssl-bundle.crt
Which looks correct, but concatenation usually require to eliminate extra download from root CA, therefore Nginx creator said:
Browsers usually store intermediate certificates which they receive
and which are signed by trusted authorities, so actively used browsers
may already have the required intermediate certificates and may not
complain about a certificate sent without a chained bundle.
The official docs explicitly says:
If the server certificate and the bundle have been concatenated in
the wrong order, nginx will fail to start and will display the error
message:
SSL_CTX_use_PrivateKey_file(" ... /www.example.com.key") failed
(SSL: error:0B080074:x509 certificate routines:
X509_check_private_key:key values mismatch)
because nginx has tried to use the private key with the bundle’s first
certificate instead of the server certificate.
So to solve the problem please try:
Attach www_example_com.crt to ssl_certificate Nginx config key
Download latest Comodo CA certificates SHA2 from official web page and try one more time to concatenate the bundle

400 Bad Request: The SSL certificate error

I get this error when I try to get page with client key and certificate using this command:
curl -v -s --key /home/dmitry/Downloads/client_cert/client.mysite.key --cert /home/dmitry/Downloads/client_cert/client.mysite.crt https://mysite.com/api/login/
Here's what I see in nginx logs:
2014/12/08 06:30:55 [crit] 13087#0: *404 SSL_do_handshake() failed (SSL: error:14094085:SSL routines:SSL3_READ_BYTES:ccs received early) while SSL handshaking, client: xxx.xxx.xxx.xxx, server: 0.0.0.0:443
And here is part of my nginx.conf:
server {
listen 443 ssl;
ssl_certificate /home/mysite/conf/dev/ssl/com.mysite.crt;
ssl_certificate_key /home/mysite/conf/dev/ssl/com.mysite.key;
ssl_client_certificate /home/mysite/conf/dev/ssl/com.mysite.crt;
ssl_verify_client optional;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
server_name mysite.com www.mysite.com;
access_log /home/mysite/logs/nginx_access.log;
error_log /home/mysite/logs/nginx_error.log;
location /api/{
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header SSL-client-serial $ssl_client_serial;
proxy_set_header SSL-client-dn $ssl_client_s_dn;
proxy_set_header SSL-client-verify $ssl_client_verify;
if ($ssl_client_verify != SUCCESS) {
return 403;
break;
}
}
}
Here are the commands I've used to create client cert:
openssl req -out client.mysite.csr -new -newkey rsa:2048 -nodes -keyout client.mysite.key
openssl x509 -req -days 3650 -in client.mysite.csr -CA com.mysite.crt -CAkey com.mysite.key -set_serial 01 -out client.mysite.crt
What could be wrong here? Should I use some other certificate as CA for my client cert than server cert?
UPDATE:
When I do
openssl verify -CAfile com.mysite.crt client.mysite.crt
I get:
error 20 at 0 depth lookup:unable to get local issuer certificate
First of all, enable debug log in nginx.conf:
error_log logs/error.log debug;
And restart nginx. Then repeat the request and check the log file. Find the first line with verify:0:
2019/12/05 22:34:50 [debug] 5980#9776: *17 verify:0, error:20, depth:0, subject:"/CN=...", issuer:"/CN=..."
Here you see error:20. The error code comes from OpenSSL. Here you can find the constant name by code and here the corresponding description by constant name.
Alternatively you can verify the certificate using openssl command line tool:
openssl verify -CAfile ca.crt client.crt
To verify it as the server sees it, ca.crt has to be the file listed in ssl_client_certificate or ssl_trusted_certificate directive in nginx.conf.
To verify the certificate on its own, ca.crt has to be the certificate that was used to sign client.crt. If it is self-signed, it'll be client.crt itself (client.crt will be twice in a row).
If you're getting error 20 specifically and your client certificate is self-signed, you might have encountered this bug. To fix it you should either drop keyUsage from your certificate entirely or add keyCertSign to the list. To verify whether you've stumbled upon it, check whether Key Usage is listed in X509v3 extensions: section in the output of the following command:
openssl x509 -in client.crt -text -noout
The certificate I used to sign another one was not CA so it simply could not be verified, so that's why I had this error from openssl verify command:
error 20 at 0 depth lookup:unable to get local issuer certificate
If you're not CA then obviously there's nothing you can do about it.
ccs received early
Looks like a fallout from fixes for CVE-2014-0224. Since patches seems to be available check that your system is up-to-date or report the bug to your distributor.
More details might be available if you would add information about the server system you are running, especially which OS, which version of OpenSSL and which patches.
In my case I mistakenly downloaded 'cloudflare.crt' file from digital ocean's website which has older certificate and that wasted quite a bit of my time.
As their tutorial shows up in the google search.
wrong certificate from digital ocean
link to correct certificate
I have A nice working way. First before Creating pointing domain i.e
server_name api.example.com;
location / {
proxy_pass "http://example.com:9191";
}
create a sub domain of api.domain.com in your cpanel then now locate your crt files here
/var/cpanel/ssl/apache_tls/api.domain.com
you will find combined file done now put combine file i.e
ssl_certificate /var/cpanel/ssl/apache_tls/api.domain.com/combined;
then ssl you will find it under cpanel ssl/tsl->install ssl
You can locate where your ssl file are via google
your end ssl configuration will be like this
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name api.example.com;
location / {
proxy_pass "http://example.com:9191";
}
location /socket.io/ {
proxy_pass "http://example.com:9191";
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
location /engine.io/ {
proxy_pass "http://example.com:9191";
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
ssl_certificate /var/cpanel/ssl/apache_tls/api.domain.com/combined;
ssl_certificate_key /var/cpanel/ssl/system/certs/crashgame/private.key;
}
Please have your Private key in path where you can locate