TLS mutual authentication with golang and nginx
TLS mutual authentication
In TLS mutual authentication communicating parties authenticate each other at the same time. Authentication happens via public key certificates. For an example, in a client server scenario client validate server certificate and server validate client certificate. This is the default authentication mode in SSH
.
Scenario
I have golang
based http service and http client. I want to use TLS mutual authentication between client and server. My server exists behind the nginx
reverse proxy. I’m using nginx in server side to validate the client certificates. In client side, golang program itself verifies the server certificate.
Following are the steps that I have followed to achieve the mutual authentication between http client and http server.
1. Generate certificates
First of all I need to generate SSL certificates to client and server. I’m creating my own certificate authority(CA)
to issue the certificates.
1.1 Generate CA Certificate and Key
Generating CA Certificate/Key use to issue/sign the server and client certificates.
# CA key and certificate
openssl genrsa -des3 -out ca.key 4096
openssl req -new -x509 -days 365 -key ca.key -out ca.crt
1.2 Generate server Key and CSR
Need to generate server key and certificate signing request(CSR)
to obtain the server certificate.
# server key
openssl genrsa -des3 -out server.key 1024# CSR (certificate sign request) to obtain certificate
openssl req -new -key server.key -out server.csr
1.3 Generate server certificate
Generated certificate signing request(CSR) need to be signed by CA’s certificate/key to obtain the server certificate. It’s a self signed certificate.
# sign server CSR with CA certificate and key
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt
1.4 Generate client Key and CSR
Need to generate client key and certificate signing request(CSR) to obtain the client certificate.
# client key
openssl genrsa -des3 -out client.key 1024# CSR to obtain certificate
openssl req -new -key client.key -out client.csr
1.5 Generate client certificate
Generated certificate signing request(CSR) need to be signed by CS’s certificate/key to obtain the client certificate. It’s a self sign certificate.
# sign client CSR with CA certificate and key
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt
1.6 Remove pass phrase from server and client keys
When generating server key and client key, its asking for a password. We need to specify that password when loading the key(in nginx
and golang http client
). Otherwise it will give an error. I’m removing that pass phrase
from the key, then I can use the key without the password.
In here I’m copying the content of server and client key to temp file and making the temp as the key
# server key out to temp.key
openssl rsa -in server.key -out temp.key# remove server.key
rm server.key# make temp.key as server key
mv temp.key server.key
By same way I’m removing the pass phrase
of the client key.
# client key out to temp.key
openssl rsa -in client.key -out temp.key# remove client.key
rm client.key# make temp.key as client key
mv temp.key client.key
2. Configure nginx
2.1 Nginx conf
Then I need to configure nginx server to verify the client certificates. When request comes, nginx verifies the client certificate(weather it issued by the given CA). Following is the nginx config.
In here I’m using nginx as a reverse proxy
. When request comes to https://www.chainz.com:8443
nginx will redirect it to internal server in chainz:7070
(http server which runs as docker container). Read more about nginx reverse proxy from here.
Nginx adds some custom headers to the http request. These headers can further verify in the http server. I have added comments in each line to describe the functionality. Hope it’s simple and clear :)
2.2 Dockerize nginx
I’m running nginx as docker. Following is the docker file that I have used to dockerize the nginx.
In here I’m creating a volume for /etc/nginx/certs
(it is the place where all certificates and keys exists). Since certificate directory is a volume, I can keep certificates in map my local machine(instead of keeping inside container) and map it to the volume directory inside docker. Finally I’m adding nginx.conf
file as default.conf
. Other things are self explained :)
3. Golang https client
I have written golang https client to interact with the https server. This client sends the request with client certificate details. The request first goes to nginx. If the certificate is valid, nginx redirects it to https server. Following is the source code of my golang https client.
In here first I’m loading client key and certificate, then load the CA certificate. Finally send http post request with JSON
object.
4. Golang https server
In here I’m creating http server on port 7070
. It reads incoming request body, read the http headers and send JSON
response back.
Reference
- https://blog.theodo.fr/2015/09/protect-your-node-js-api-with-nginx-and-ssl-client-certificates/
- https://fardog.io/blog/2017/12/30/client-side-certificate-authentication-with-nginx/
- https://stackoverflow.com/questions/9380403/what-does-ssl-ctx-use-privatekey-file-problems-getting-password-error-indica/9380476
- https://medium.com/@itseranga/nginx-as-reverse-proxy-with-docker-c9ead938fffd