From 16cde1eab510907f8c8a7b7d7c2aeff995c98e9a Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Mon, 23 Mar 2026 22:19:34 +0200 Subject: [PATCH] add Kyverno TLS secret sync + enhance renewal pipeline Kyverno ClusterPolicy clones tls-secret from kyverno namespace to all namespaces with synchronize=true. Renewal pipeline now updates the source secret via kubectl, verifies cert validity, and sends Slack notification. --- .woodpecker/renew-tls.yml | 29 ++++++++ .../modules/kyverno/tls-secret-sync.tf | 72 +++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 stacks/kyverno/modules/kyverno/tls-secret-sync.tf diff --git a/.woodpecker/renew-tls.yml b/.woodpecker/renew-tls.yml index d68360d4..6787bc00 100644 --- a/.woodpecker/renew-tls.yml +++ b/.woodpecker/renew-tls.yml @@ -43,3 +43,32 @@ steps: - "git commit -m 'Woodpecker CI Update TLS Certificates Commit' || echo 'No changes'" - "GIT_SSH_COMMAND='ssh -i ./secrets/deploy_key -o IdentitiesOnly=yes' git pull --rebase origin master" - "GIT_SSH_COMMAND='ssh -i ./secrets/deploy_key -o IdentitiesOnly=yes' git push origin master" + + - name: verify-cert + image: alpine + commands: + - "apk update && apk add openssl" + - "openssl x509 -checkend 604800 -noout -in secrets/fullchain.pem" + - "echo 'Certificate is valid for at least 7 more days'" + + - name: update-tls-source-secret + image: bitnami/kubectl:latest + commands: + - | + kubectl create secret tls tls-secret \ + --cert=secrets/fullchain.pem --key=secrets/privkey.pem \ + --namespace=kyverno --dry-run=client -o yaml | kubectl apply -f - + - "echo 'Source TLS secret updated in kyverno namespace — Kyverno will sync to all namespaces'" + + - name: slack + image: curlimages/curl + commands: + - | + curl -s -X POST -H 'Content-type: application/json' \ + --data "{\"channel\":\"general\",\"text\":\"Woodpecker CI: TLS certificate renewal ${CI_PIPELINE_STATUS}\"}" \ + "$SLACK_WEBHOOK" || true + environment: + SLACK_WEBHOOK: + from_secret: slack_webhook + when: + status: [success, failure] diff --git a/stacks/kyverno/modules/kyverno/tls-secret-sync.tf b/stacks/kyverno/modules/kyverno/tls-secret-sync.tf new file mode 100644 index 00000000..e7cab3df --- /dev/null +++ b/stacks/kyverno/modules/kyverno/tls-secret-sync.tf @@ -0,0 +1,72 @@ + +# ============================================================================= +# TLS Certificate — Auto-sync to all namespaces +# ============================================================================= +# Source wildcard cert (*.viktorbarzin.me) in kyverno namespace, cloned by +# ClusterPolicy into every NS. Renewal pipeline updates this source secret, +# Kyverno propagates to all namespaces within seconds. + +resource "kubernetes_secret" "tls_secret" { + metadata { + name = "tls-secret" + namespace = kubernetes_namespace.kyverno.metadata[0].name + } + type = "kubernetes.io/tls" + data = { + "tls.crt" = file("${path.root}/secrets/fullchain.pem") + "tls.key" = file("${path.root}/secrets/privkey.pem") + } +} + +resource "kubernetes_manifest" "sync_tls_secret" { + manifest = { + apiVersion = "kyverno.io/v1" + kind = "ClusterPolicy" + metadata = { + name = "sync-tls-secret" + } + spec = { + rules = [ + { + name = "sync-tls-secret" + match = { + any = [ + { + resources = { + kinds = ["Namespace"] + } + } + ] + } + exclude = { + any = [ + { + resources = { + namespaces = ["kube-system", "kube-public", "kube-node-lease"] + } + } + ] + } + generate = { + apiVersion = "v1" + kind = "Secret" + name = "tls-secret" + namespace = "{{request.object.metadata.name}}" + synchronize = true + clone = { + namespace = "kyverno" + name = "tls-secret" + } + } + } + ] + } + } + + depends_on = [ + helm_release.kyverno, + kubernetes_secret.tls_secret, + kubernetes_cluster_role_binding.kyverno_admission_secret_manager, + kubernetes_cluster_role_binding.kyverno_background_secret_manager, + ] +}