Container image signatures
To verify the integrity of the images in the Red Hat Container Registries between Red Hat registries and your infrastructure, you can enable signature verification.
Red Hat delivers signatures for the images in the Red Hat Container Registries. Those signatures can be automatically verified when being pulled to OpenShift Container Platform 4 clusters by using the Machine Config Operator (MCO).
To verify the integrity of those images between Red Hat registries and your infrastructure, enable signature verification.
Enable signature verification for Red Hat Container Registries
To verify the integrity of the images in the Red Hat Container Registries, you can enable container signature validation for Red Hat Container Registries by writing a signature verification policy file specifying the keys to verify images from these registries.
For RHEL8 nodes, the registries are already defined in /etc/containers/registries.d by default.
Procedure
-
Create a Butane config file,
51-worker-rh-registry-trust.bu, containing the necessary configuration for the worker nodes.noteThe Butane version you specify in the config file should match the OpenShift Container Platform version and always ends in
0. For example,4.22.0. See "Creating machine configs with Butane" for information about Butane.variant: openshiftversion: 4.22.0metadata:name: 51-worker-rh-registry-trustlabels:machineconfiguration.openshift.io/role: workerstorage:files:- path: /etc/containers/policy.jsonmode: 0644overwrite: truecontents:inline: |{"default": [{"type": "insecureAcceptAnything"}],"transports": {"docker": {"registry.access.redhat.com": [{"type": "signedBy","keyType": "GPGKeys","keyPath": "/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"}],"registry.redhat.io": [{"type": "signedBy","keyType": "GPGKeys","keyPath": "/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"}]},"docker-daemon": {"": [{"type": "insecureAcceptAnything"}]}}} -
Use Butane to generate a machine config YAML file,
51-worker-rh-registry-trust.yaml, containing the file to be written to disk on the worker nodes:$ butane 51-worker-rh-registry-trust.bu -o 51-worker-rh-registry-trust.yaml -
Apply the created machine config:
$ oc apply -f 51-worker-rh-registry-trust.yaml -
Check that the worker machine config pool has rolled out with the new machine config:
-
Check that the new machine config was created:
$ oc get mcSample outputNAME GENERATEDBYCONTROLLER IGNITIONVERSION AGE00-master a2178ad522c49ee330b0033bb5cb5ea132060b0a 3.5.0 25m00-worker a2178ad522c49ee330b0033bb5cb5ea132060b0a 3.5.0 25m01-master-container-runtime a2178ad522c49ee330b0033bb5cb5ea132060b0a 3.5.0 25m01-master-kubelet a2178ad522c49ee330b0033bb5cb5ea132060b0a 3.5.0 25m01-worker-container-runtime a2178ad522c49ee330b0033bb5cb5ea132060b0a 3.5.0 25m01-worker-kubelet a2178ad522c49ee330b0033bb5cb5ea132060b0a 3.5.0 25m51-master-rh-registry-trust 3.5.0 13s51-worker-rh-registry-trust 3.5.0 53s99-master-generated-crio-seccomp-use-default 3.5.0 25m99-master-generated-registries a2178ad522c49ee330b0033bb5cb5ea132060b0a 3.5.0 25m99-master-ssh 3.2.0 28m99-worker-generated-crio-seccomp-use-default 3.5.0 25m99-worker-generated-registries a2178ad522c49ee330b0033bb5cb5ea132060b0a 3.5.0 25m99-worker-ssh 3.2.0 28mrendered-master-af1e7ff78da0a9c851bab4be2777773b a2178ad522c49ee330b0033bb5cb5ea132060b0a 3.5.0 8srendered-master-cd51fd0c47e91812bfef2765c52ec7e6 a2178ad522c49ee330b0033bb5cb5ea132060b0a 3.5.0 24mrendered-worker-2b52f75684fbc711bd1652dd86fd0b82 a2178ad522c49ee330b0033bb5cb5ea132060b0a 3.5.0 24mrendered-worker-be3b3bce4f4aa52a62902304bac9da3c a2178ad522c49ee330b0033bb5cb5ea132060b0a 3.5.0 48swhere:
51-worker-rh-registry-trust- Specifies the new machine config.
rendered-worker-be3b3bce4f4aa52a62902304bac9da3c- Specifies the new rendered machine config.
-
Check that the worker machine config pool is updating with the new machine config:
$ oc get mcpSample outputNAME CONFIG UPDATED UPDATING DEGRADED MACHINECOUNT READYMACHINECOUNT UPDATEDMACHINECOUNT DEGRADEDMACHINECOUNT AGEmaster rendered-master-af1e7ff78da0a9c851bab4be2777773b True False False 3 3 3 0 30mworker rendered-worker-be3b3bce4f4aa52a62902304bac9da3c False True False 3 0 0 0 30mWhen the
UPDATINGfield isTrue, the machine config pool is updating with the new machine config. When the field becomesFalse, the worker machine config pool has rolled out to the new machine config.
-
-
If your cluster uses any RHEL7 worker nodes, when the worker machine config pool is updated, create YAML files on those nodes in the
/etc/containers/registries.ddirectory, which specify the location of the detached signatures for a given registry server. The following example works only for images hosted inregistry.access.redhat.comandregistry.redhat.io.- Start a debug session to each RHEL7 worker node:
$ oc debug node/<node_name>
- Change your root directory to
/host:sh-4.2# chroot /host - Create a
/etc/containers/registries.d/registry.redhat.io.yamlfile that contains the following:docker:registry.redhat.io:sigstore: https://registry.redhat.io/containers/sigstore - Create a
/etc/containers/registries.d/registry.access.redhat.com.yamlfile that contains the following:docker:registry.access.redhat.com:sigstore: https://access.redhat.com/webassets/docker/content/sigstore - Exit the debug session.
- Start a debug session to each RHEL7 worker node:
Verify the signature verification configuration
After you apply the machine configs to the cluster, you can verify that the Machine Config Controller detected the new MachineConfig object and generated a new rendered-worker-<hash> version.
Prerequisites
- You enabled signature verification by using a machine config file.
Procedure
-
On the command line, run the following command to display information about a required worker:
$ oc describe machineconfigpool/workerExample output of initial worker monitoringName: workerNamespace:Labels: machineconfiguration.openshift.io/mco-built-in=Annotations: <none>API Version: machineconfiguration.openshift.io/v1Kind: MachineConfigPoolMetadata:Creation Timestamp: 2019-12-19T02:02:12ZGeneration: 3Resource Version: 16229Self Link: /apis/machineconfiguration.openshift.io/v1/machineconfigpools/workerUID: 92697796-2203-11ea-b48c-fa163e3940e5Spec:Configuration:Name: rendered-worker-f6819366eb455a401c42f8d96ab25c02Source:API Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 00-workerAPI Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 01-worker-container-runtimeAPI Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 01-worker-kubeletAPI Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 51-worker-rh-registry-trustAPI Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 99-worker-92697796-2203-11ea-b48c-fa163e3940e5-registriesAPI Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 99-worker-sshMachine Config Selector:Match Labels:machineconfiguration.openshift.io/role: workerNode Selector:Match Labels:node-role.kubernetes.io/worker:Paused: falseStatus:Conditions:Last Transition Time: 2019-12-19T02:03:27ZMessage:Reason:Status: FalseType: RenderDegradedLast Transition Time: 2019-12-19T02:03:43ZMessage:Reason:Status: FalseType: NodeDegradedLast Transition Time: 2019-12-19T02:03:43ZMessage:Reason:Status: FalseType: DegradedLast Transition Time: 2019-12-19T02:28:23ZMessage:Reason:Status: FalseType: UpdatedLast Transition Time: 2019-12-19T02:28:23ZMessage: All nodes are updating to rendered-worker-f6819366eb455a401c42f8d96ab25c02Reason:Status: TrueType: UpdatingConfiguration:Name: rendered-worker-d9b3f4ffcfd65c30dcf591a0e8cf9b2eSource:API Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 00-workerAPI Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 01-worker-container-runtimeAPI Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 01-worker-kubeletAPI Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 99-worker-92697796-2203-11ea-b48c-fa163e3940e5-registriesAPI Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 99-worker-sshDegraded Machine Count: 0Machine Count: 1Observed Generation: 3Ready Machine Count: 0Unavailable Machine Count: 1Updated Machine Count: 0Events: <none> -
Run the
oc describecommand again:$ oc describe machineconfigpool/workerExample output after the worker is updated...Last Transition Time: 2019-12-19T04:53:09ZMessage: All nodes are updated with rendered-worker-f6819366eb455a401c42f8d96ab25c02Reason:Status: TrueType: UpdatedLast Transition Time: 2019-12-19T04:53:09ZMessage:Reason:Status: FalseType: UpdatingConfiguration:Name: rendered-worker-f6819366eb455a401c42f8d96ab25c02Source:API Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 00-workerAPI Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 01-worker-container-runtimeAPI Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 01-worker-kubeletAPI Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 51-worker-rh-registry-trustAPI Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 99-worker-92697796-2203-11ea-b48c-fa163e3940e5-registriesAPI Version: machineconfiguration.openshift.io/v1Kind: MachineConfigName: 99-worker-sshDegraded Machine Count: 0Machine Count: 3Observed Generation: 4Ready Machine Count: 3Unavailable Machine Count: 0Updated Machine Count: 3...noteThe
Observed Generationparameter shows an increased count based on the generation of the controller-produced configuration. This controller updates this value even if it fails to process the specification and generate a revision. TheConfiguration Sourcevalue points to the51-worker-rh-registry-trustconfiguration. -
Confirm that the
policy.jsonfile exists with the following command:$ oc debug node/<node> -- chroot /host cat /etc/containers/policy.jsonExample outputStarting pod/<node>-debug ...To use host binaries, run `chroot /host`{"default": [{"type": "insecureAcceptAnything"}],"transports": {"docker": {"registry.access.redhat.com": [{"type": "signedBy","keyType": "GPGKeys","keyPath": "/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"}],"registry.redhat.io": [{"type": "signedBy","keyType": "GPGKeys","keyPath": "/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"}]},"docker-daemon": {"": [{"type": "insecureAcceptAnything"}]}}} -
Confirm that the
registry.redhat.io.yamlfile exists with the following command:$ oc debug node/<node> -- chroot /host cat /etc/containers/registries.d/registry.redhat.io.yamlExample outputStarting pod/<node>-debug ...To use host binaries, run `chroot /host`docker:registry.redhat.io:sigstore: https://registry.redhat.io/containers/sigstore -
Confirm that the
registry.access.redhat.com.yamlfile exists with the following command:$ oc debug node/<node> -- chroot /host cat /etc/containers/registries.d/registry.access.redhat.com.yamlExample outputStarting pod/<node>-debug ...To use host binaries, run `chroot /host`docker:registry.access.redhat.com:sigstore: https://access.redhat.com/webassets/docker/content/sigstore
Understand the verification of container images lacking verifiable signatures
Each OpenShift Container Platform release image is immutable and signed with a Red Hat production key. During cluster update or installation, a release image might deploy container images without a verifiable signature. The signature on the release image validates all release contents transitively.
For example, the image references lacking a verifiable signature are contained in the signed OpenShift Container Platform release image:
$ oc adm release info quay.io/openshift-release-dev/ocp-release@sha256:2309578b68c5666dad62aed696f1f9d778ae1a089ee461060ba7b9514b7ca417 -o pullspec
quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:9aafb914d5d7d0dec4edd800d02f811d7383a7d49e500af548eab5d00c1bffdb
The first line specifies the signed release image SHA. The second line specifies a container image lacking a verifiable signature that is included in the release.
Automated verification during updates
Verification of signatures is automatic. The OpenShift Cluster Version Operator (CVO) verifies signatures on the release images during an OpenShift Container Platform update. This is an internal process. An OpenShift Container Platform installation or update fails if the automated verification fails.
Verification of signatures can also be done manually using the skopeo command-line utility.
Use skopeo to verify signatures of Red Hat container images
You can verify the signatures for container images included in an OpenShift Container Platform release image by pulling those signatures from the OpenShift Container Platform release mirror site.
Because the signatures on the mirror site are not in a format readily understood by Podman or CRI-O, you can use the skopeo standalone-verify command to verify that your release images are signed by Red Hat.
Prerequisites
- You have installed the
skopeocommand-line utility.
Procedure
-
Get the full SHA for your release by running the following command:
$ oc adm release info <release_version>- Substitute <release_version> with your release number, for example,
4.14.3.Example output snippet---Pull From: quay.io/openshift-release-dev/ocp-release@sha256:e73ab4b33a9c3ff00c9f800a38d69853ca0c4dfa5a88e3df331f66df8f18ec55---
- Substitute <release_version> with your release number, for example,
-
Pull down the Red Hat release key by running the following command:
$ curl -o pub.key https://access.redhat.com/security/data/fd431d51.txt -
Get the signature file for the specific release that you want to verify by running the following command:
$ curl -o signature-1 https://mirror.openshift.com/pub/openshift-v4/signatures/openshift-release-dev/ocp-release/sha256=<sha_from_version>/signature-1Replace
<sha_from_version>with SHA value from the full link to the mirror site that matches the SHA of your release. For example, the link to the signature for the 4.12.23 release ishttps://mirror.openshift.com/pub/openshift-v4/signatures/openshift-release-dev/ocp-release/sha256=e73ab4b33a9c3ff00c9f800a38d69853ca0c4dfa5a88e3df331f66df8f18ec55/signature-1, and the SHA value ise73ab4b33a9c3ff00c9f800a38d69853ca0c4dfa5a88e3df331f66df8f18ec55. -
Get the manifest for the release image by running the following command:
$ skopeo inspect --raw docker://<quay_link_to_release> > manifest.jsonReplace
<quay_link_to_release>with the output of theoc adm release infocommand. For example,quay.io/openshift-release-dev/ocp-release@sha256:e73ab4b33a9c3ff00c9f800a38d69853ca0c4dfa5a88e3df331f66df8f18ec55. -
Use skopeo to verify the signature:
$ skopeo standalone-verify manifest.json quay.io/openshift-release-dev/ocp-release:<release_number>-<arch> any signature-1 --public-key-file pub.keywhere:
<release_number>- Specifies the release number, for example
4.14.3. <arch>- Specifies the architecture, for example
x86_64.
Example outputSignature verified using fingerprint 567E347AD0044ADE55BA8A5F199E2F91FD431D51, digest sha256:e73ab4b33a9c3ff00c9f800a38d69853ca0c4dfa5a88e3df331f66df8f18ec55
Additional resources