27

I have two servers, both have nginx. Server A is listening to 443 and is configured to authenticate with a Client SSL certificate.

Server B has an internal process that needs to communicate to Server A through nginx.

I'd like to configure Nginx on server B that will listen to 8080 (no encryption, since it's all local communication) and proxy_pass to ServerA:443.

The question is how do I inject a Client Certificate ? I didn't found any proxy_xxxx function that would do that.

I do know how to make an equivalent to that with socat, but my requirement is to use nginx.

Bastien974
  • 1,896
  • 12
  • 44
  • 62
  • 2
    Looking at the directives in the proxy module of nginx, it seems it is not possible to make a nginx server to use a cert to authenticate: http://nginx.org/en/docs/http/ngx_http_proxy_module.html Apache does support that feature, though. – NuTTyX Aug 20 '14 at 19:43
  • That's what I was afraid of... any idea if there's a custom module or something that can make this work ? That kind of feature has to exist ! – Bastien974 Aug 21 '14 at 12:49
  • I have found a utility to migrate config files from apache to nginx (https://github.com/nhnc-nginx/apache2nginx), so I downloaded it, created a dummy apache.conf and pass it through the tool, but I've got this result: `### Section 2: Unconverted directives ###` `# Flag Description` `# [S] Unsupported directives.` `# In conf file: dummy.conf` `# Line 32: SSLProxyMachineCertificateFile /path/to/cert (mod_ssl.c)` `# [S] SSLProxyMachineCertificateFile: No relevant directive in Nginx.` – NuTTyX Aug 21 '14 at 15:21

4 Answers4

34

Is it sufficient to have the client certificate details passed through?

You can add

proxy_set_header X-SSL-CERT $ssl_client_escaped_cert;

to your config and then the certificate info is available to server B via a X-SSL-Cert header.

dubek
  • 103
  • 3
jwilkins
  • 511
  • 3
  • 4
  • 1
    As comented [here](http://stackoverflow.com/questions/32957938/tls-client-authentication-with-nginx-and-tomcat-web-application#comment63326145_32958243), be aware if your backend can replace `\t` with `\n` from this header once readed. – lucasvc Jun 22 '16 at 13:01
  • 7
    According to [nginx's documentation](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#var_ssl_client_cert), the `$ssl_client_cert` variable is deprecated; the`$ssl_client_escaped_cert` variable should be used instead. – dubek Apr 01 '18 at 08:36
  • 2
    @dubek good catch, I would update the answer directly in cases like this. – Chris Stryczynski Apr 01 '18 at 14:40
  • I've been trying this solution, but it only seems to be passing the cert through if certificate validation is set ON. I don't want to turn it on, is there a way to make it work without setting the validation on? – juhako Oct 16 '19 at 11:58
  • @juhako I am having the same issue... I want Nginx to pass down the certificate information, but I dont want to turn on the `ssl_verify_client` (or put it as optional).... This is driving me crazy – Lucas Mattos Oct 08 '20 at 09:32
  • `proxy_set_header X-SSL-CERT $ssl_client_raw_cert;` is also an option (that at this time is not deprecated) in versions prior to 1.13.5+ when `$ssl_client_escaped_cert` was introduced. This answer led me down the right path, but wanted to call this out for others not running a new enough nginx version – Cameron Gagnon Mar 18 '21 at 20:32
8

The issue seems to be largely version dependend. On Ubuntu 14.04 LTS the default nginx is an outdated 1.4. First you need to install a PPA based version

https://leftshift.io/upgrading-nginx-to-the-latest-version-on-ubuntu-servers

shows how to do this with:

sudo add-apt-repository ppa:nginx/stable
sudo aptitude safe-upgrade

you should end up with:

nginx -v
nginx version: nginx/1.8.0

The configuration from @xatr0z answer https://serverfault.com/a/636455/162693 pointing to http://www.senginx.org/en/index.php/Proxy_HTTPS_Client_Certificate does not work:

non-working proposal

backend {
    server some-ip:443;
}

server {
    listen 80;


    location / {
        proxy_ssl_certificate        certs/client.crt;
        proxy_ssl_certificate_key    certs/client.key;


        proxy_pass https://backend;
    }
}

does not work out of the box with 1.8.0. It's probably meant as a hint only and not to be used as a configuration file as such or depends on another version.

I am testing with a apache2 based backend server A with SSL and self-signed client certificates enabled. The Apache config SSLOptions are set to:

SSLOptions +ExportCertData +FakeBasicAuth + StdEnvVars

This makes debugging the situation easier since a phpinfo() script on the backend side will show the Server and Client Side information.

To verify this i used:

https://backend/test/phpinfo

with the SSL certificate installed in the browser and I get sections like: SSL_SERVER_S_DN_CN for the server certificate and SSL_CLIENT_S_DN_CN for the client certificate.

As a first start I used (fill in the parts in brackets) to configure nginx on the frontend server B:

server {
  listen 8080;
  server_name <frontend>;

  location / {
    proxy_buffering off;
    proxy_pass https://<backend>;
    #proxy_ssl_certificate      certs/<SSL Client Certificate>.crt;
    #proxy_ssl_certificate_key  certs/<SSL Client Certificate>.key;
  }
}

uncomenting the SSL Client Certificate specific part just to check that the reverse proxy itself works.

nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
service nginx restart
nginx stop/waiting
nginx start/running, process 8931

Now http://frontend:8080/test/phpinfo.php works The

SSL_SERVER_S_DN_CN for the server certificate is displayed and SSL_CLIENT_S_DN_CN for the client certificate is not (yet) displayed

Now after uncommenting:

server {
  listen 8080;
  server_name <frontend>;

  location / {
    proxy_buffering off;
    proxy_pass https://<backend>;
    proxy_ssl_certificate      certs/<SSL Client Certificate>.crt;
    proxy_ssl_certificate_key  certs/<SSL Client Certificate>.key;
  }
}

and checking/restarting

nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
service nginx restart
nginx stop/waiting
nginx start/running, process 8931

http://frontend:8080/test/phpinfo.php works and

SSL_SERVER_S_DN_CN for the server certificate is displayed and SSL_CLIENT_S_DN_CN for the client certificate is displayed

so now we got things working as asked for.

Please note bug https://trac.nginx.org/nginx/ticket/872#ticket

Wolfgang Fahl
  • 593
  • 1
  • 6
  • 14
  • You might want to watch out for the renegotiating issue https://www.ruby-forum.com/topic/6875137 which might spoil the show – Wolfgang Fahl Jan 05 '16 at 19:28
6

Apparently, this is what you are looking for: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate Available since version 1.7.8.

location / {
    ...
    proxy_pass     the_other_nginx;
    proxy_ssl_certificate  the_certificate.pem;
    ...
}
  • 1
    This is wrong. This assigns a client cert to the proxy to be used for the requests to the backend. But the asked, said that this communication is unencrypted an local only, so there is no client certificate checking. The answer of jwilkins works well. – Kenyakorn Ketsombut Sep 10 '15 at 07:01
  • 1
    @KenyakornKetsombut I believe you misread the question. Server B has no encryption (listening on 8080) but has to communicate to server A (listening on 443, with encryption). So B need to send the client cert to A to authenticate. You can use proxy_ssl_certificate to accomplish that. jwilkins answer, will forward to A the cert given to B. Both may work depending on what you need. – Nicolas Malbran Sep 17 '15 at 16:47
  • Hi Nicolas. I was trying to say: if Server B is not using encryption (on port 8080) it does not use any thing like HTTPS/SSL or however you call it. Client certificates are a part of SSL, so this part can't be done by Server B. B cannot send or receive any client certificate. – Kenyakorn Ketsombut Sep 18 '15 at 03:33
  • 1
    I get nginx: [emerg] no "proxy_ssl_certificate_key" is defined for certificate "certs/Roro_Client.pem" nginx: configuration file /etc/nginx/nginx.conf test failed when I try this with 1.8.0 – Wolfgang Fahl Jan 05 '16 at 10:00
0

There's quite a neat article on nginx and SSL client certificates; it uses PHP with FastCGI as the example but I'm think you can be adapt that to a reverse proxy setup:

server {
    listen        443;
    ssl on;
    server_name example.com;

    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 optional;

    location / {
        root           /var/www/example.com/html;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_param  SCRIPT_FILENAME /var/www/example.com/lib/Request.class.php;
        fastcgi_param  VERIFIED $ssl_client_verify;
        fastcgi_param  DN $ssl_client_s_dn;
        include        fastcgi_params;
    }
}

Source http://nategood.com/client-side-certificate-authentication-in-ngi

Erik Kaplun
  • 109
  • 4