Creating an image policy CR

A cluster administrator or application developer can use an ImagePolicy custom resource (CR) to configure a sigstore signature verification policy for a specific namespace.

The MCO watches ImagePolicy instances in different namespaces and updates the /etc/crio/policies/<namespace>.json and /etc/containers/registries.d/sigstore-registries.yaml files on all the nodes in the cluster.

Note

If a scoped image or repository in an image policy is nested under one of the scoped images or repositories in a cluster image policy, only the policy from cluster image policy is applied. However, the image policy object is created with an error message. For example, if an image policy specifies example.com/global/image, and the cluster image policy specifies example.com/global, the namespace inherits the policy from the cluster image policy.

The following example shows general guidelines on how to configure an ImagePolicy object. For more details on the parameters, see "About cluster and image policy parameters".

Prerequisites
  • You have a sigstore-supported public key infrastructure (PKI) key, a Bring Your Own Public Key Infrastructure (BYOPKI) certificate, or provide a Cosign public and private key pair for signing operations.

  • You have a signing process in place to sign your images.

  • You have access to a registry that supports Cosign signatures, if you are using Cosign signatures.

Procedure
  1. Create an image policy object similar to the following examples. See "About cluster and image policy parameters" for specific details on these parameters.

    The following example image policy object uses a public key policy and the MatchRepository match policy:

    apiVersion: config.openshift.io/v1
    kind: ImagePolicy
    metadata:
      name: p0
      namespace: mynamespace
    spec:
      scopes:
        - example.io/crio/signed
      policy:
        rootOfTrust:
          policyType: PublicKey
          publicKey:
            keyData: a2V5RGF0YQ==
            rekorKeyData: cmVrb3JLZXlEYXRh
        signedIdentity:
          matchPolicy: MatchRepository

    where:

    kind

    Specifies that the configuration is for a ImagePolicy object.

    metadata.namespace

    Specifies the namespace where the image policy is applied.

    spec.scopes

    Specifies a list of repositories or images assigned to this policy.

    spec.policy

    Specifies the parameters that define how the images are verified.

    spec.policy.rootOfTrust

    Specifies a root of trust for the policy.

    spec.policy.rootOfTrust.policyType

    Specifies the policy types that define the root of trust, either a public key, a BYOPKI certificate, or a Fulcio certificate. Here, a public key with Rekor verification.

    spec.policy.rootOfTrust.publicKey.keyData

    For a public key policy, specifies a base64-encoded public key in the PEM format. The maximum length is 8192 characters.

    spec.policy.rootOfTrust.publicKey.rekorKeyData

    Optional: Specifies a base64-encoded Rekor public key in the PEM format. The maximum length is 8192 characters.

    spec.policy.signedIdentity.matchPolicy

    Optional: Specifies one of the following processes to verify the identity in the signature and the actual image identity:

    • MatchRepoDigestOrExact.

    • MatchRepository.

    • ExactRepository. The exactRepository parameter must be specified.

    • RemapIdentity. The prefix and signedPrefix parameters must be specified.

    The following example image policy object uses a BYOPKI policy and the MatchRepository match policy:

    apiVersion: config.openshift.io/v1alpha1
    kind: ImagePolicy
    metadata:
      name: pki-policy
      namespace: mynamespace
    spec:
      scopes:
      - example.io
      policy:
        rootOfTrust:
          policyType: PKI
          pki:
            caRootsData: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk....RVJUSUZJQ0FURS0tLS0t
            caIntermediatesData: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURkVENDQ....0QT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
            pkiCertificateSubject:
              email: email@example.com
              hostname: myhost.example.com
        signedIdentity:
          matchPolicy: MatchRepository

    where:

    kind

    Specifies that the configuration is for a ImagePolicy object.

    metadata.namespace

    Specifies the namespace where the image policy is applied.

    spec.scopes

    Specifies a list of repositories or images assigned to this policy.

    spec.policy

    Specifies the parameters that define how the images are verified.

    spec.policy.rootOfTrust

    Specifies a root of trust for the policy.

    spec.policy.rootOfTrust.policyType

    Specifies the policy types that define the root of trust, either a public key, a BYOPKI certificate, or a Fulcio certificate. Here, a BYOPKI certificate.

    spec.policy.rootOfTrust.pki

    For a BYOPKI certificate, specifies caRootsData. This parameter specifies a base64-encoded CA root certificate in the PEM format. The maximum length is 8192 characters. Optionally with caIntermediatesData, specifies a base64-encoded intermediate CA root certificate in the PEM format. The maximum length is 8192 characters.

    spec.policy.rootOfTrust.pki.pkiCertificateSubject

    Specifies a subject alternative name (SAN) to authenticate the user’s identity by using a hostname and an email address:

    • email. Specifies the email address specified when the certificate was generated.

    • hostname. Specifies the hostname specified when the certificate was generated.

    spec.policy.signedIdentity.matchPolicy

    For a BYOPKI certificate, specify MatchRepository to verify the identity in the signature and the actual image identity. The default signed identity is matchRepoDigestOrExact, which requires digest specification. The signature in this case was not created for digested image.

    The following example image policy object uses a Fulcio certificate policy and the ExactRepository match policy:

    apiVersion: config.openshift.io/v1
    kind: ImagePolicy
    metadata:
      name: p1
      namespace: mynamespace
    spec:
      scopes:
        - example.io/crio/signed
      policy:
        rootOfTrust:
          policyType: FulcioCAWithRekor
          fulcioCAWithRekor:
            fulcioCAData: a2V5RGF0YQ==
            fulcioSubject:
              oidcIssuer: "https://expected.OIDC.issuer/"
              signedEmail: "expected-signing-user@example.com"
            rekorKeyData: cmVrb3JLZXlEYXRh
        signedIdentity:
          matchPolicy: ExactRepository
          exactRepository:
            repository: quay.io/crio/signed

    where:

    kind

    Specifies that the configuration is for a ImagePolicy object.

    metadata.namespace

    Specifies the namespace where the image policy is applied.

    spec.scopes

    Specifies a list of repositories or images assigned to this policy.

    spec.policy

    Specifies the parameters that define how the images are verified.

    spec.policy.rootOfTrust

    Specifies a root of trust for the policy.

    spec.policy.rootOfTrust.policyType

    Specifies the policy types that define the root of trust, either a public key, a BYOPKI certificate, or a Fulcio certificate. Here, a Fulcio certificate with required Rekor verification.

    spec.policy.rootOfTrust.fulcioCAWithRekor

    For a Fulcio certificate policy, the following parameters are required:

    • fulcioCAData: Specifies a base64-encoded Fulcio certificate in the PEM format. The maximum length is 8192 characters.

    • fulcioSubject: Specifies the OIDC issuer and the email of the Fulcio authentication configuration.

    • rekorKeyData: Specifies a base64-encoded Rekor public key in the PEM format. This parameter is required when the policyType is FulcioCAWithRekor. The maximum length is 8192 characters.

    spec.policy.signedIdentity.matchPolicy

    Optional: Specifies one of the following processes to verify the identity in the signature and the actual image identity:

    • MatchRepoDigestOrExact.

    • MatchRepository.

    • ExactRepository. The exactRepository parameter must be specified.

    • RemapIdentity. The prefix and signedPrefix parameters must be specified.

    spec.policy.signedIdentity.exactRepository.repository

    For the exactRepository match policy, specifies the repository that contains the image identity and signature.

  2. Create the image policy object:

    $ oc create -f <file_name>.yaml

    The Machine Config Operator (MCO) updates the machine config pools (MCP) in your cluster.

Verification
  • After the nodes in your cluster are updated, you can verify that the image policy has been configured:

    1. Start a debug pod for the node by running the following command:

      $ oc debug node/<node_name>
    2. Set /host as the root directory within the debug shell by running the following command:

      sh-5.1# chroot /host/
    3. Examine the <namespace>.json file by running the following command:

      sh-5.1# cat /etc/crio/policies/<namespace>.json
      Example output for the image policy object with a public key showing the new image policy
      # ...
       "transports": {
      # ...
        "docker": {
         "example.io/crio/signed": [
          {
           "type": "sigstoreSigned",
           "keyData": "a2V5RGF0YQ==",
           "rekorPublicKeyData": "cmVrb3JLZXlEYXRh",
           "signedIdentity": {
            "type": "matchRepository",
            "dockerRepository": "example.org/crio/signed"
           }
      # ...
      Example output for the image policy object for a BYOPKI certificate showing the new image policy
      # ...
       "transports": {
      # ...
          "docker": {
            "docker.io": [
              {
                "type": "sigstoreSigned",
                "pki": {
                  "caRootsData": "LS0t...LS0t",
                  "caIntermediatesData": "LS0t...LS0t"
                  "subjectEmail": "email@example.com",
                  "subjectHostname": "myhost.example.com"
                },
                "signedIdentity": {
                  "type": "matchRepository"
                }
              }
            ],
      Example output for the image policy object with a Fulcio certificate showing the new image policy
      # ...
       "transports": {
      # ...
        "docker": {
         "example.io/crio/signed": [
          {
           "type": "sigstoreSigned",
           "fulcio": {
            "caData": "a2V5RGF0YQ==",
            "oidcIssuer": "https://expected.OIDC.issuer/",
            "subjectEmail": "expected-signing-user@example.com"
           },
           "rekorPublicKeyData": "cmVrb3JLZXlEYXRh",
           "signedIdentity": {
            "type": "exactRepository",
            "dockerRepository": "quay.io/crio/signed"
           }
          }
         ],
      # ...
    4. Examine the sigstore-registries.yaml file by running the following command:

      sh-5.1# cat /etc/containers/registries.d/sigstore-registries.yaml
      Example output showing that the scoped registry was added
      docker:
        example.io/crio/signed:
          use-sigstore-attachments: true
        quay.io/openshift-release-dev/ocp-release:
          use-sigstore-attachments: true

      where:

      docker.example.com.use-sigstore-attachments

      When true, specifies that sigstore signatures are going to be read along with the image.

    5. Check the crio log for sigstore signature verification by running the following command:

      sh-5.1#  journalctl -u crio | grep -A 100 "Pulling image: example.io/crio"
      Example output with timestamp removed
      # ...
      msg="IsRunningImageAllowed for image docker:example.io/crio/signed:latest" file="signature/policy_eval.go:274"
      msg="Using transport \"docker\" specific policy section \"example.io/crio/signed\"" file="signature/policy_eval.go:150"
      msg="Reading /var/lib/containers/sigstore/crio/signed@sha256=18b42e8ea347780f35d979a829affa178593a8e31d90644466396e1187a07f3a/signature-1" file="docker/docker_image_src.go:545"
      msg="Looking for Sigstore attachments in quay.io/crio/signed:sha256-18b42e8ea347780f35d979a829affa178593a8e31d90644466396e1187a07f3a.sig" file="docker/docker_client.go:1138"
      msg="GET https://quay.io/v2/crio/signed/manifests/sha256-18b42e8ea347780f35d979a829affa178593a8e31d90644466396e1187a07f3a.sig" file="docker/docker_client.go:617"
      msg="Content-Type from manifest GET is \"application/vnd.oci.image.manifest.v1+json\"" file="docker/docker_client.go:989"
      msg="Found a Sigstore attachment manifest with 1 layers" file="docker/docker_image_src.go:639"
      msg="Fetching Sigstore attachment 1/1: sha256:8276724a208087e73ae5d9d6e8f872f67808c08b0acdfdc73019278807197c45" file="docker/docker_image_src.go:644"
      # ...

      The IsRunningImageAllowed line confirms that image is allowed by the configured sigstore verification policy.

      The Using transport \"docker\" specific policy section \"example.io/crio/signed\"" file="signature/policy_eval.go:150 line confirms that the image policy has been applied.