Data Security Manager with Docker Notary for PKCS#11

1.0 Introduction

This article describes the steps to integrate the Docker Notary client with the Fortanix PKCS#11 library to use Fortanix-Data-Security-Manager (DSM) as a signer. The Fortanix DSM stores the root key, which is an EC key pair in the case of Notary.

1.1 Prerequisites

Ensure the following:

  • The system should have an open SSL with PKCS#11 enabled engine.

  • The PKCS#11 tool must be installed.

    • Run the following command to update the PKCS#11 tool:

      apt update opensc 
    • Run the following command to install the PKCS#11 tool:

      apt install opensc 
    • Run the following command to update the PKCS#11 tool, if using yum as package manager:

      yum update opensc 
    • Run the following command to install the PKCS#11 tool, if using yum as package manager:

      yum install opensc
  • Ensure that you have created an application and a group in the Fortanix DSM. Refer to User's Guide: Getting Started with Fortanix Data Security Manager – UI to know the steps.

2.0 Create OpenSSL Configuration File

Create an OpenSSL configuration file at /root/.docker/certs/openssl.cnf directory with the following content:

openssl_conf = openssl_def
[openssl_def]
engines = engine_section
[req]
distinguished_name = req_distinguished_name
[req_distinguished_name]
#empty.
[engine_section]
pkcs11 = pkcs11_section
[pkcs11_section]
engine_id = pkcs11
dynamic_path = /usr/lib/engines/engine_pkcs11.so
MODULE_PATH = /opt/fortanix/fortanix_pkcs11.so
PIN = file:///etc/fortanix/pkcs11.conf
init = 0

3.0 Create SymLink to fortanix PKCS#11 Library

Create a symlink for Fortanix PKCS#11 library to Notary at the required location. Run the following command:

sudo ln -s fortanix_pkcs11.so /usr/lib/libykcs11.so 
sudo ln -s fortanix_pkcs11.so /usr/lib64/libykcs11.so

4.0 Create Notary Root Key

The Docker Notary root key and certificate are created using the PKCS#11 tool and library and these are stored in the Fortanix DSM. These keys are never exported out of Fortanix DSM and are used as the default root of trust for all the Docker repositories.

Perform the following steps:

  1. Create a PKCS#11 configuration file with the following content at the default location, /etc/fortanix/pkcs11.conf:

    api_key = "<API Key>"
    api_endpoint = "https://<FORTANIX_DSM_URL>"
    [log]
    system = true
    file = "/path/to/log/file"

    Where,

    • The value for api_key is the copied API key from Fortanix DSM.

    • The value for api_endpoint is the Fortanix DSM cluster endpoint.

    NOTE

    You can also use FORTANIX_PKCS11_CONFIG_PATH environment variable to set a custom configuration path.

  2. Run the following command to create the root key:

    $ pkcs11-tool --module /opt/fortanix/fortanix_pkcs11.so --login --pin file:///etc/fortanix/pkcs11.conf -k --label "Notary Root Key" --key-type
    
     EC:prime256v1
    Using slot 0 with a present token (0x0)
     Key pair generated:
     Private Key Object; EC
     label: test EC Key
     ID: 6866774f524a436c7a2f72614f6a374c394e7063376a664c4c303d
     Usage: sign, derive
    Public Key Object; EC EC_POINT 256 bits
     EC_POINT: 
    04410406ca6ddfafb9c3...f0a944bbcdbb3d746a315b08853ae1bc50416fa93c98fae68cb
    d0
     EC_PARAMS: 06082a8648ce3d030107
     label: test EC Key
     ID: 6866774f524a436c7a2f72614f6a374c394e7063376a664c4c303d
     Usage: verify

    NOTE

    Note down the value for Private Key Object ID in the output above.

  3. Run the following command to create the self -signed certificate for the root key:

    $ openssl req -config /root/.docker/certs/openssl.cnf -engine pkcs11 -
    keyform engine -new -key 1:<PRIV-KEY-ID> -nodes -days 365 -x509 -sha256 -
    out /root/.docker/certs/root.crt -subj "/CN=root"
    Import the certificate into Fortanix DSM:
    $ openssl x509 -inform pem -outform der -in /root/.docker/certs/root.crt -
    out /root/.docker/certs/root-crt.der
    $ pkcs11-tool --module /opt/fortanix/fortanix_pkcs11.so --login --pin
    file:///etc/fortanix/pkcs11.conf --write-object /root/.docker/certs/root-
    crt.der --type cert --id <PRIV_KEY-ID> --label "Notary Root Key"

    For more information on the configuration file, refer to the Configuration File Format.

    NOTE

    Ensure to enter the value for subject as CN=root for Notary to identify the self-signed certificate created from the root key.

  4. Run the following command to upload the certificate to Fortanix DSM:

    $ pkcs11-tool --module /opt/fortanix/fortanix_pkcs11.so --login –pin file:///etc/fortanix/pkcs11.conf --write-object /root/.docker/certs/root-crt.der --type cert --id  --label "Notary Root Key"

5.0 Publish Root Key and Certificate

Perform the following steps to publish the root key and certificate using the Fortanix DSM UI:

  1. Log in to the Fortanix DSM UI.

  2. Navigate to Security Objects tab and search for the key or certificate that you want to publish. Ensure to note the name of the key or certificate that you are publishing.

  3. On the detailed view page, click the Public key published toggle button to publish the key.
    This action might require approval from the approvers, refer to Section: Quorum Approval Policy.
    Selecting this toggle button, displays the URL of the API endpoint. You can copy this endpoint using the copy icon.
    In case you want to view the older version of the key or certificate, select the check box for List previous version and click the Save button.

  4. Run the following command to list all the security objects in the Fortanix DSM along with their CKA_ID value:

    pkcs11-tool --module ~/path/to/pkcs11/library.so --login --pin <API_KEY> -O

    The output of this command lists all the objects in the account, along with all published root keys and certificates. Note the CKA_ID value mentioned against the published keys and certificates.

    NOTE

    Ensure that the CKA_IDs is in HEX string format only.

  5. Add information about the root keys and certificates in the PKCS#11 configuration file in the following format:

    api_key = "..."api_key = "..."
    api_endpoint = "..."
    [log]
    system = true
    file = "/path/to/log/file"
    [[pub_obj_info]]
    account_id = "<DSM_ACCOUNT_ID>"
    key_name = "<PUBLIC_KEY_NAME>
    pkcs11_id = "<CKA_ID>"
    [[pub_obj_info]]
    account_id = "<DSM_ACCOUNT_ID>"
    key_name = "<CERTIFICATE_NAME>"
    pkcs11_id = "<CKA_ID>"

    NOTE

    You can find the values for the following:

    • account_id - this value is available in the Settings tab → Account Settings page.

    • key_name - this value is copied in Step 2.

    • pkcs11_id – this value is copied while creating the root key in Step 4.

6.0 Verification

Run the following command to list all the Docker Notary keys:

notary key list

The following is the sample output of the above command:

ROLE GUN KEY ID LOCATION

  root e38202b6664ad57...5579707260b1b yubikey

NOTE

The location of the key is always displayed as yubikey in the CLI, however it is also reflected in the Fortanix DSM.

After the integration operation is successfully done, you can run the Docker trust command and Notary operations using the keys stored in Fortanix DSM.

7.0 Sign the Image with Notary

Perform the following steps to sign the image with Notary:

  1. Run the following command to export the PKCS#11 configuration file path:

    export FORTANIX_PKCS11_CONFIG_PATH=/home/ubuntu/pkcs11.cnf
  2. Run the following command to create the certificate for the repository:

    openssl req -engine pkcs11 -keyform engine -new -key 1:<PRIV-KEY-ID> -nodes -sha256 -out <path to repo csr file> -subj "/CN=<docker repo>”

    NOTE

    If you encounter any error, contact the Fortanix Support team for troubleshooting steps.

  3. Run the following command to create local key pair:

    openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout <path to ca.key> -out <path to ca.pem>
  4. Run the following command to sign the Certificate Signing Request (CSR):

    openssl x509 -req -in <path to repo csr file> -CA <path to ca.pem> -CAkey <path to ca.key> -CAcreateserial -out <path to repo.pem> -days 375 -sha256
  5. Run the following command to initialize the repository in Notary:

    notary -c ~/.notary/config.json -D init <docker repo> -s https://notary.docker.io --publish --rootcert <path to repo.pem>
  6. Run the following command to push the snapshot key to Notary servers:

    notary -s https://notary.docker.io -d ~/.docker/trust key rotate <docker repo> snapshot --server-managed
  7. Run the following commands to get the size and bash:

    docker manifest inspect <docker repo> -v | jq .Descriptor.size
    docker manifest inspect <docker repo> -v | jq .Descriptor.digest
  8. Run the following command to create the signing key:

    docker trust key generate <signing key>

    NOTE

    This command generates a key pair. The path to the public key file is mentioned at the end of the output. Note this public key path to use in the next step for <path to the signing key>.

  9. Run the following command to add the delegation role:

    notary -s https://notary.docker.io -d ~/.docker/trust delegation add -p <docker repo> targets/<delegation role> --all-paths <path to the signing key>
  10. Run the following command to sign the image:

    notary -c ~/.notary/config.json -D addhash -p "<docker repo>" <tagname> <size> --sha256 <hash> -r targets/<delegation role>
  11. Now this repository can be used when the docker content trust is enabled. Run the following command to set the value for DOCKER_CONTENT_TRUST environment variable to 1.

    export DOCKER_CONTENT_TRUST=1