TORIPIYO DIARY

recent event, IoT, programming, infrastructure topics

How to Implement Nginx Client Certificate Authentication

When we need to collaborate with outside companies to manage web site's content, admin tool site is sometimes used. So admin tool has to be exposed on the Internet network. In this case password authentication function is often used to restrict other client's access. But Nginx can also use client certificate authentication function. So I would like to introduce client certificate implementation in this article.

client certificate authentication benefits
1. Nginx can deny irrelevant user's access.
2. Nginx can revoke issued client certificate at any time.

About 1, even if username and password is not leaked, hackers try to do a brute force attack indiscriminately. So password authentication might be broken someday. Client certificate authentication doesn't allow user access who doesn't have a client certificate. This feature makes the site more secure that is exposed to the Internet Network.
About 2, Nginx administrator can revoke the client certificate to deny admin tool access from unauthorized user when outside company's contract finishes.

How to implement client certificate authentication by Nginx

Nginx server side settings

Off course root user is used when configure Nginx settings. In this example Nginx is installed under "/usr/local/nginx" directory. Please adopt below instructions to your environment properly.

1. generate CA certificate and Key

mkdir -pv /usr/local/nginx/conf/client-certificates
cd /usr/local/nginx/conf/client-certificates

openssl genrsa -out ca.key 2048

# set proper certificate expiration date, in this example 2 years
# set site's domain name on "CN" field
openssl req -new -x509 -days 730 -key ca.key -out ca.crt -subj "/C=JP/ST=Tokyo/L=Ota-ku/O=Cat Inc./OU=Development/CN=tool.toripiyo.com/emailAddress=admin@toripiyo.com"

2. generate CRL

# configure CRL for revocation settings
touch /etc/pki/CA/index.txt  && echo '01' > /etc/pki/CA/crlnumber
openssl ca -name CA_default -gencrl -keyfile ca.key -cert ca.crt -out ca.crl -crldays 730

diff -U0 <(openssl x509 -noout -modulus -in ca.crt) <(openssl rsa -noout -modulus -in ca.key)


3. generate client certificate for each user

# In this example "toripiyo" user's client certificate is generated
# Please iterate this process as you require
_CLIENTNAME='toripiyo'

mkdir /usr/local/nginx/conf/client-certificates/${_CLIENTNAME}
cd /usr/local/nginx/conf/client-certificates/${_CLIENTNAME}
openssl genrsa -out ${_CLIENTNAME}.key 2048
openssl req -new -key ${_CLIENTNAME}.key -out ${_CLIENTNAME}.csr -subj "/C=JP/ST=Tokyo/L=Ota-ku/O=Cat Inc./OU=Development/CN=${_CLIENTNAME}/emailAddress=admin@toripiyo.com"

openssl x509 -req -days 365 -in ${_CLIENTNAME}.csr -CA ../ca.crt -CAkey ../ca.key -CAcreateserial -CAserial ../ca.seq -out ${_CLIENTNAME}.crt

# this command execution prompts the password input. don't forget the password. the password is used for client certificate registration
openssl pkcs12 -export -clcerts -in ${_CLIENTNAME}.crt -inkey ${_CLIENTNAME}.key -out ${_CLIENTNAME}.p12

# set file permission to 640
chmod 640 ${_CLIENTNAME}.*
ls -lta

4. configure Nginx settings

cd /usr/local/nginx/conf
cp -ivp nginx.conf{,.$(date +%Y%m%d)}
vi nginx.conf
# add the following directives that are related to client authentication
# - set clientcert log format
# - ssl_verify_client on: enable client certificate authenticate. if client certificate is not shown, access is defied.
----------------------------------------------------------------------------------------------
log_format clientcert '$remote_addr - $remote_user [$time_local] "$request" '
                   '$status $body_bytes_sent "$http_referer" '
                   '"$http_user_agent" "$http_x_forwarded_for" '
                   '$request_time $upstream_response_time '
                   '"$ssl_client_s_dn($ssl_client_serial)"';

ssl_client_certificate client-certificates/ca.crt;
ssl_crl client-certificates/ca.crl;
ssl_verify_client on;

access_log  logs/host.access.log  clientcert;
----------------------------------------------------------------------------------------------

5. restart Nginx

# stop Nginx daemon
ps aux | grep -i nginx
/etc/init.d/nginx stop
ps aux | grep -i nginx
less /usr/local/nginx/logs/error.log

# start Nginx daemon
/etc/init.d/nginx start
ps aux | grep -i nginx
less /usr/local/nginx/logs/error.log

Client side settings

Client certificate file name will be ${_CLIENTNAME}.p12. On the way to client side settings requires the password that is set on above procedure.

macOS safari browser's client certificate settings

Adding a client certificate | Cybozu manual site

iOS safari browser's client certificate settings

Adding a client certificate | Cybozu manual site

typical regular operations for Nginx client certificate management

issue new client certificate for new user

If new client(tama) joins our team, issue the new client certificate by following below procedure.

1. issue client certificate
_CLIENTNAME='tama'

mkdir /usr/local/nginx/conf/client-certificates/${_CLIENTNAME}
cd /usr/local/nginx/conf/client-certificates/${_CLIENTNAME}
openssl genrsa -out ${_CLIENTNAME}.key 2048
openssl req -new -key ${_CLIENTNAME}.key -out ${_CLIENTNAME}.csr -subj "/C=JP/ST=Tokyo/L=Ota-ku/O=Cat Inc./OU=Development/CN=${_CLIENTNAME}/emailAddress=admin@toripiyo.com"

openssl x509 -req -days 365 -in ${_CLIENTNAME}.csr -CA ../ca.crt -CAkey ../ca.key -CAcreateserial -CAserial ../ca.seq -out ${_CLIENTNAME}.crt

# password can be put on as a openssl option directly. "doraemon" is used as a password in this case
openssl pkcs12 -password doraemon -export -clcerts -in ${_CLIENTNAME}.crt -inkey ${_CLIENTNAME}.key -out ${_CLIENTNAME}.p12

# set file permission to 640
chmod 640 ${_CLIENTNAME}.*
ls -lta

2.Nginx restart is not required

3. Share new client certificate with new user

revoke specific client certificate

When tama leaves our team, following procedure makes the certificate to be revoked.

1. update index.txt
_CLIENTNAME='tama'
cd /usr/local/nginx/conf/client-certificates
openssl ca -name CA_default \
    -revoke ${_CLIENTNAME}/${_CLIENTNAME}.crt \
    -keyfile ca.key \
    -cert ca.crt

2. update ca.crl
openssl ca -name CA_default -gencrl \
    -keyfile ca.key \
    -cert ca.crt \
    -out ca.crl \
    -crldays 730

3. restart nginx
ps aux | grep -i nginx
/etc/init.d/nginx stop
ps aux | grep -i nginx
less /usr/local/nginx/logs/error.log

/etc/init.d/nginx start
ps aux | grep -i nginx
less /usr/local/nginx/logs/error.log

reactivate the revoked certificate

If tama withdraws team leaving, following procedure makes the certificate to be reactivated.

1. modify index.txt
vi /etc/pki/CA/index.txt
====================
1. change "R" => "V" on first column of corresponding certificate row
2. delete third column characters of corresponding certificate row (important: tabs between second and fourth colomn not to be deleted!! If not, openssl outputs error message.)
====================

2. update ca.crl
cd /usr/local/nginx/conf/client-certificates
openssl ca -name CA_default -gencrl \
    -keyfile ca.key \
    -cert ca.crt \
    -out ca.crl \
    -crldays 730

3. restart nginx
ps aux | grep -i nginx
/etc/init.d/nginx stop
ps aux | grep -i nginx
less /usr/local/nginx/logs/error.log

/etc/init.d/nginx start
ps aux | grep -i nginx
less /usr/local/nginx/logs/error.log