Use a Cloud HSM key to serve Apache traffic

This guide provides instructions for setting up an Apache server to use a Cloud HSM key for TLS signing on Debian 11 (Bullseye). You might need to modify these commands to work with your OS or Linux distribution.

You can find a Terraform-based blueprint version of this tutorial in the kms-solutions GitHub repository.

Before you begin

As a prerequisite, complete the configuration documented in OpenSSL Setup.

Once OpenSSL setup is complete, ensure that a recent version of Apache is installed:

sudo apt-get update
sudo apt-get install apache2

Configuration

Create a Cloud KMS-hosted signing key

Create a Cloud KMS EC-P256-SHA256 signing key in your Google Cloud project, in the key ring that you previously configured for OpenSSL:

gcloud kms keys create "KEY_NAME" --keyring "KEY_RING" \
  --project "PROJECT_ID" --location "LOCATION" \
  --purpose "asymmetric-signing" --default-algorithm "ec-sign-p256-sha256" \
  --protection-level "hsm"

Create a self-signed certificate with OpenSSL

Generate a self-signed certificate with the Cloud KMS-hosted signing key. You can use OpenSSL to use a PKCS #11 URI instead of a file path and identify the key by its label. In the Cloud KMS PKCS #11 library, the key label is the CryptoKey name.

openssl req -new -x509 -days 3650 -subj '/CN=CERTIFICATE_NAME/' \
  DIGEST_FLAG -engine pkcs11 -keyform engine \
  -key PKCS_KEY_TYPE=KEY_IDENTIFIER > PATH_TO_CERTIFICATE

Replace the following:

  • CERTIFICATE_NAME: a name for the certificate.
  • DIGEST_FLAG: the digest algorithm used by the asymmetric signing key. Use -sha256, -sha384, or -sha512 depending on the key.
  • PKCS_KEY_TYPE: the type of identifier used to identify the key. To use the latest key version, use pkcs11:object with the key's name. To use a specific key version, use pkcs11:id with the full resource ID of the key version.
  • KEY_IDENTIFIER: an identifier for the key. If you're using pkcs11:object, use the key's name—for example, KEY_NAME. If you're using pkcs11:id, use the full resource ID of the key or key version—for example, projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY_NAME/cryptoKeyVersions/KEY_VERSION.
  • PATH_TO_CERTIFICATE: the path where you want to save the certificate file.

If this command fails, PKCS11_MODULE_PATH might have been set incorrectly, or you might not have the right permissions to use the Cloud KMS signing key.

You should now have a certificate that looks like this:

-----BEGIN CERTIFICATE-----
...
...
...
-----END CERTIFICATE-----

Set up the Apache server

  1. Create a directory in /etc/apache2 to store your self-signed certificate in:

    sudo mkdir /etc/apache2/ssl
    sudo mv ca.cert /etc/apache2/ssl
    
  2. Edit the 000-default.conf virtual host configuration files located in /etc/apache2/sites-available to provide the certificate file path and ensure that the SSLEngine is on.

    Here is a sample configuration listening on port 443:

      <VirtualHost *:443>
            ServerAdmin webmaster@localhost
            DocumentRoot /var/www/html
            ErrorLog ${APACHE_LOG_DIR}/error.log
            CustomLog ${APACHE_LOG_DIR}/access.log combined
            SSLEngine on
            SSLCertificateFile /etc/apache2/ssl/ca.cert
            SSLCertificateKeyFile "PKCS_KEY_TYPE=KEY_IDENTIFIER"
      </VirtualHost>
    
  3. Ensure Apache exports the environment variables correctly by adding them to the /etc/apache2/envvars file using your text editor of choice. You might need to edit the file as root using sudo. Add the following lines to the end of the file:

    export PKCS11_MODULE_PATH="<var>PATH_TO_LIBKMSP11</var>"
    export KMS_PKCS11_CONFIG="<var>PATH_TO_PKCS11_CONFIG</var>"
    export GRPC_ENABLE_FORK_SUPPORT=1
    

    Replace the following:

    • PATH_TO_LIBKMSP11: the path to libkmsp11.so.
    • PATH_TO_PKCS11_CONFIG: the path to pkcs11-config.yaml.

    GRPC_ENABLE_FORK_SUPPORT is needed for gRPC to include fork support and correctly run the Cloud KMS PKCS #11 library as part of the Apache server.

    If you want to authenticate using a service account key, you must also export a value for the GOOGLE_APPLICATION_CREDENTIALS environment variable.

Run your server

Enable the Apache SSL module, enable the virtualhost configuration, and add a test web page in your DocumentRoot folder:

sudo a2enmod ssl
sudo a2ensite 000-default.conf
echo '<!doctype html><html><body><h1>Hello World!</h1></body></html>' | \
  sudo tee /var/www/html/index.html

Restart your Apache server and test with curl that the configuration works as expected. The --insecure flag is needed to ignore self-signed certificate checks.

sudo systemctl restart apache2
curl -v --insecure https://127.0.0.1

If you encounter any errors, the Apache error log is a good starting place to see what went wrong. Authentication issues are a common source of errors. If you see PERMISSION_DENIED errors, make sure that you are fully authenticated and that the credentials file has the right permissions. To make sure you are fully authenticated, run the following command:

gcloud auth application-default login

To confirm that authentication was successful, the output should include the line Credentials saved to file: [/path/to/credentials.json].