Verify an image with Notation using GitHub Actions

In this article, you'll learn how to create a GitHub Actions workflow to achieve the following goals:

  1. Verify an image's signatures with Notation trust store and trust policy.

Prerequisites

  • You've created an Azure Key Vault (AKV) and a self-signed signing key and certificate. To learn how to create these resources for testing purposes, see Create a self-signed certificate in AKV.
  • You've created an Azure Container Registry (ACR).
  • You have a GitHub repository to store the sample workflow file and GitHub Secrets.

Authenticate from Azure to GitHub

Follow the instructions in the Authenticate from Azure to GitHub section in order to connect GitHub Actions to your ACR and AKV.

Create the GitHub Actions workflow

Once you've configured authentication, you're ready to create the GitHub Actions workflow.

  1. Create a .github/workflows directory in your repository on GitHub if this directory does not already exist.

  2. In the .github/workflows directory, create a file named <your-workflow>.yml, filling in your own name for the placeholder.

  3. Copy the verify template workflow from the collapsed section below into your own <your-workflow>.yml file.

Click here to see the verify workflow template.
# setup notation and verify an OCI artifact stored in ACR
name: notation-github-actions-verify-template

on:
  push:

env:
  ACR_REGISTRY_NAME: <registry_name_of_your_ACR>                       # example: myRegistry.azurecr.io
  ACR_REPO_NAME: <repository_name_of_your_ACR>                         # example: myRepo
  target_artifact_reference: <ACR_REGISTRY_NAME/ACR_REPO_NAME@digest>  # example: myRegistry.azurecr.io/myRepo@sha256:abcdef
  NOTATION_EXPERIMENTAL: 1                                             # [Optional] when set, can use Referrers API in the workflow

jobs:
  notation-verify:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      # Log in to Azure with your service principal secret
      - name: Azure login
        uses: Azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}
      # If you are using OIDC and federated credential, make sure to replace the above step with below:
      # - name: Azure login
      #   uses: Azure/login@v1
      #   with:
      #     client-id: ${{ secrets.AZURE_CLIENT_ID }}
      #     tenant-id: ${{ secrets.AZURE_TENANT_ID }}
      #     subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

      # Log in to your ACR registry
      - name: ACR login
        run: |
            az acr login --name ${{ env.ACR_REGISTRY_NAME }}

      # Install Notation CLI, the default version is "1.1.0"
      - name: setup notation
        uses: notaryproject/notation-action/setup@v1
      
      # Verify the OCI artifact
      - name: verify OCI artifact
        uses: notaryproject/notation-action/verify@v1
        with:
          target_artifact_reference: ${{ env.target_artifact_reference }}
          trust_policy: .github/trustpolicy/trustpolicy.json
          trust_store: .github/truststore
          allow_referrers_api: 'true'
  1. Finally, update the environment variables based on your own environment and your chosen authentication method by following the comments in the template. Save and commit the file to your repository.

Important

.github/trustpolicy/trustpolicy.json must follow the Notation trust policy specs. .github/truststore must follow the Notation trust store specs. For example:

.github/truststore
└── x509
    ├── ca
    │   └── <my_trust_store1>
    │       ├── <my_certificate1>
    │       └── <my_certificate2>
    └── signingAuthority
        └── <my_trust_store2>
            ├── <my_certificate3>
            └── <my_certificate4>

Trigger the GitHub Actions workflow

  1. The trigger logic in the sample workflow is set to the on: push event. Committing the workflow file to a branch in your repository triggers the push event and runs your workflow.

  2. On success, you'll be able to see the image's signatures have been verified. Your output will look similar to the following:

Run notaryproject/notation-action/verify@v1
/home/runner/work/_temp/3d9e3a7c-3cb0-459f-81c4-36b3e0c95f8b/notation policy import --force .github/trustpolicy/trustpolicy.json
Warning: existing trust policy configuration file will be overwritten
Trust policy configuration imported successfully.
/home/runner/work/_temp/3d9e3a7c-3cb0-459f-81c4-36b3e0c95f8b/notation policy show
***
    "version": "1.0",
    "trustPolicies": [
        ***
            "name": "wabbit-networks-images",
            "registryScopes": [ "*" ],
            "signatureVerification": ***
              "level" : "audit" 
            ***,
            "trustStores": ["ca:acme-rockets"],
            "trustedIdentities": [
              "x509.subject: C=US, ST=WA, L=Seattle, O=acme-rockets.io, OU=Finance, CN=SecureBuilder",
        "x509.subject: CN=wabbit-networks.io,O=Notation,L=Seattle,ST=WA,C=US"
            ]
        ***
    ]
***
/home/runner/work/_temp/3d9e3a7c-3cb0-459f-81c4-36b3e0c95f8b/notation cert add -t ca -s acme-rockets .github/truststore/x509/ca/acme-rockets/cert2.pem
Successfully added following certificates to named store acme-rockets of type ca:
.github/truststore/x509/ca/acme-rockets/cert2.pem
/home/runner/work/_temp/3d9e3a7c-3cb0-459f-81c4-36b3e0c95f8b/notation cert ls
STORE TYPE   STORE NAME     CERTIFICATE   
ca           acme-rockets   cert2.pem     
/home/runner/work/_temp/3d9e3a7c-3cb0-459f-81c4-36b3e0c95f8b/notation verify -v --allow-referrers-api ******
Warning: This feature is experimental and may not be fully tested or completed and may be deprecated. Report any issues to "https://github/notaryproject/notation"
level=info msg="Trying to use the referrers API"
level=info msg="Reference sha256:0e3642f0659bed3d5264e8445f38392ab9952b86730204ab9a19d1c5559e6b6e resolved to manifest descriptor: ***MediaType:application/vnd.docker.distribution.manifest.v2+json Digest:sha256:0e3642f0659bed3d5264e8445f38392ab9952b86730204ab9a19d1c5559e6b6e Size:2004 URLs:[] Annotations:map[] Data:[] Platform:<nil> ArtifactType:***"
level=info msg="Checking whether signature verification should be skipped or not"
level=info msg="Trust policy configuration: &***Name:wabbit-networks-images RegistryScopes:[*] SignatureVerification:***VerificationLevel:audit Override:map[]*** TrustStores:[ca:acme-rockets] TrustedIdentities:[x509.subject: C=US, ST=WA, L=Seattle, O=acme-rockets.io, OU=Finance, CN=SecureBuilder x509.subject: CN=wabbit-networks.io,O=Notation,L=Seattle,ST=WA,C=US]***"
level=info msg="Check over. Trust policy is not configured to skip signature verification"
level=info msg="Processing signature with manifest mediaType: application/vnd.oci.image.manifest.v1+json and digest: sha256:6a038e955464b882cd964954687152f6f115b2c0e77ac328b0bbdaf8ce869e61"
level=info msg="Trust policy configuration: &***Name:wabbit-networks-images RegistryScopes:[*] SignatureVerification:***VerificationLevel:audit Override:map[]*** TrustStores:[ca:acme-rockets] TrustedIdentities:[x509.subject: C=US, ST=WA, L=Seattle, O=acme-rockets.io, OU=Finance, CN=SecureBuilder x509.subject: CN=wabbit-networks.io,O=Notation,L=Seattle,ST=WA,C=US]***"
Successfully verified signature for ******
  1. Alternatively, you can configure your workflow file to trigger when a new tag is pushed to the Github repository. See Triggering a workflow for more details. This is a common practice that's done in order to secure a software release process while using GitHub Actions.

View your GitHub Actions workflow results

Under your GitHub repository name, click the Actions tab of your GitHub repository to see the workflow logs.