From 256179a4edaa6c03f5a303e722e29177af66b62f Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Thu, 18 Jun 2026 15:15:21 +0100 Subject: [PATCH 01/17] feat: add kyverno ecr pull requirement policy --- .../hard-requirement/ecr-pull-policy.yaml | 56 +++++++++++++++++++ .../hard-requirement/kustomization.yaml | 1 + 2 files changed, 57 insertions(+) create mode 100644 apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml new file mode 100644 index 00000000..fcb919dc --- /dev/null +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -0,0 +1,56 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: ecr-pull-compliance + annotations: + policies.kyverno.io/title: ECR Pull Policy Compliance Check + policies.kyverno.io/category: Requirement + policies.kyverno.io/severity: medium + policies.kyverno.io/subject: Pod + policies.kyverno.io/description: >- + Validates that container images from org-owned ECR repositories have + compliant pull policies that allow cross-account access within the + AWS Organization. +spec: + validationFailureAction: Audit + background: true + emitWarning: true + rules: + - name: check-ecr-image-compliance + match: + any: + - resources: + kinds: + - Pod + operations: + - CREATE + - UPDATE + exclude: + any: + - resources: + namespaces: + - kube-system + - kyverno + validate: + message: >- + Image {{ element.image }} references an ECR repository that does not + have a compliant pull policy. The repository must have a policy that + allows pull access for all accounts in the AWS Organization. + Reason: {{ complianceCheck.reason }} + foreach: + - list: "request.object.spec.containers" + context: + - name: complianceCheck + apiCall: + method: POST + data: + - key: image + value: "{{ element.image }}" + service: + url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc:8080/api/v1/compliance/check + deny: + conditions: + all: + - key: "{{ complianceCheck.allowed }}" + operator: Equals + value: false \ No newline at end of file diff --git a/apps/kyverno/policies/hard-requirement/kustomization.yaml b/apps/kyverno/policies/hard-requirement/kustomization.yaml index 17d43750..938f357f 100644 --- a/apps/kyverno/policies/hard-requirement/kustomization.yaml +++ b/apps/kyverno/policies/hard-requirement/kustomization.yaml @@ -4,3 +4,4 @@ resources: - require-external-secret-label.yaml - require-probes.yaml - require-hpa.yaml + - ecr-pull-policy.yaml From 8b9a95e5441b46efde1319c0efe5ece10b1aaf2d Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Tue, 23 Jun 2026 16:53:21 +0100 Subject: [PATCH 02/17] fix: fix ecr pull policy --- .../hard-requirement/ecr-pull-policy.yaml | 76 ++++++++++++++++--- 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml index fcb919dc..e3a48b38 100644 --- a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -15,6 +15,7 @@ spec: validationFailureAction: Audit background: true emitWarning: true + rules: - name: check-ecr-image-compliance match: @@ -25,20 +26,71 @@ spec: operations: - CREATE - UPDATE + exclude: any: - - resources: - namespaces: - - kube-system - - kyverno + - resources: + namespaces: + - kube-system + - kyverno + validate: - message: >- - Image {{ element.image }} references an ECR repository that does not - have a compliant pull policy. The repository must have a policy that - allows pull access for all accounts in the AWS Organization. - Reason: {{ complianceCheck.reason }} foreach: + + # Regular containers - list: "request.object.spec.containers" + + context: + - name: complianceCheck + apiCall: + method: POST + data: + - key: image + value: "{{ element.image }}" + service: + url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc:8080/api/v1/compliance/check + + message: >- + Image {{ element.image }} references an ECR repository that does + not have a compliant pull policy. The repository must have a + policy that allows pull access for all accounts in the AWS + Organization. Reason: {{ complianceCheck.reason }} + + deny: + conditions: + all: + - key: "{{ complianceCheck.allowed }}" + operator: Equals + value: false + + # Init containers + - list: "request.object.spec.initContainers || `[]`" + + context: + - name: complianceCheck + apiCall: + method: POST + data: + - key: image + value: "{{ element.image }}" + service: + url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc:8080/api/v1/compliance/check + + message: >- + Init container image {{ element.image }} references an ECR + repository that does not have a compliant pull policy. + Reason: {{ complianceCheck.reason }} + + deny: + conditions: + all: + - key: "{{ complianceCheck.allowed }}" + operator: Equals + value: false + + # Ephemeral containers + - list: "request.object.spec.ephemeralContainers || `[]`" + context: - name: complianceCheck apiCall: @@ -48,6 +100,12 @@ spec: value: "{{ element.image }}" service: url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc:8080/api/v1/compliance/check + + message: >- + Ephemeral container image {{ element.image }} references an ECR + repository that does not have a compliant pull policy. + Reason: {{ complianceCheck.reason }} + deny: conditions: all: From 2e12ad4561dda0dae8bd5dd77d75db96d899b848 Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Tue, 23 Jun 2026 17:02:37 +0100 Subject: [PATCH 03/17] fix: fix ecr policy --- .../hard-requirement/ecr-pull-policy.yaml | 28 ++++--------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml index e3a48b38..3dbbed6b 100644 --- a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -35,11 +35,14 @@ spec: - kyverno validate: + message: >- + One or more container images reference an ECR repository that does + not have a compliant pull policy. + foreach: - # Regular containers + # Standard containers - list: "request.object.spec.containers" - context: - name: complianceCheck apiCall: @@ -49,13 +52,6 @@ spec: value: "{{ element.image }}" service: url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc:8080/api/v1/compliance/check - - message: >- - Image {{ element.image }} references an ECR repository that does - not have a compliant pull policy. The repository must have a - policy that allows pull access for all accounts in the AWS - Organization. Reason: {{ complianceCheck.reason }} - deny: conditions: all: @@ -65,7 +61,6 @@ spec: # Init containers - list: "request.object.spec.initContainers || `[]`" - context: - name: complianceCheck apiCall: @@ -75,12 +70,6 @@ spec: value: "{{ element.image }}" service: url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc:8080/api/v1/compliance/check - - message: >- - Init container image {{ element.image }} references an ECR - repository that does not have a compliant pull policy. - Reason: {{ complianceCheck.reason }} - deny: conditions: all: @@ -90,7 +79,6 @@ spec: # Ephemeral containers - list: "request.object.spec.ephemeralContainers || `[]`" - context: - name: complianceCheck apiCall: @@ -100,12 +88,6 @@ spec: value: "{{ element.image }}" service: url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc:8080/api/v1/compliance/check - - message: >- - Ephemeral container image {{ element.image }} references an ECR - repository that does not have a compliant pull policy. - Reason: {{ complianceCheck.reason }} - deny: conditions: all: From df1e69416fb432c49be8e7e0bcffa498af93c597 Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Wed, 24 Jun 2026 07:57:38 +0100 Subject: [PATCH 04/17] fix: update url --- apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml index 3dbbed6b..b775d8ce 100644 --- a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -51,7 +51,7 @@ spec: - key: image value: "{{ element.image }}" service: - url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc:8080/api/v1/compliance/check + url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check deny: conditions: all: From 75463e44f74a15257c923bf0a1c61a47bb428dff Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Wed, 24 Jun 2026 10:17:28 +0100 Subject: [PATCH 05/17] fix: fix ecr policy --- .../policies/hard-requirement/ecr-pull-policy.yaml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml index b775d8ce..c61e1c42 100644 --- a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -52,6 +52,9 @@ spec: value: "{{ element.image }}" service: url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check + headers: + - key: Content-Type + value: application/json deny: conditions: all: @@ -69,7 +72,10 @@ spec: - key: image value: "{{ element.image }}" service: - url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc:8080/api/v1/compliance/check + url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check + headers: + - key: Content-Type + value: application/json deny: conditions: all: @@ -87,7 +93,10 @@ spec: - key: image value: "{{ element.image }}" service: - url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc:8080/api/v1/compliance/check + url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check + headers: + - key: Content-Type + value: application/json deny: conditions: all: From bb3875d5e5e5455f0a418cef054bffc1fd086114 Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Wed, 24 Jun 2026 10:40:42 +0100 Subject: [PATCH 06/17] fix: add link to wiki for more info --- apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml index c61e1c42..065cb999 100644 --- a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -37,8 +37,8 @@ spec: validate: message: >- One or more container images reference an ECR repository that does - not have a compliant pull policy. - + not have a compliant pull policy. Visit https://wiki.dfds.cloud/en/playbooks/requirements/ECR + for more information. foreach: # Standard containers From 68241939a3976e1898270ee1e87c3a60bf53a647 Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Wed, 24 Jun 2026 14:58:49 +0100 Subject: [PATCH 07/17] feat: allow if calls to the service fail so we don't block people deploying --- .../hard-requirement/ecr-pull-policy.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml index 065cb999..cd26cd86 100644 --- a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -16,6 +16,12 @@ spec: background: true emitWarning: true + # Fail-open at the webhook level: if Kyverno (or its webhook) is + # unreachable or times out, allow the request rather than block it. + webhookConfiguration: + failurePolicy: Ignore + timeoutSeconds: 10 + rules: - name: check-ecr-image-compliance match: @@ -39,6 +45,7 @@ spec: One or more container images reference an ECR repository that does not have a compliant pull policy. Visit https://wiki.dfds.cloud/en/playbooks/requirements/ECR for more information. + foreach: # Standard containers @@ -55,6 +62,9 @@ spec: headers: - key: Content-Type value: application/json + # Fail-open: if the service errors, treat the image as allowed. + default: + allowed: true deny: conditions: all: @@ -76,6 +86,9 @@ spec: headers: - key: Content-Type value: application/json + # Fail-open: if the service errors, treat the image as allowed. + default: + allowed: true deny: conditions: all: @@ -97,6 +110,9 @@ spec: headers: - key: Content-Type value: application/json + # Fail-open: if the service errors, treat the image as allowed. + default: + allowed: true deny: conditions: all: From 4f2ededbc985f0946d287ab3414e68bdb46c69cf Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Wed, 24 Jun 2026 15:50:10 +0100 Subject: [PATCH 08/17] fix: better handling of check_unavailable --- .../hard-requirement/ecr-pull-policy.yaml | 118 +++++++++++++++++- 1 file changed, 116 insertions(+), 2 deletions(-) diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml index cd26cd86..5ce2f2ba 100644 --- a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -12,7 +12,6 @@ metadata: compliant pull policies that allow cross-account access within the AWS Organization. spec: - validationFailureAction: Audit background: true emitWarning: true @@ -23,6 +22,13 @@ spec: timeoutSeconds: 10 rules: + # ------------------------------------------------------------------ + # Rule A: enforce compliance for images the service successfully + # evaluated. Currently Audit (warn only). Flip failureAction to + # Enforce to start blocking non-compliant images. On a service error + # the apiCall default sets allowed=true, so this rule fails open + # silently (the warning is emitted by Rule B instead). + # ------------------------------------------------------------------ - name: check-ecr-image-compliance match: any: @@ -41,6 +47,7 @@ spec: - kyverno validate: + failureAction: Audit message: >- One or more container images reference an ECR repository that does not have a compliant pull policy. Visit https://wiki.dfds.cloud/en/playbooks/requirements/ECR @@ -65,6 +72,7 @@ spec: # Fail-open: if the service errors, treat the image as allowed. default: allowed: true + reason: check_unavailable deny: conditions: all: @@ -89,6 +97,7 @@ spec: # Fail-open: if the service errors, treat the image as allowed. default: allowed: true + reason: check_unavailable deny: conditions: all: @@ -113,9 +122,114 @@ spec: # Fail-open: if the service errors, treat the image as allowed. default: allowed: true + reason: check_unavailable deny: conditions: all: - key: "{{ complianceCheck.allowed }}" operator: Equals - value: false \ No newline at end of file + value: false + + # ------------------------------------------------------------------ + # Rule B: warn (never block) when the compliance service could not be + # reached. Detected via the sentinel reason set by the apiCall default. + # Kept on Audit permanently so a service outage only surfaces a + # warning while images are still admitted (fail-open). + # ------------------------------------------------------------------ + - name: warn-ecr-check-unavailable + match: + any: + - resources: + kinds: + - Pod + operations: + - CREATE + - UPDATE + + exclude: + any: + - resources: + namespaces: + - kube-system + - kyverno + + validate: + failureAction: Audit + message: >- + The ECR pull compliance service was unavailable; one or more + container images were admitted without verification. See + https://wiki.dfds.cloud/en/playbooks/requirements/ECR + + foreach: + + # Standard containers + - list: "request.object.spec.containers" + context: + - name: complianceCheck + apiCall: + method: POST + data: + - key: image + value: "{{ element.image }}" + service: + url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check + headers: + - key: Content-Type + value: application/json + default: + allowed: true + reason: check_unavailable + deny: + conditions: + all: + - key: "{{ complianceCheck.reason }}" + operator: Equals + value: check_unavailable + + # Init containers + - list: "request.object.spec.initContainers || `[]`" + context: + - name: complianceCheck + apiCall: + method: POST + data: + - key: image + value: "{{ element.image }}" + service: + url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check + headers: + - key: Content-Type + value: application/json + default: + allowed: true + reason: check_unavailable + deny: + conditions: + all: + - key: "{{ complianceCheck.reason }}" + operator: Equals + value: check_unavailable + + # Ephemeral containers + - list: "request.object.spec.ephemeralContainers || `[]`" + context: + - name: complianceCheck + apiCall: + method: POST + data: + - key: image + value: "{{ element.image }}" + service: + url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check + headers: + - key: Content-Type + value: application/json + default: + allowed: true + reason: check_unavailable + deny: + conditions: + all: + - key: "{{ complianceCheck.reason }}" + operator: Equals + value: check_unavailable From 81e59cf5e1251ecc2d94d87ca6384c35dcd79772 Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Tue, 30 Jun 2026 14:04:29 +0100 Subject: [PATCH 09/17] chore: migrate to validatingpolicy --- .../hard-requirement/ecr-pull-policy.yaml | 276 ++++-------------- 1 file changed, 62 insertions(+), 214 deletions(-) diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml index 5ce2f2ba..235b81e1 100644 --- a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -1,5 +1,5 @@ -apiVersion: kyverno.io/v1 -kind: ClusterPolicy +apiVersion: policies.kyverno.io/v1 +kind: ValidatingPolicy metadata: name: ecr-pull-compliance annotations: @@ -12,224 +12,72 @@ metadata: compliant pull policies that allow cross-account access within the AWS Organization. spec: - background: true - emitWarning: true + # Fail-open at the webhook level: if Kyverno (or the compliance service) is + # unreachable or times out, the http.Post expression errors and the request + # is admitted rather than blocked. + # Note: this does not emit a a dedicated "admitted without verification" warning + # as it does not seem possible with the new CEL policies + failurePolicy: Ignore + + # Without this, ValidatingPolicy only matches Pods, + # so a bad Deployment would be admitted and silently fail to create pods. + autogen: + podControllers: + controllers: + - deployments + - statefulsets + - daemonsets + - jobs + - cronjobs - # Fail-open at the webhook level: if Kyverno (or its webhook) is - # unreachable or times out, allow the request rather than block it. webhookConfiguration: - failurePolicy: Ignore timeoutSeconds: 10 - rules: - # ------------------------------------------------------------------ - # Rule A: enforce compliance for images the service successfully - # evaluated. Currently Audit (warn only). Flip failureAction to - # Enforce to start blocking non-compliant images. On a service error - # the apiCall default sets allowed=true, so this rule fails open - # silently (the warning is emitted by Rule B instead). - # ------------------------------------------------------------------ - - name: check-ecr-image-compliance - match: - any: - - resources: - kinds: - - Pod - operations: - - CREATE - - UPDATE - - exclude: - any: - - resources: - namespaces: - - kube-system - - kyverno - - validate: - failureAction: Audit - message: >- - One or more container images reference an ECR repository that does - not have a compliant pull policy. Visit https://wiki.dfds.cloud/en/playbooks/requirements/ECR - for more information. - - foreach: - - # Standard containers - - list: "request.object.spec.containers" - context: - - name: complianceCheck - apiCall: - method: POST - data: - - key: image - value: "{{ element.image }}" - service: - url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check - headers: - - key: Content-Type - value: application/json - # Fail-open: if the service errors, treat the image as allowed. - default: - allowed: true - reason: check_unavailable - deny: - conditions: - all: - - key: "{{ complianceCheck.allowed }}" - operator: Equals - value: false - - # Init containers - - list: "request.object.spec.initContainers || `[]`" - context: - - name: complianceCheck - apiCall: - method: POST - data: - - key: image - value: "{{ element.image }}" - service: - url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check - headers: - - key: Content-Type - value: application/json - # Fail-open: if the service errors, treat the image as allowed. - default: - allowed: true - reason: check_unavailable - deny: - conditions: - all: - - key: "{{ complianceCheck.allowed }}" - operator: Equals - value: false - - # Ephemeral containers - - list: "request.object.spec.ephemeralContainers || `[]`" - context: - - name: complianceCheck - apiCall: - method: POST - data: - - key: image - value: "{{ element.image }}" - service: - url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check - headers: - - key: Content-Type - value: application/json - # Fail-open: if the service errors, treat the image as allowed. - default: - allowed: true - reason: check_unavailable - deny: - conditions: - all: - - key: "{{ complianceCheck.allowed }}" - operator: Equals - value: false - - # ------------------------------------------------------------------ - # Rule B: warn (never block) when the compliance service could not be - # reached. Detected via the sentinel reason set by the apiCall default. - # Kept on Audit permanently so a service outage only surfaces a - # warning while images are still admitted (fail-open). - # ------------------------------------------------------------------ - - name: warn-ecr-check-unavailable - match: - any: - - resources: - kinds: - - Pod - operations: - - CREATE - - UPDATE - - exclude: - any: - - resources: - namespaces: - - kube-system - - kyverno + evaluation: + background: + enabled: true - validate: - failureAction: Audit - message: >- - The ECR pull compliance service was unavailable; one or more - container images were admitted without verification. See - https://wiki.dfds.cloud/en/playbooks/requirements/ECR + # Audit (warn only). Switch to [Deny] to start blocking non-compliant images. + validationActions: + - Audit - foreach: + matchConstraints: + resourceRules: + - apiGroups: [''] + apiVersions: ['v1'] + operations: + - CREATE + - UPDATE + resources: + - pods - # Standard containers - - list: "request.object.spec.containers" - context: - - name: complianceCheck - apiCall: - method: POST - data: - - key: image - value: "{{ element.image }}" - service: - url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check - headers: - - key: Content-Type - value: application/json - default: - allowed: true - reason: check_unavailable - deny: - conditions: - all: - - key: "{{ complianceCheck.reason }}" - operator: Equals - value: check_unavailable + # Equivalent of the ClusterPolicy exclude on namespaces. + matchConditions: + - name: exclude-system-namespaces + expression: "!(object.metadata.namespace in ['kube-system', 'kyverno'])" - # Init containers - - list: "request.object.spec.initContainers || `[]`" - context: - - name: complianceCheck - apiCall: - method: POST - data: - - key: image - value: "{{ element.image }}" - service: - url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check - headers: - - key: Content-Type - value: application/json - default: - allowed: true - reason: check_unavailable - deny: - conditions: - all: - - key: "{{ complianceCheck.reason }}" - operator: Equals - value: check_unavailable + variables: + # Combine standard, init, and ephemeral containers into a single list, + # replacing the three separate foreach blocks. + - name: allContainers + expression: >- + object.spec.containers + + object.spec.?initContainers.orValue([]) + + object.spec.?ephemeralContainers.orValue([]) - # Ephemeral containers - - list: "request.object.spec.ephemeralContainers || `[]`" - context: - - name: complianceCheck - apiCall: - method: POST - data: - - key: image - value: "{{ element.image }}" - service: - url: http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check - headers: - - key: Content-Type - value: application/json - default: - allowed: true - reason: check_unavailable - deny: - conditions: - all: - - key: "{{ complianceCheck.reason }}" - operator: Equals - value: check_unavailable + validations: + # Per-image POST to the compliance exporter. deny(allowed == false) is + # inverted to an assertion that every image is allowed. + - expression: >- + variables.allContainers.all(c, + http.Post( + "http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check", + {"image": c.image}, + {"Content-Type": "application/json"} + ).allowed == true + ) + message: >- + One or more container images reference an ECR repository that does + not have a compliant pull policy. Visit + https://wiki.dfds.cloud/en/playbooks/requirements/ECR + for more information. From da97484439af554bfafd3b1b6ae968789e073cab Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Tue, 30 Jun 2026 15:04:09 +0100 Subject: [PATCH 10/17] feat: attempt to warn when ecr check is not available --- .../hard-requirement/ecr-pull-policy.yaml | 108 ++++++++++++++++-- 1 file changed, 96 insertions(+), 12 deletions(-) diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml index 235b81e1..df1edc7a 100644 --- a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -14,9 +14,9 @@ metadata: spec: # Fail-open at the webhook level: if Kyverno (or the compliance service) is # unreachable or times out, the http.Post expression errors and the request - # is admitted rather than blocked. - # Note: this does not emit a a dedicated "admitted without verification" warning - # as it does not seem possible with the new CEL policies + # is admitted rather than blocked (the failure is still recorded as an `error` + # result in policy reports). This replaces the ClusterPolicy + # webhookConfiguration.failurePolicy: Ignore and apiCall.default fail-open. failurePolicy: Ignore # Without this, ValidatingPolicy only matches Pods, @@ -64,20 +64,104 @@ spec: object.spec.containers + object.spec.?initContainers.orValue([]) + object.spec.?ephemeralContainers.orValue([]) - - validations: - # Per-image POST to the compliance exporter. deny(allowed == false) is - # inverted to an assertion that every image is allowed. - - expression: >- - variables.allContainers.all(c, + # One compliance check per image. http.Post merges the decoded JSON body + # with a "statusCode" field, so r.statusCode, r.allowed and r.reason are + # all available. If any call errors at the transport level (service down, + # timeout, DNS), this whole expression throws and the request fails open + # via failurePolicy: Ignore. + - name: responses + expression: >- + variables.allContainers.map(c, http.Post( "http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check", {"image": c.image}, - {"Content-Type": "application/json"} - ).allowed == true - ) + {"Content-Type": "application/json"})) + + validations: + # Fail-open: only block when the service returned 200 AND said allowed:false. + # A non-200 response means we could not verify, so it passes here (the + # ecr-pull-availability policy surfaces those as warnings instead). + - expression: >- + variables.responses.all(r, r.statusCode != 200 || r.?allowed.orValue(true) == true) message: >- One or more container images reference an ECR repository that does not have a compliant pull policy. Visit https://wiki.dfds.cloud/en/playbooks/requirements/ECR for more information. +--- +apiVersion: policies.kyverno.io/v1 +kind: ValidatingPolicy +metadata: + name: ecr-pull-availability + annotations: + policies.kyverno.io/title: ECR Pull Compliance Service Availability + policies.kyverno.io/category: Requirement + policies.kyverno.io/severity: medium + policies.kyverno.io/subject: Pod + policies.kyverno.io/description: >- + Warns (never blocks) when the ECR pull compliance service answered with a + non-OK status or self-reported as degraded, meaning one or more images + were admitted without verification. True service outages (unreachable) + cannot be turned into a warning and instead surface as `error` results in + policy reports. +spec: + failurePolicy: Ignore + + autogen: + podControllers: + controllers: + - deployments + - statefulsets + - daemonsets + - jobs + - cronjobs + + webhookConfiguration: + timeoutSeconds: 10 + + evaluation: + background: + enabled: true + + # Audit records a report entry; Warn returns a user-facing admission warning. + validationActions: + - Audit + - Warn + + matchConstraints: + resourceRules: + - apiGroups: [''] + apiVersions: ['v1'] + operations: + - CREATE + - UPDATE + resources: + - pods + + matchConditions: + - name: exclude-system-namespaces + expression: "!(object.metadata.namespace in ['kube-system', 'kyverno'])" + + variables: + - name: allContainers + expression: >- + object.spec.containers + + object.spec.?initContainers.orValue([]) + + object.spec.?ephemeralContainers.orValue([]) + - name: responses + expression: >- + variables.allContainers.map(c, + http.Post( + "http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check", + {"image": c.image}, + {"Content-Type": "application/json"})) + + validations: + # Warn when the service answered but not with a clean 200, or when it + # self-reported as degraded. Passing means every image got a healthy 200. + - expression: >- + variables.responses.all(r, r.statusCode == 200 && r.?reason.orValue('') != 'check_unavailable') + message: >- + The ECR pull compliance service was unavailable or returned a non-OK + status; one or more container images were admitted without verification. + See https://wiki.dfds.cloud/en/playbooks/requirements/ECR From 21cbcd2678d2ba8000da8239c00f1a4c1e19feab Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Tue, 30 Jun 2026 15:12:26 +0100 Subject: [PATCH 11/17] Revert "feat: attempt to warn when ecr check is not available" This reverts commit da97484439af554bfafd3b1b6ae968789e073cab. --- .../hard-requirement/ecr-pull-policy.yaml | 108 ++---------------- 1 file changed, 12 insertions(+), 96 deletions(-) diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml index df1edc7a..235b81e1 100644 --- a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -14,9 +14,9 @@ metadata: spec: # Fail-open at the webhook level: if Kyverno (or the compliance service) is # unreachable or times out, the http.Post expression errors and the request - # is admitted rather than blocked (the failure is still recorded as an `error` - # result in policy reports). This replaces the ClusterPolicy - # webhookConfiguration.failurePolicy: Ignore and apiCall.default fail-open. + # is admitted rather than blocked. + # Note: this does not emit a a dedicated "admitted without verification" warning + # as it does not seem possible with the new CEL policies failurePolicy: Ignore # Without this, ValidatingPolicy only matches Pods, @@ -64,104 +64,20 @@ spec: object.spec.containers + object.spec.?initContainers.orValue([]) + object.spec.?ephemeralContainers.orValue([]) - # One compliance check per image. http.Post merges the decoded JSON body - # with a "statusCode" field, so r.statusCode, r.allowed and r.reason are - # all available. If any call errors at the transport level (service down, - # timeout, DNS), this whole expression throws and the request fails open - # via failurePolicy: Ignore. - - name: responses - expression: >- - variables.allContainers.map(c, - http.Post( - "http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check", - {"image": c.image}, - {"Content-Type": "application/json"})) validations: - # Fail-open: only block when the service returned 200 AND said allowed:false. - # A non-200 response means we could not verify, so it passes here (the - # ecr-pull-availability policy surfaces those as warnings instead). + # Per-image POST to the compliance exporter. deny(allowed == false) is + # inverted to an assertion that every image is allowed. - expression: >- - variables.responses.all(r, r.statusCode != 200 || r.?allowed.orValue(true) == true) + variables.allContainers.all(c, + http.Post( + "http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check", + {"image": c.image}, + {"Content-Type": "application/json"} + ).allowed == true + ) message: >- One or more container images reference an ECR repository that does not have a compliant pull policy. Visit https://wiki.dfds.cloud/en/playbooks/requirements/ECR for more information. ---- -apiVersion: policies.kyverno.io/v1 -kind: ValidatingPolicy -metadata: - name: ecr-pull-availability - annotations: - policies.kyverno.io/title: ECR Pull Compliance Service Availability - policies.kyverno.io/category: Requirement - policies.kyverno.io/severity: medium - policies.kyverno.io/subject: Pod - policies.kyverno.io/description: >- - Warns (never blocks) when the ECR pull compliance service answered with a - non-OK status or self-reported as degraded, meaning one or more images - were admitted without verification. True service outages (unreachable) - cannot be turned into a warning and instead surface as `error` results in - policy reports. -spec: - failurePolicy: Ignore - - autogen: - podControllers: - controllers: - - deployments - - statefulsets - - daemonsets - - jobs - - cronjobs - - webhookConfiguration: - timeoutSeconds: 10 - - evaluation: - background: - enabled: true - - # Audit records a report entry; Warn returns a user-facing admission warning. - validationActions: - - Audit - - Warn - - matchConstraints: - resourceRules: - - apiGroups: [''] - apiVersions: ['v1'] - operations: - - CREATE - - UPDATE - resources: - - pods - - matchConditions: - - name: exclude-system-namespaces - expression: "!(object.metadata.namespace in ['kube-system', 'kyverno'])" - - variables: - - name: allContainers - expression: >- - object.spec.containers - + object.spec.?initContainers.orValue([]) - + object.spec.?ephemeralContainers.orValue([]) - - name: responses - expression: >- - variables.allContainers.map(c, - http.Post( - "http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check", - {"image": c.image}, - {"Content-Type": "application/json"})) - - validations: - # Warn when the service answered but not with a clean 200, or when it - # self-reported as degraded. Passing means every image got a healthy 200. - - expression: >- - variables.responses.all(r, r.statusCode == 200 && r.?reason.orValue('') != 'check_unavailable') - message: >- - The ECR pull compliance service was unavailable or returned a non-OK - status; one or more container images were admitted without verification. - See https://wiki.dfds.cloud/en/playbooks/requirements/ECR From 28cf6103690165490b55f4985f7292deea338ba9 Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Tue, 30 Jun 2026 15:38:45 +0100 Subject: [PATCH 12/17] fix: add Warn --- apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml index 235b81e1..dd14ea20 100644 --- a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -37,9 +37,9 @@ spec: background: enabled: true - # Audit (warn only). Switch to [Deny] to start blocking non-compliant images. validationActions: - Audit + - Warn matchConstraints: resourceRules: From 6a59588060b3dc2aa29c8e152198a72f140b39ae Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Tue, 30 Jun 2026 15:54:25 +0100 Subject: [PATCH 13/17] fix: autogen not working properly in 1.17 --- .../policies/hard-requirement/ecr-pull-policy.yaml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml index dd14ea20..b347432e 100644 --- a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -19,17 +19,6 @@ spec: # as it does not seem possible with the new CEL policies failurePolicy: Ignore - # Without this, ValidatingPolicy only matches Pods, - # so a bad Deployment would be admitted and silently fail to create pods. - autogen: - podControllers: - controllers: - - deployments - - statefulsets - - daemonsets - - jobs - - cronjobs - webhookConfiguration: timeoutSeconds: 10 From c7d1593728c4a24daa5a3821d68de34f058f0b5d Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Wed, 1 Jul 2026 09:05:18 +0100 Subject: [PATCH 14/17] fix: add extra resources to constraints --- apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml index b347432e..e023f108 100644 --- a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -38,7 +38,11 @@ spec: - CREATE - UPDATE resources: - - pods + - deployments + - statefulsets + - daemonsets + - jobs + - cronjobs # Equivalent of the ClusterPolicy exclude on namespaces. matchConditions: From 4c5693feb2973cc706e34c8a1f1e0062c3c37d9c Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Wed, 1 Jul 2026 10:04:02 +0100 Subject: [PATCH 15/17] fix: amend apigroups and variable expression --- .../hard-requirement/ecr-pull-policy.yaml | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml index e023f108..aa00165b 100644 --- a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -6,7 +6,7 @@ metadata: policies.kyverno.io/title: ECR Pull Policy Compliance Check policies.kyverno.io/category: Requirement policies.kyverno.io/severity: medium - policies.kyverno.io/subject: Pod + policies.kyverno.io/subject: Deployment,StatefulSet,DaemonSet,Job,CronJob policies.kyverno.io/description: >- Validates that container images from org-owned ECR repositories have compliant pull policies that allow cross-account access within the @@ -32,7 +32,7 @@ spec: matchConstraints: resourceRules: - - apiGroups: [''] + - apiGroups: ['apps'] apiVersions: ['v1'] operations: - CREATE @@ -41,6 +41,12 @@ spec: - deployments - statefulsets - daemonsets + - apiGroups: ['batch'] + apiVersions: ['v1'] + operations: + - CREATE + - UPDATE + resources: - jobs - cronjobs @@ -50,13 +56,22 @@ spec: expression: "!(object.metadata.namespace in ['kube-system', 'kyverno'])" variables: - # Combine standard, init, and ephemeral containers into a single list, - # replacing the three separate foreach blocks. + # Resolve the pod spec depending on the workload kind: + # - CronJob nests under spec.jobTemplate.spec.template.spec + # - all other supported kinds (Deployment, StatefulSet, DaemonSet, Job) + # nest under spec.template.spec + - name: podSpec + expression: >- + object.kind == 'CronJob' + ? object.spec.jobTemplate.spec.template.spec + : object.spec.template.spec + + # Combine standard, init, and ephemeral containers into a single list. - name: allContainers expression: >- - object.spec.containers - + object.spec.?initContainers.orValue([]) - + object.spec.?ephemeralContainers.orValue([]) + variables.podSpec.containers + + variables.podSpec.?initContainers.orValue([]) + + variables.podSpec.?ephemeralContainers.orValue([]) validations: # Per-image POST to the compliance exporter. deny(allowed == false) is From 565cf7dd6f79910f6216b40d56e1887f985fba4d Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Wed, 1 Jul 2026 14:38:57 +0100 Subject: [PATCH 16/17] fix: attempt to list offending containers in message --- .../hard-requirement/ecr-pull-policy.yaml | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml index aa00165b..cb59e85e 100644 --- a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -40,7 +40,6 @@ spec: resources: - deployments - statefulsets - - daemonsets - apiGroups: ['batch'] apiVersions: ['v1'] operations: @@ -73,17 +72,30 @@ spec: + variables.podSpec.?initContainers.orValue([]) + variables.podSpec.?ephemeralContainers.orValue([]) - validations: - # Per-image POST to the compliance exporter. deny(allowed == false) is - # inverted to an assertion that every image is allowed. - - expression: >- - variables.allContainers.all(c, + # Per-image POST to the compliance exporter, keeping only the images that + # are not allowed. Reused by both the validation expression and the + # messageExpression so the compliance check runs once per container. + - name: offendingImages + expression: >- + variables.allContainers.filter(c, http.Post( "http://platform-requirements-ecr-pull-compliance-exporter.ssu-mgmt-dznts.svc.cluster.local:8080/api/v1/compliance/check", {"image": c.image}, {"Content-Type": "application/json"} - ).allowed == true - ) + ).allowed != true + ).map(c, c.image) + + validations: + # Assert that no container image is non-compliant. + - expression: >- + size(variables.offendingImages) == 0 + # messageExpression lists the offending image names. The folded scalar + # collapses newlines into spaces so the result is a single line (a + # multi-line result would make Kyverno fall back to the static message). + messageExpression: >- + "The following container image(s) reference an ECR repository that does + not have a compliant pull policy: " + variables.offendingImages.join(", ") + + ". Visit https://wiki.dfds.cloud/en/playbooks/requirements/ECR for more information." message: >- One or more container images reference an ECR repository that does not have a compliant pull policy. Visit From 7734ad4f893c2e8b239b0792ce0a1e8e36e77e85 Mon Sep 17 00:00:00 2001 From: rifisdfds Date: Wed, 1 Jul 2026 14:44:55 +0100 Subject: [PATCH 17/17] fix: also remove daemonset from annotation --- apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml index cb59e85e..5a823a26 100644 --- a/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml +++ b/apps/kyverno/policies/hard-requirement/ecr-pull-policy.yaml @@ -6,7 +6,7 @@ metadata: policies.kyverno.io/title: ECR Pull Policy Compliance Check policies.kyverno.io/category: Requirement policies.kyverno.io/severity: medium - policies.kyverno.io/subject: Deployment,StatefulSet,DaemonSet,Job,CronJob + policies.kyverno.io/subject: Deployment,StatefulSet,Job,CronJob policies.kyverno.io/description: >- Validates that container images from org-owned ECR repositories have compliant pull policies that allow cross-account access within the