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-sha512depending on the key.PKCS_KEY_TYPE: the type of identifier used to identify the key. To use the latest key version, usepkcs11:objectwith the key's name. To use a specific key version, usepkcs11:idwith the full resource ID of the key version.KEY_IDENTIFIER: an identifier for the key. If you're usingpkcs11:object, use the key's name—for example,KEY_NAME. If you're usingpkcs11: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
Create a directory in
/etc/apache2to store your self-signed certificate in:sudo mkdir /etc/apache2/ssl sudo mv ca.cert /etc/apache2/sslEdit the
000-default.confvirtual host configuration files located in/etc/apache2/sites-availableto 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>Ensure Apache exports the environment variables correctly by adding them to the
/etc/apache2/envvarsfile using your text editor of choice. You might need to edit the file as root usingsudo. 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=1Replace the following:
PATH_TO_LIBKMSP11: the path tolibkmsp11.so.PATH_TO_PKCS11_CONFIG: the path topkcs11-config.yaml.
GRPC_ENABLE_FORK_SUPPORTis 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_CREDENTIALSenvironment 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].