|
| 1 | +--- |
| 2 | +title: Sign container images with Notation and Azure Key vault using a CA-issued certificate (Preview) |
| 3 | +description: In this tutorial learn to create a CA-issued certificate in Azure Key Vault, build and sign a container image stored in Azure Container Registry (ACR) with notation and AKV, and then verify the container image using notation. |
| 4 | +author: yizha1 |
| 5 | +ms.author: yizha1 |
| 6 | +ms.service: container-registry |
| 7 | +ms.custom: devx-track-azurecli |
| 8 | +ms.topic: how-to |
| 9 | +ms.date: 6/9/2023 |
| 10 | +--- |
| 11 | + |
| 12 | +# Sign container images with Notation and Azure Key Vault using a CA-issued certificate (Preview) |
| 13 | + |
| 14 | +Signing and verifying the container images with a certificate issued by a trusted Certificate Authority (CA) ensure authorizing and validating the identity responsibly with approved CA without any compromise. The trusted CA entities (for example, GlobalSign or DigiCert) ensure to validate the user's and organization's identity and also have the authority to revoke the certificate immediately upon any risk or misuse. |
| 15 | + |
| 16 | +[Notation](https://github.com/notaryproject/notation) is an open source supply chain tool developed by [Notary Project](https://notaryproject.dev/), which supports signing and verifying container images and other artifacts. The Azure Key Vault (AKV) is used to store a certificate with a signing key that can be utilized by Notation with the [Notation AKV plugin (azure-kv)](https://github.com/Azure/notation-azure-kv) to sign and verify container images and other artifacts. The Azure Container Registry (ACR) allows you to attach these signatures to the signed image. |
| 17 | + |
| 18 | +> [!IMPORTANT] |
| 19 | +> This feature is currently in preview. Previews are made available to you on the condition that you agree to the [supplemental terms of use][terms-of-use]. Some aspects of this feature may change prior to general availability (GA). |
| 20 | +
|
| 21 | +In this article: |
| 22 | + |
| 23 | +> [!div class="checklist"] |
| 24 | +> * Install the notation CLI and AKV plugin |
| 25 | +> * Create or import a certificate issued by a CA in AKV |
| 26 | +> * Build and push a container image with ACR task |
| 27 | +> * Sign a container image with Notation CLI and AKV plugin |
| 28 | +> * Verify a container image signature with Notation CLI |
| 29 | +
|
| 30 | +## Prerequisites |
| 31 | + |
| 32 | +> * Create or use an [Azure Container Registry](../container-registry/container-registry-get-started-azure-cli.md) for storing container images and signatures |
| 33 | +> * Create or use an [Azure Key Vault.](../key-vault/general/quick-create-cli.md) |
| 34 | +> * Install and configure the latest [Azure CLI](/cli/azure/install-azure-cli), or run commands in the [Azure Cloud Shell](https://portal.azure.com/#cloudshell/) |
| 35 | +
|
| 36 | +> [!NOTE] |
| 37 | +> We recommend creating a new Azure Key Vault for storing certificates only. |
| 38 | +
|
| 39 | +## Install the notation CLI and AKV plugin |
| 40 | + |
| 41 | +1. Install `Notation v1.0.0` on a Linux amd64 environment. Follow the [Notation installation guide](https://notaryproject.dev/docs/user-guides/installation/) to download the package for other environments. |
| 42 | + |
| 43 | + ```bash |
| 44 | + # Download, extract and install |
| 45 | + curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v1.0.0/notation_1.0.0_linux_amd64.tar.gz |
| 46 | + tar xvzf notation.tar.gz |
| 47 | + |
| 48 | + # Copy the notation cli to the desired bin directory in your PATH, for example |
| 49 | + cp ./notation /usr/local/bin |
| 50 | + ``` |
| 51 | + |
| 52 | +2. Install the notation Azure Key Vault plugin on a Linux environment for remote signing. You can also download the package for other environments by following the [Notation AKV plugin installation guide](https://github.com/Azure/notation-azure-kv#installation-the-akv-plugin). |
| 53 | + |
| 54 | + ```bash |
| 55 | + # Create a directory for the plugin |
| 56 | + mkdir -p ~/.config/notation/plugins/azure-kv |
| 57 | + |
| 58 | + # Download the plugin |
| 59 | + curl -Lo notation-azure-kv.tar.gz https://github.com/Azure/notation-azure-kv/releases/download/v1.0.1/notation-azure-kv_1.0.1_linux_amd64.tar.gz |
| 60 | + |
| 61 | + # Extract to the plugin directory |
| 62 | + tar xvzf notation-azure-kv.tar.gz -C ~/.config/notation/plugins/azure-kv |
| 63 | + ``` |
| 64 | + |
| 65 | +> [!NOTE] |
| 66 | +> The plugin directory varies depending upon the operating system in use. The directory path assumes Ubuntu. For more information, see [Notation directory structure for system configuration.](https://notaryproject.dev/docs/user-guides/how-to/notary-project-concepts/) |
| 67 | + |
| 68 | +3. List the available plugins. |
| 69 | + |
| 70 | + ```bash |
| 71 | + notation plugin ls |
| 72 | + ``` |
| 73 | + |
| 74 | +## Configure environment variables |
| 75 | + |
| 76 | +> [!NOTE] |
| 77 | +> We recommend to provide values for the Azure resources to match the existing AKV and ACR resources for easy execution of commands in the tutorial. |
| 78 | + |
| 79 | +1. Configure AKV resource names. |
| 80 | + |
| 81 | + ```bash |
| 82 | + # Name of the existing Azure Key Vault used to store the signing keys |
| 83 | + AKV_NAME=myakv |
| 84 | + |
| 85 | + # Name of the certificate created or imported in AKV |
| 86 | + CERT_NAME=wabbit-networks-io |
| 87 | + |
| 88 | + # X.509 certificate subject |
| 89 | + CERT_SUBJECT="CN=wabbit-networks.io,O=Notation,L=Seattle,ST=WA,C=US" |
| 90 | + ``` |
| 91 | + |
| 92 | +2. Configure ACR and image resource names. |
| 93 | + |
| 94 | + ```bash |
| 95 | + # Name of the existing registry example: myregistry.azurecr.io |
| 96 | + ACR_NAME=myregistry |
| 97 | + # Existing full domain of the ACR |
| 98 | + REGISTRY=$ACR_NAME.azurecr.io |
| 99 | + # Container name inside ACR where image will be stored |
| 100 | + REPO=net-monitor |
| 101 | + TAG=v1 |
| 102 | + # Source code directory containing Dockerfile to build |
| 103 | + IMAGE_SOURCE=https://github.com/wabbit-networks/net-monitor.git#main |
| 104 | + ``` |
| 105 | + |
| 106 | +## Sign in with Azure CLI |
| 107 | + |
| 108 | +```bash |
| 109 | +az login |
| 110 | +``` |
| 111 | + |
| 112 | +To learn more about Azure CLI and how to sign in with it, see [Sign in with Azure CLI](/cli/azure/authenticate-azure-cli). |
| 113 | + |
| 114 | + |
| 115 | +## Create or import a certificate issued by a CA in AKV |
| 116 | + |
| 117 | +### Certificate requirements |
| 118 | + |
| 119 | +When creating certificates for signing and verification, it is important to ensure that they meet the [Notary Project certificate requirement](https://github.com/notaryproject/specifications/blob/v1.0.0/specs/signature-specification.md#certificate-requirements). |
| 120 | + |
| 121 | +The requirements for root and intermediate certificates are as follows: |
| 122 | +- The `basicConstraints` extension MUST be present and MUST be marked as critical. The `cA` field MUST be set `true`. |
| 123 | +- The `keyUsage` extension MUST be present and MUST be marked `critical`. Bit positions for `keyCertSign` MUST be set. |
| 124 | + |
| 125 | +The requirements for certificates issued by a CA are as follows: |
| 126 | +- X.509 certificate properties: |
| 127 | + - Subject MUST contain common name (`CN`), country (`C`), state or province (`ST`), and organization (`O`). In this tutorial, `$CERT_SUBJECT` is used as the subject. |
| 128 | + - X.509 key usage flag must be `DigitalSignature` only. |
| 129 | + - Extended Key Usages (EKUs) must be empty or `1.3.6.1.5.5.7.3.3` (for Codesigning). |
| 130 | +- Key properties: |
| 131 | + - The property "exportable" should be set to `false` |
| 132 | + - Select [supported key type and size](https://github.com/notaryproject/specifications/blob/v1.0.0/specs/signature-specification.md#algorithm-selection) |
| 133 | + |
| 134 | +> [!NOTE] |
| 135 | +> Version v1.0.0 of the Notation AKV plugin requires a specific certificate order in a certificate chain. However, this limitation has been removed in version v1.0.1. We recommend using the latest version of the plugin. |
| 136 | + |
| 137 | +### Create a certificate issued by a CA |
| 138 | + |
| 139 | +To create a certificate issued by a CA, follow these steps: |
| 140 | + |
| 141 | +1. Create a certificate signing request (CSR) by following the instructions in [create certificate signing request](../key-vault/certificates/create-certificate-signing-request.md). |
| 142 | +2. When merging the CSR, make sure you merge the entire chain that brought back from the CA vendor. |
| 143 | + |
| 144 | +### Import a certificate in AKV |
| 145 | + |
| 146 | +To import a certificate, follow these steps: |
| 147 | + |
| 148 | +1. Get the certificate file from CA vendor with entire certificate chain. |
| 149 | +2. Import the certificate into Azure Key Vault by following the instructions in [import a certificate](../key-vault/certificates/tutorial-import-certificate.md). |
| 150 | + |
| 151 | +> [!NOTE] |
| 152 | +> If the certificate does not contain a certificate chain after creation or importing, you can obtain the intermediate and root certificates from your CA vendor. You can ask your vendor to provide you with a PEM file that contains the intermediate certificates (if any) and root certificate. This file can then be used at step 5 of [signing container images](#sign-a-container-image-with-notation-cli-and-akv-plugin). |
| 153 | + |
| 154 | +## Sign a container image with Notation CLI and AKV plugin |
| 155 | + |
| 156 | +1. Authenticate to your ACR by using your individual Azure identity. |
| 157 | + |
| 158 | + ```bash |
| 159 | + az acr login --name $ACR_NAME |
| 160 | + ``` |
| 161 | + |
| 162 | +> [!IMPORTANT] |
| 163 | +> If you have Docker installed on your system and used `az acr login` or `docker login` to authenticate to your ACR, your credentials are already stored and available to notation. In this case, you don’t need to run `notation login` again to authenticate to your ACR. To learn more about authentication options for notation, see [Authenticate with OCI-compliant registries](https://notaryproject.dev/docs/user-guides/how-to/registry-authentication/). |
| 164 | + |
| 165 | +2. Build and push a new image with ACR Tasks. Always use `digest` to identify the image for signing, because tags are mutable and and can be overwritten. |
| 166 | + |
| 167 | + ```bash |
| 168 | + DIGEST=$(az acr build -r $ACR_NAME -t $REGISTRY/${REPO}:$TAG $IMAGE_SOURCE --no-logs --query "outputImages[0].digest" -o tsv) |
| 169 | + IMAGE=$REGISTRY/${REPO}@$DIGEST |
| 170 | + ``` |
| 171 | + |
| 172 | +3. Assign access policy in AKV (Azure CLI) |
| 173 | + |
| 174 | + To sign a container image with a certificate in AKV, a principal must have authorized access to AKV. The principal can be a user principal, service principal, or managed identity. In this tutorial, we assign access policy to a signed-in user. To learn more about assigning policy to a principal, see [Assign Access Policy](/azure/key-vault/general/assign-access-policy). |
| 175 | + |
| 176 | + To set the subscription that contains the AKV resources, run the following command: |
| 177 | + |
| 178 | + ```bash |
| 179 | + az account set --subscription <your_subscription_id> |
| 180 | + ``` |
| 181 | + |
| 182 | + If the certificate contains the entire certificate chain, the principal must be granted key permission `Sign`, secret permission `Get`, and certificate permissions `Get`. To grant these permissions to the principal, run the following command: |
| 183 | + |
| 184 | + ```bash |
| 185 | + USER_ID=$(az ad signed-in-user show --query id -o tsv) |
| 186 | + az keyvault set-policy -n $AKV_NAME --key-permissions sign --secret-permissions get --certificate-permissions get --object-id $USER_ID |
| 187 | + ``` |
| 188 | + |
| 189 | + If the certificate doesn't contain the chain, the principal must be granted key permission `Sign`, and certificate permissions `Get`. To grant these permissions to the principal, run the following command: |
| 190 | + |
| 191 | + ```bash |
| 192 | + USER_ID=$(az ad signed-in-user show --query id -o tsv) |
| 193 | + az keyvault set-policy -n $AKV_NAME --key-permissions sign --certificate-permissions get --object-id $USER_ID |
| 194 | + ``` |
| 195 | +
|
| 196 | +4. Get the Key ID for a certificate, assuming the certificate name is $CERT_NAME. A certificate in AKV can have multiple versions, the following command get the Key Id for the latest version. |
| 197 | +
|
| 198 | + ```bash |
| 199 | + KEY_ID=$(az keyvault certificate show -n $CERT_NAME --vault-name $AKV_NAME --query 'kid' -o tsv) |
| 200 | + ``` |
| 201 | +
|
| 202 | +5. Sign the container image with the COSE signature format using the key ID. |
| 203 | +
|
| 204 | + If the certificate contains the entire certificate chain, run the following command: |
| 205 | +
|
| 206 | + ```bash |
| 207 | + notation sign --signature-format cose $IMAGE --id $KEY_ID --plugin azure-kv |
| 208 | + ``` |
| 209 | +
|
| 210 | + If the certificate does not contain the chain, you need to use an additional plugin parameter `--plugin-config ca_certs=<ca_bundle_file>` to pass the CA certificates in a PEM file to AKV plugin, run the following command: |
| 211 | +
|
| 212 | + ```bash |
| 213 | + notation sign --signature-format cose $IMAGE --id $KEY_ID --plugin azure-kv --plugin-config ca_certs=<ca_bundle_file> |
| 214 | + ``` |
| 215 | +
|
| 216 | +6. View the graph of signed images and associated signatures. |
| 217 | +
|
| 218 | + ```bash |
| 219 | + notation ls $IMAGE |
| 220 | + ``` |
| 221 | +
|
| 222 | +## Verify a container image with Notation CLI |
| 223 | +
|
| 224 | +1. Add root certificate to a named trust store for signature verification. Users need to obtain the root certificate from the CA vendor, and assuming the root certificate file is stored in $ROOT_CERT. |
| 225 | +
|
| 226 | + ```bash |
| 227 | + STORE_TYPE="ca" |
| 228 | + STORE_NAME="wabbit-networks.io" |
| 229 | + notation cert add --type $STORE_TYPE --store $STORE_NAME $ROOT_CERT |
| 230 | + ``` |
| 231 | +
|
| 232 | +2. List the root certificate to confirm. |
| 233 | +
|
| 234 | + ```bash |
| 235 | + notation cert ls |
| 236 | + ``` |
| 237 | +
|
| 238 | +3. Configure trust policy before verification. |
| 239 | +
|
| 240 | + Trust policies allow users to specify fine-tuned verification policies. Use the following command to configure trust policy. Upon successful execution of the command, one trust policy named `wabbit-networks-images` is created. This trust policy applies to all the artifacts stored in repositories defined in `$REGISTRY/$REPO`. Assuming that the user trusts a specific identity with the X.509 subject `$CERT_SUBJECT`, which is used for the signing certificate. The named trust store `$STORE_NAME` of type `$STORE_TYPE` contains the root certificates. See [Trust store and trust policy specification](https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/trust-store-trust-policy.md) for details. |
| 241 | +
|
| 242 | + ```bash |
| 243 | + cat <<EOF > ./trustpolicy.json |
| 244 | + { |
| 245 | + "version": "1.0", |
| 246 | + "trustPolicies": [ |
| 247 | + { |
| 248 | + "name": "wabbit-networks-images", |
| 249 | + "registryScopes": [ "$REGISTRY/$REPO" ], |
| 250 | + "signatureVerification": { |
| 251 | + "level" : "strict" |
| 252 | + }, |
| 253 | + "trustStores": [ "$STORE_TYPE:$STORE_NAME" ], |
| 254 | + "trustedIdentities": [ |
| 255 | + "x509.subject: $CERT_SUBJECT" |
| 256 | + ] |
| 257 | + } |
| 258 | + ] |
| 259 | + } |
| 260 | + EOF |
| 261 | + ``` |
| 262 | +
|
| 263 | +4. Use `notation policy` to import the trust policy configuration from a JSON file that we created previously. |
| 264 | +
|
| 265 | + ```bash |
| 266 | + notation policy import ./trustpolicy.json |
| 267 | + notation policy show |
| 268 | + ``` |
| 269 | + |
| 270 | +5. The notation command can also help to ensure the container image hasn't been tampered with since build time by comparing the `sha` with what is in the registry. |
| 271 | + |
| 272 | + ```bash |
| 273 | + notation verify $IMAGE |
| 274 | + ``` |
| 275 | + Upon successful verification of the image using the trust policy, the sha256 digest of the verified image is returned in a successful output message. |
| 276 | + |
| 277 | +## FAQ |
| 278 | + |
| 279 | +- What should I do if the certificate is expired? |
| 280 | + |
| 281 | + If the certificate has expired, it invalidates the signature. To resolve this issue, you should renew the certificate and sign container images again. Learn more about [Renew your Azure Key Vault certificates](../key-vault/certificates/overview-renew-certificate.md). |
| 282 | + |
| 283 | +- What should I do if the root certificate is expired? |
| 284 | + |
| 285 | + If the root certificate has expired, it invalidates the signature. To resolve this issue, you should obtain a new certificate from a trusted CA vendor and sign container images again. Replace the expired root certificate with the new one from the CA vendor. |
| 286 | + |
| 287 | +- What should I do if the certificate is revoked? |
| 288 | + |
| 289 | + If the certificate is revoked, it invalidates the signature. The most common reason for revoking a certificate is when the certificate’s private key has been compromised. To resolve this issue, you should obtain a new certificate from a trusted CA vendor and sign container images again. |
| 290 | + |
| 291 | +## Next steps |
| 292 | + |
| 293 | +See [Ratify on Azure: Allow only signed images to be deployed on AKS with Notation and Ratify](https://github.com/deislabs/ratify/blob/main/docs/quickstarts/ratify-on-azure.md). |
| 294 | + |
| 295 | +[terms-of-use]: https://azure.microsoft.com/support/legal/preview-supplemental-terms/ |
0 commit comments