Notary
Notary is a CNCF project that provides a specification and tooling for securing software supply chains.
The Notation CLI can be used to sign images and attestations in a CI/CD pipeline. A quick start guide providing a complete example of signing and verifying a container image using Notation can be found here.
The Notation CLI can also be used to inspect details of the container image signature.
1notation inspect ghcr.io/kyverno/test-verify-image@sha256:b31bfb4d0213f254d361e0079deaaebefa4f82ba7aa76ef82e90b4935ad5b105
2Inspecting all signatures for signed artifact
3ghcr.io/kyverno/test-verify-image@sha256:b31bfb4d0213f254d361e0079deaaebefa4f82ba7aa76ef82e90b4935ad5b105
4└── application/vnd.cncf.notary.signature
5 └── sha256:7f870420d92765b42cec0f71ee8e25bf39b692f64d95d6f6607e9e6e54300265
6 ├── media type: application/jose+json
7 ├── signature algorithm: RSASSA-PSS-SHA-256
8 ├── signed attributes
9 │ ├── signingScheme: notary.x509
10 │ └── signingTime: Mon May 22 14:45:04 2023
11 ├── user defined attributes
12 │ └── (empty)
13 ├── unsigned attributes
14 │ └── signingAgent: Notation/1.0.0
15 ├── certificates
16 │ └── SHA256 fingerprint: da1f2d7d648dfacc7ebd59f98a9f35c753c331d80ca4280bb94060f4af4a5357
17 │ ├── issued to: CN=test,O=Notary,L=Seattle,ST=WA,C=US
18 │ ├── issued by: CN=test,O=Notary,L=Seattle,ST=WA,C=US
19 │ └── expiry: Thu May 19 21:15:18 2033
20 └── signed artifact
21 ├── media type: application/vnd.docker.distribution.manifest.v2+json
22 ├── digest: sha256:b31bfb4d0213f254d361e0079deaaebefa4f82ba7aa76ef82e90b4935ad5b105
23 └── size: 938
You can also use an OCI registry client to discover signatures and attestations for an image.
1oras discover ghcr.io/kyverno/test-verify-image:signed -o tree
2ghcr.io/kyverno/test-verify-image:signed
3├── application/vnd.cncf.notary.signature
4│ └── sha256:7f870420d92765b42cec0f71ee8e25bf39b692f64d95d6f6607e9e6e54300265
5├── vulnerability-scan
6│ └── sha256:f89cb7a0748c63a674d157ca84d725ff3ac09cc2d4aee9d0ec4315e0fe92a5fd
7│ └── application/vnd.cncf.notary.signature
8│ └── sha256:ec45844601244aa08ac750f44def3fd48ddacb736d26b83dde9f5d8ac646c2f3
9└── sbom/cyclone-dx
10 └── sha256:8cad9bd6de426683424a204697dd48b55abcd6bb6b4930ad9d8ade99ae165414
11 └── application/vnd.cncf.notary.signature
12 └── sha256:61f3e42f017b72f4277c78a7a42ff2ad8f872811324cd984830dfaeb4030c322
Verifying Image Signatures
The following policy checks whether an image is signed with a valid X.509 key that matches the provided public certificate.
1apiVersion: kyverno.io/v2beta1
2kind: ClusterPolicy
3metadata:
4 name: check-image-notary
5spec:
6 validationFailureAction: Enforce
7 webhookTimeoutSeconds: 30
8 failurePolicy: Fail
9 rules:
10 - name: verify-signature-notary
11 match:
12 any:
13 - resources:
14 kinds:
15 - Pod
16 verifyImages:
17 - type: Notary
18 imageReferences:
19 - "ghcr.io/kyverno/test-verify-image*"
20 attestors:
21 - count: 1
22 entries:
23 - certificates:
24 cert: |-
25 -----BEGIN CERTIFICATE-----
26 MIIDTTCCAjWgAwIBAgIJAPI+zAzn4s0xMA0GCSqGSIb3DQEBCwUAMEwxCzAJBgNV
27 BAYTAlVTMQswCQYDVQQIDAJXQTEQMA4GA1UEBwwHU2VhdHRsZTEPMA0GA1UECgwG
28 Tm90YXJ5MQ0wCwYDVQQDDAR0ZXN0MB4XDTIzMDUyMjIxMTUxOFoXDTMzMDUxOTIx
29 MTUxOFowTDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0
30 dGxlMQ8wDQYDVQQKDAZOb3RhcnkxDTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3
31 DQEBAQUAA4IBDwAwggEKAoIBAQDNhTwv+QMk7jEHufFfIFlBjn2NiJaYPgL4eBS+
32 b+o37ve5Zn9nzRppV6kGsa161r9s2KkLXmJrojNy6vo9a6g6RtZ3F6xKiWLUmbAL
33 hVTCfYw/2n7xNlVMjyyUpE+7e193PF8HfQrfDFxe2JnX5LHtGe+X9vdvo2l41R6m
34 Iia04DvpMdG4+da2tKPzXIuLUz/FDb6IODO3+qsqQLwEKmmUee+KX+3yw8I6G1y0
35 Vp0mnHfsfutlHeG8gazCDlzEsuD4QJ9BKeRf2Vrb0ywqNLkGCbcCWF2H5Q80Iq/f
36 ETVO9z88R7WheVdEjUB8UrY7ZMLdADM14IPhY2Y+tLaSzEVZAgMBAAGjMjAwMAkG
37 A1UdEwQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0G
38 CSqGSIb3DQEBCwUAA4IBAQBX7x4Ucre8AIUmXZ5PUK/zUBVOrZZzR1YE8w86J4X9
39 kYeTtlijf9i2LTZMfGuG0dEVFN4ae3CCpBst+ilhIndnoxTyzP+sNy4RCRQ2Y/k8
40 Zq235KIh7uucq96PL0qsF9s2RpTKXxyOGdtp9+HO0Ty5txJE2txtLDUIVPK5WNDF
41 ByCEQNhtHgN6V20b8KU2oLBZ9vyB8V010dQz0NRTDLhkcvJig00535/LUylECYAJ
42 5/jn6XKt6UYCQJbVNzBg/YPGc1RF4xdsGVDBben/JXpeGEmkdmXPILTKd9tZ5TC0
43 uOKpF5rWAruB5PCIrquamOejpXV9aQA/K2JQDuc0mcKz
44 -----END CERTIFICATE-----
With this policy configured, Kyverno will verify matching container image signatures and only allow the pod to be configured if the signatures are valid.
1kubectl run test --image=ghcr.io/kyverno/test-verify-image:signed --dry-run=server
2pod/test created (server dry run)
Kyverno will also mutate the pod to replace the image tag with its digest.
1kubectl run test --image=ghcr.io/kyverno/test-verify-image:signed --dry-run=server -o yaml | grep "image: "
2 - image: ghcr.io/kyverno/test-verify-image:signed@sha256:b31bfb4d0213f254d361e0079deaaebefa4f82ba7aa76ef82e90b4935ad5b105
Attempting to run a pod with an unsigned image will be blocked.
1kubectl run test --image=ghcr.io/kyverno/test-verify-image:unsigned --dry-run=server
2Error from server: admission webhook "mutate.kyverno.svc-fail" denied the request:
3
4resource Pod/default/test was blocked due to the following policies
5
6check-image-notary:
7 verify-signature-notary: 'failed to verify image ghcr.io/kyverno/test-verify-image:unsigned:
8 .attestors[0].entries[0]: failed to verify ghcr.io/kyverno/test-verify-image@sha256:74a98f0e4d750c9052f092a7f7a72de7b20f94f176a490088f7a744c76c53ea5:
9 no signature is associated with "ghcr.io/kyverno/test-verify-image@sha256:74a98f0e4d750c9052f092a7f7a72de7b20f94f176a490088f7a744c76c53ea5",
10 make sure the image was signed successfully'
Tip
You can manage public keys and certificates as external data in a ConfigMap. See Variables from ConfigMaps for details.Verifying Image Attestations
Consider the following image: ghcr.io/kyverno/test-verify-image:signed
ghcr.io/kyverno/test-verify-image:signed
├── application/vnd.cncf.notary.signature
│ └── sha256:7f870420d92765b42cec0f71ee8e25bf39b692f64d95d6f6607e9e6e54300265
├── vulnerability-scan
│ └── sha256:f89cb7a0748c63a674d157ca84d725ff3ac09cc2d4aee9d0ec4315e0fe92a5fd
│ └── application/vnd.cncf.notary.signature
│ └── sha256:ec45844601244aa08ac750f44def3fd48ddacb736d26b83dde9f5d8ac646c2f3
└── sbom/cyclone-dx
└── sha256:8cad9bd6de426683424a204697dd48b55abcd6bb6b4930ad9d8ade99ae165414
└── application/vnd.cncf.notary.signature
└── sha256:61f3e42f017b72f4277c78a7a42ff2ad8f872811324cd984830dfaeb4030c322
This image has:
- A notary signature.
- A vulnerability scan report, signed using notary.
- A CycloneDX SBOM, signed using notary.
This policy checks the signature in the repo
ghcr.io/kyverno/test-verify-image
and ensures that it has been signed by verifying its signature against the provided certificates:
1apiVersion: kyverno.io/v1
2kind: ClusterPolicy
3metadata:
4 name: check-image-attestation
5spec:
6 validationFailureAction: Enforce
7 webhookTimeoutSeconds: 30
8 failurePolicy: Fail
9 rules:
10 - name: verify-attestation-notary
11 match:
12 any:
13 - resources:
14 kinds:
15 - Pod
16 context:
17 - name: keys
18 configMap:
19 name: keys
20 namespace: kyverno
21 verifyImages:
22 - type: Notary
23 imageReferences:
24 - "ghcr.io/kyverno/test-verify-image*"
25 attestations:
26 - type: sbom/cyclone-dx
27 attestors:
28 - entries:
29 - certificates:
30 cert: |-
31 -----BEGIN CERTIFICATE-----
32 MIIDTTCCAjWgAwIBAgIJAPI+zAzn4s0xMA0GCSqGSIb3DQEBCwUAMEwxCzAJBgNV
33 BAYTAlVTMQswCQYDVQQIDAJXQTEQMA4GA1UEBwwHU2VhdHRsZTEPMA0GA1UECgwG
34 Tm90YXJ5MQ0wCwYDVQQDDAR0ZXN0MB4XDTIzMDUyMjIxMTUxOFoXDTMzMDUxOTIx
35 MTUxOFowTDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0
36 dGxlMQ8wDQYDVQQKDAZOb3RhcnkxDTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3
37 DQEBAQUAA4IBDwAwggEKAoIBAQDNhTwv+QMk7jEHufFfIFlBjn2NiJaYPgL4eBS+
38 b+o37ve5Zn9nzRppV6kGsa161r9s2KkLXmJrojNy6vo9a6g6RtZ3F6xKiWLUmbAL
39 hVTCfYw/2n7xNlVMjyyUpE+7e193PF8HfQrfDFxe2JnX5LHtGe+X9vdvo2l41R6m
40 Iia04DvpMdG4+da2tKPzXIuLUz/FDb6IODO3+qsqQLwEKmmUee+KX+3yw8I6G1y0
41 Vp0mnHfsfutlHeG8gazCDlzEsuD4QJ9BKeRf2Vrb0ywqNLkGCbcCWF2H5Q80Iq/f
42 ETVO9z88R7WheVdEjUB8UrY7ZMLdADM14IPhY2Y+tLaSzEVZAgMBAAGjMjAwMAkG
43 A1UdEwQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0G
44 CSqGSIb3DQEBCwUAA4IBAQBX7x4Ucre8AIUmXZ5PUK/zUBVOrZZzR1YE8w86J4X9
45 kYeTtlijf9i2LTZMfGuG0dEVFN4ae3CCpBst+ilhIndnoxTyzP+sNy4RCRQ2Y/k8
46 Zq235KIh7uucq96PL0qsF9s2RpTKXxyOGdtp9+HO0Ty5txJE2txtLDUIVPK5WNDF
47 ByCEQNhtHgN6V20b8KU2oLBZ9vyB8V010dQz0NRTDLhkcvJig00535/LUylECYAJ
48 5/jn6XKt6UYCQJbVNzBg/YPGc1RF4xdsGVDBben/JXpeGEmkdmXPILTKd9tZ5TC0
49 uOKpF5rWAruB5PCIrquamOejpXV9aQA/K2JQDuc0mcKz
50 -----END CERTIFICATE-----
51 conditions:
52 - all:
53 - key: "{{ components[].licenses[].expression }}"
54 operator: AllIn
55 value: ["GPL-3.0"]
56
After this policy is applied, Kyverno will verify the signature on the sbom/cyclone-dx attestation and check if the license version of all the components in the SBOM is GPL-3.0
.
1kubectl run test --image=ghcr.io/kyverno/test-verify-image:signed --dry-run=server
2pod/test created (server dry run)