Skip to main content

NGINX Server

Securosys HSM Integration Guide - PKCS #11

The following example shows how to set up NGINX to work with the OpenSSLv3 pkcs11-provider.

tip

If you prefer a native set-up instead, the nginx.conf in this section and the section Preparing the key and certificate will be the most relevant for you.

note

The support for reading keys from a PEM file was added to the pkcs11-provider in version 0.3-22. Please use the release 0.4 or newer.

For this example an NGINX server is started as a service in systemd.

Install nginx

Update the package list.

sudo apt update

Install Nginx.

sudo apt install nginx

Configure nginx's systemd to use the correct openssl file.

nano /usr/lib/systemd/system/nginx.service

Under the section [Service], add the line of the environment of where your openssl.cnf file is located. This entry should be before ExecStartPre.

Environment="OPENSSL_CONF=/etc/primus/openssl.cnf"

Preparing the key and certificate

For this example a simple self-signed certificate is created. For real applications you probably want to get your certificate signed by a CA and put the certificate chain into the cert.pem-file.

Again some environment variables are set-up as placeholders.

export P11_TOKEN=<YOUR_USER_NAME>	# partition name
export P11_PIN=<YOUR_PKCS#11_PIN> # hsm pkcs11 pin (don't use for production)
export P11_KEY_NAME=TESTING_NGINX_KEY # name of Key
mkdir -p /etc/nginx/certificates/

A RSA-4096 private key is generated

openssl genpkey -propquery "provider=pkcs11" \
-algorithm "rsa" -pkeyopt "rsa_keygen_bits:4096" \
-pkeyopt "pkcs11_uri:pkcs11:token=${P11_TOKEN};object=${P11_KEY_NAME}?pin-value=${P11_PIN}"

Given the encoder of the OpenSSL pkcs11-provider is enabled, the key will be outputted as a "PKCS#11 PROVIDER URI" pem file.

Use the key to create a self signed certificate

openssl req -new -x509 -copy_extensions=copyall \
-key "pkcs11:type=private;token=${P11_TOKEN};object=${P11_KEY_NAME}?pin-value=${P11_PIN}" \
-subj "/C=CH/ST=Bern/L=Bern/O=My Example Organisation/OU=IT Department/CN=www.example.com" \
-addext "subjectAltName = DNS:www.example.com, DNS:*.www.example.com" \
-sha256 -days 99 -out /etc/nginx/certificates/p11-provider-cert.pem

For long-term usage, the PKCS PIN can be stored on the machine running Nginx. For security reasons, the "pin-value" should be obfuscated when used by the Nginx process.

Create a new file /run/secrets/securosys_p11_pin to be called instead, via pin-source=file:/run/secrets/securosys_p11_pin. The OpenSSL decoder will then try to read the pkcs11-pin from /run/secrets/securosys_p11_pin.

warning

Each time the VM is rebooted, the file /run/secrets/securosys_p11_pin containing the P11 PIN, will be deleted. As a workaround, you can create a persistent file at /etc/primus/securosys_p11_pin containing the P11 PIN.

Then, modify the /usr/lib/systemd/system/nginx.service file by adding the line ExecStartPre=cp -p /etc/primus/securosys_p11_pin /run/secrets under the "[Service]" section, when configuring nginx further in this page.

The Python script included in the OpenSSL pkcs11-provider's source can be used. Please note that Securosys does not maintain this script and cannot guarantee it will be available and functional in perpetuity.

As an alternative, in the shell script below, the pem-file is created using tools commonly available on *nix systems.

touch generate.sh
chmod +x generate.sh

#!/bin/bash
make-pkcs11-uri-pem() {
# helper function to create a PEM file from a pkcs11-uri provided as argument
export LC_ALL=C
URI=$1
URI_HEX=$(printf '%s' "${URI:?}" | od -An -t x1)
DESC="PKCS#11 Provider URI v1.0"
DESC_HEX=$(printf '%s' "${DESC}" | od -An -t x1)
PEM_HEX=$(printf '30 82 %04x 1a 82 %04x %s 0c 82 %04x %s' \
"$((${#URI} + ${#DESC} + 8))" "${#DESC}" "${DESC_HEX[*]}" \
"${#URI}" "${URI_HEX[*]}" \
| tr -d '\r\n\t ' | sed -e 's,\(.\{2\}\),\\x\1,g')
# shellcheck disable=SC2059 # printf should use PEM_HEX as format string
PEM=$(printf "${PEM_HEX}" | base64)
printf "%s\n%s\n%s" \
"-----BEGIN PKCS#11 PROVIDER URI-----" \
"${PEM}" \
"-----END PKCS#11 PROVIDER URI-----"
}
make-pkcs11-uri-pem "pkcs11:type=private;token=${P11_TOKEN};object=${P11_KEY_NAME}?pin-source=file:/run/secrets/securosys_p11_pin" \
> /etc/nginx/certificates/p11-provider-pkey.pem
chmod 644 /etc/nginx/certificates/*
chgrp www-data /etc/nginx/certificates/*

Execute this script to generate and update the file's permissions.

The certificates directory is ready, it contains the cert.pem and the pkey.pem referenced in the NGINX configuration. The pkey.pem contains the pkcs11-uri to the private key, and a file uri to load the pkcs11-pin.

To finilize the configuration of the Nginx server, we need to reference our newly created keys and cert.

nano /etc/nginx/sites-enabled/primus

server {
listen 8082 ssl http2;
listen [::]:8082 ssl http2;
server_name 34.133.134.100;
root /var/www;
ssl_certificate /etc/nginx/certificates/p11-provider-cert.pem; # path of the certificate located in the server
ssl_certificate_key /etc/nginx/certificates/p11-provider-pkey.pem; # path of the PKCS#11 PROVIDER URI

ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_prefer_server_ciphers on;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
}

location = /404.html {
}
}

Lastly, we need to allow the nginx process to view the PKCS11 secrets file so that it can use to authenticate to the HSM. To do this, we can simply add the www-data user in the primus group.

usermod -a -G primus www-data

As we made changes to the systemd file of Nginx, we need to reload and run some sanity tests.

# reload the systemd daemon
sudo systemctl daemon-reload

# test nginx
nginx -t
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

# restart nginx
sudo systemctl restart nginx-service

After that, our nginx server will be running and accessible.

More resources: