feat: add Kyverno admission controller + cosign image verification
All checks were successful
AI Review / AI Code Review (pull_request) Successful in 1s
PR Checks / Validate & Security Scan (pull_request) Successful in 7s

- Deploy Kyverno v1.13.4 (chart 3.3.4) via ArgoCD Helm chart
- Add ClusterPolicy to verify cosign signatures on registry images (Audit mode)
- Add NetworkPolicy for kyverno namespace (default-deny + selective allow)
- Extend keycloak-secrets-manager RBAC to kyverno namespace for cosign key sync
- ArgoCD Application for kyverno-policies directory
This commit is contained in:
root 2026-02-18 06:06:07 +01:00
parent efe1aba2c4
commit 4188d1dd6f
5 changed files with 248 additions and 0 deletions

View File

@ -0,0 +1,104 @@
# Kyverno NetworkPolicies
# Default deny + selective allow for kyverno namespace
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: kyverno
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# Allow DNS egress (all kyverno pods need DNS)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: kyverno
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
---
# Admission controller: receives webhook calls from K8s API, needs K8s API + registry
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-kyverno-admission
namespace: kyverno
spec:
podSelector:
matchLabels:
app.kubernetes.io/component: admission-controller
policyTypes:
- Ingress
- Egress
ingress:
# K8s API calls webhook on port 9443
- ports:
- port: 9443
protocol: TCP
egress:
# K8s API server
- ports:
- port: 6443
protocol: TCP
# Gitea registry (verify image signatures)
- to:
- ipBlock:
cidr: 10.10.10.1/32
ports:
- port: 3000
protocol: TCP
---
# Background controller: K8s API + registry
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-kyverno-background
namespace: kyverno
spec:
podSelector:
matchLabels:
app.kubernetes.io/component: background-controller
policyTypes:
- Egress
egress:
# K8s API server
- ports:
- port: 6443
protocol: TCP
# Gitea registry
- to:
- ipBlock:
cidr: 10.10.10.1/32
ports:
- port: 3000
protocol: TCP
---
# Cleanup controller: K8s API only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-kyverno-cleanup
namespace: kyverno
spec:
podSelector:
matchLabels:
app.kubernetes.io/component: cleanup-controller
policyTypes:
- Egress
egress:
# K8s API server
- ports:
- port: 6443
protocol: TCP

View File

@ -0,0 +1,39 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signatures
annotations:
policies.kyverno.io/title: Verify Image Signatures
policies.kyverno.io/description: >-
Verifies that container images from our Gitea registry are signed
with cosign. Unsigned images are blocked in Enforce mode.
policies.kyverno.io/category: Supply Chain Security
policies.kyverno.io/severity: high
spec:
validationFailureAction: Audit
background: true
webhookTimeoutSeconds: 30
failurePolicy: Ignore
rules:
- name: verify-gitea-registry-images
match:
any:
- resources:
kinds:
- Pod
namespaces:
- dev
- staging
- prod
verifyImages:
- imageReferences:
- "10.10.10.1:3000/claude/*"
attestors:
- entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEh5WuZfakVC/MApgZIfU4MYoKtbWq
ekPSqfXznycT1OBUIdHQ7O4tpXwSppEiruJ7gV8+iexSRqGQOWL9y4dK3A==
-----END PUBLIC KEY-----
required: true

View File

@ -85,3 +85,28 @@ subjects:
- kind: ServiceAccount
name: keycloak-secrets-manager
namespace: kube-system
---
# Secrets management in kyverno namespace (cosign public key)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: keycloak-secrets-manager
namespace: kyverno
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: keycloak-secrets-manager
namespace: kyverno
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: keycloak-secrets-manager
subjects:
- kind: ServiceAccount
name: keycloak-secrets-manager
namespace: kube-system

View File

@ -0,0 +1,21 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: kyverno-policies
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: http://10.10.10.1:3000/claude/k8s-apps.git
targetRevision: main
path: apps/kyverno-policies
destination:
server: https://kubernetes.default.svc
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=false

59
argocd-apps/kyverno.yaml Normal file
View File

@ -0,0 +1,59 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: kyverno
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
chart: kyverno
repoURL: https://kyverno.github.io/kyverno/
targetRevision: "3.3.4"
helm:
values: |
admissionController:
replicas: 1
container:
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
serviceMonitor:
enabled: false
backgroundController:
enabled: true
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
cleanupController:
enabled: true
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
reportsController:
enabled: false
features:
registryClient:
allowInsecure: true
destination:
server: https://kubernetes.default.svc
namespace: kyverno
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true