Friday, 19 August 2016

Setup Mutual (2-way) SSL Authentication with apache / httpd

Client certificates can provide a great way of helping to secure a service that you are unable to (or unpredictably) lock down to a specific source.

For example quite often VPN's and reverse proxies will rely on certificate authentication to provide access to users from the internet - while the service is publicly available (at least on a network level) a valid private key is needed to access the service.

In this scenario we wish to have four main requirements:

A: The remote web service should be authenticated.
B: The remote web service should ensure that we are who we say we are
C: Everything should be encrypted.
D: The client should not have to manually send the client certificate to the server - this should be injected by a reverse proxy on the client side.

A & C can be met using a typical SSL/TLS setup on the remote server.
B can be achieved with the use of client certificate authentication.
D can be achieved by using a reverse proxy (e.g. apache, nginx) on the client side.

Below I have illustrated the high-level design for the 2-way authentication:



Firstly ensure that openssl is installed on the server (almost certainly is in most cases) with:

rpm -qa | grep openssl

The basic process flow is as follows - you (the server administrator) setup your own root CA, you then issue a certificate (and corresponding private key) for a connecting client - when the client connects to your server they will present the public/private key pair they were issued - the server will then validate them and check whether the CA issued them - if successful the user will be granted access to the web service - otherwise the request will fail.

So we should firstly setup our own CA starting by generating our private key:

mkdir /tmp/ssl && cd /tmp/ssl
openssl genrsa -out rootca.key 2048 -nodes

We should now self-sign the certificate:

openssl req -x509 -new -nodes -key rootca.key -sha256 -days 1024 -out rootca.pem

and then ensure that the private key is stored securely somewhere (ideally completely offline) - as if someone get holds of it they will be able to generate certificates freely!

Now the next step is to issue a certificate to our client:

openssl genrsa -out client.key 2048 -nodes

generate a certifcate signing request from it:

openssl req -new -key client.key -out client.csr

and finally sign it with our CA:

openssl x509 -req -in client.csr -CA rootca.pem -CAkey rootca.key -CAcreateserial -out client.crt -days 365 -sha256

rm client.csr

We can now validate our certificate has been issued by our CA with:

openssl verify -verbose -CAfile rootca.pem  client.crt

Now on the server side we will need to configure something like apache or nginx to handle the SSL termination and client certificate authentication (remembering to copy the rootca.pem certificate over) - please refer to here for an example: http://blog.manton.im/2016/04/setting-up-client-ssltls-authnetication.html

Now on the client side we wish to inject the client certificate and its corresponding private key into requests (so the requesting application does not have to do so) - we do this with the 'SSLProxyMachineCertificateFile' directive.

Ensure that the certificate and key is in PEM format and then concatenate them:

cat client.crt client.key > client_bundle.pem

and update your apache vhost configuration with the following addition:

SSLProxyMachineCertificateFile /etc/httpd/ssl/client_bundle.pem

Finally reload apache:

sudo service httpd restart

So to summarize the process flow:

A client from company A makes a request to 'Service1' that is located at Company B's site - the request gets routed through Company A's (the client) reverse proxy: proxy.companya.com and in turn then routes it to company B's proxy: proxy.companyb.com and then finally routes it to the application server running 'Service1'.

For reference I have included the server block from the upstream host (the host that the client connects to):

server {
  listen 10.11.12.13:443 ssl;
  server_name backend.mydomain.com;

  ssl_certificate /etc/nginx/ssl/mykey.pem;
  ssl_certificate_key /etc/nginx/ssl/mykey.key;
  proxy_ssl_certificate /etc/nginx/ssl/client.crt;
  proxy_ssl_certificate_key /etc/nginx/ssl/client.key;
  proxy_ssl_server_name on;

  access_log  /var/log/nginx/backenddom.log  combined;
  error_log  /var/log/nginx/backenddom.error.log debug;

  # Reverse proxy configuration
     location / {
     proxy_pass  https://backend.mydomain.com;
     proxy_set_header        Host            $host;
     proxy_set_header        X-Real-IP       $remote_addr;
     proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
   }
}


0 comments:

Post a Comment