From b034d99655daf9211c841e5be6cf90c4d2764038 Mon Sep 17 00:00:00 2001 From: Lukas Voetmand Date: Mon, 22 Jul 2024 17:31:04 +0200 Subject: [PATCH 1/5] feat: migrate CA secret to new namespace (#476) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: migrate CA secret to new namespace * fix: add helm hook annotations to prevent race condition * fix: separated secret migration RBAC * fix: added shebang to fix shellcheck complaint * Update deploy/helm/secret-operator/templates/secret_migration_job.yaml Co-authored-by: Natalie Klestrup Röijezon * Update deploy/helm/secret-operator/templates/secret_migration_job.yaml Co-authored-by: Natalie Klestrup Röijezon * Update deploy/helm/secret-operator/templates/secret_migration_job.yaml Co-authored-by: Natalie Klestrup Röijezon * Update deploy/helm/secret-operator/templates/secret_migration_job.yaml Co-authored-by: Natalie Klestrup Röijezon * Update deploy/helm/secret-operator/templates/secret_migration_job.yaml Co-authored-by: Natalie Klestrup Röijezon * fix: copy paste error * fix: merged migration resoures into one file * fix: unmerged resources / added hook weights instead to guarantee order --------- Co-authored-by: Natalie Klestrup Röijezon --- .../templates/secret_migration_job.yaml | 55 ++++++++++++++++++ .../templates/secret_migration_rbac.yaml | 56 +++++++++++++++++++ deploy/helm/secret-operator/values.yaml | 12 ++++ 3 files changed, 123 insertions(+) create mode 100644 deploy/helm/secret-operator/templates/secret_migration_job.yaml create mode 100644 deploy/helm/secret-operator/templates/secret_migration_rbac.yaml diff --git a/deploy/helm/secret-operator/templates/secret_migration_job.yaml b/deploy/helm/secret-operator/templates/secret_migration_job.yaml new file mode 100644 index 00000000..525c7613 --- /dev/null +++ b/deploy/helm/secret-operator/templates/secret_migration_job.yaml @@ -0,0 +1,55 @@ +--- +# Migrates the TLS CA keypair from the hard-coded default namespace to the operator namespace +# See https://github.com/stackabletech/secret-operator/issues/453 +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "operator.fullname" . }}-secret-migration + annotations: + "helm.sh/hook": pre-install + "helm.sh/hook-delete-policy": hook-succeeded + "helm.sh/hook-weight": "-5" + labels: + {{- include "operator.labels" . | nindent 4 }} +spec: + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "operator.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.image.pullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "operator.fullname" . }}-secret-migration-serviceaccount + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: migrate-secret + image: "{{ .Values.secretMigrationJob.image.repository }}:1.0.0-stackable{{ .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.secretMigrationJob.image.pullPolicy }} + resources: + {{ .Values.secretMigrationJob.resources | toYaml | nindent 12 }} + command: ["bash", "-c"] + args: + - | + #!/bin/bash + set -euo pipefail + SOURCE_NAMESPACE=default + TARGET_NAMESPACE={{ .Values.secretClasses.tls.caSecretNamespace | default .Release.Namespace }} + + # only continue if secret exists + if source_ca_secret="$(kubectl get secret -n $SOURCE_NAMESPACE secret-provisioner-tls-ca -o json)"; then + echo "secret exists in namespace $SOURCE_NAMESPACE" + # only continue if secret in target namespace does NOT exist + if ! kubectl get secret -n $TARGET_NAMESPACE secret-provisioner-tls-ca; then + echo "secret does not exist in namespace $TARGET_NAMESPACE" + # copy secret from default to {{ .Values.secretClasses.tls.caSecretNamespace | default .Release.Namespace }} + echo "$source_ca_secret" | jq 'del(.metadata["namespace","creationTimestamp","resourceVersion","selfLink","uid"])' | kubectl apply -n $TARGET_NAMESPACE -f - + fi + fi + restartPolicy: Never \ No newline at end of file diff --git a/deploy/helm/secret-operator/templates/secret_migration_rbac.yaml b/deploy/helm/secret-operator/templates/secret_migration_rbac.yaml new file mode 100644 index 00000000..d4a462d1 --- /dev/null +++ b/deploy/helm/secret-operator/templates/secret_migration_rbac.yaml @@ -0,0 +1,56 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "operator.fullname" . }}-secret-migration-serviceaccount + labels: + {{- include "operator.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": pre-install + "helm.sh/hook-delete-policy": hook-succeeded + "helm.sh/hook-weight": "-10" + {{- with .Values.serviceAccount.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "operator.fullname" . }}-secret-migration-clusterrolebinding + annotations: + "helm.sh/hook": pre-install + "helm.sh/hook-delete-policy": hook-succeeded + "helm.sh/hook-weight": "-10" + labels: + {{- include "operator.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ include "operator.fullname" . }}-secret-migration-serviceaccount + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ include "operator.fullname" . }}-secret-migration-clusterrole + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "operator.fullname" . }}-secret-migration-clusterrole + annotations: + "helm.sh/hook": pre-install + "helm.sh/hook-delete-policy": hook-succeeded + "helm.sh/hook-weight": "-10" + labels: + {{- include "operator.labels" . | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch + - create + - patch + - update \ No newline at end of file diff --git a/deploy/helm/secret-operator/values.yaml b/deploy/helm/secret-operator/values.yaml index 574274be..e723249c 100644 --- a/deploy/helm/secret-operator/values.yaml +++ b/deploy/helm/secret-operator/values.yaml @@ -5,6 +5,18 @@ image: pullPolicy: IfNotPresent pullSecrets: [] +secretMigrationJob: + image: + repository: docker.stackable.tech/stackable/tools + pullPolicy: IfNotPresent + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 100m + memory: 128Mi + csiProvisioner: image: repository: docker.stackable.tech/k8s/sig-storage/csi-provisioner From eb757efbc9db83f829ef930686dfb9014a6e46aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natalie=20Klestrup=20R=C3=B6ijezon?= Date: Mon, 22 Jul 2024 18:46:05 +0200 Subject: [PATCH 2/5] Document how to use with Cert-Manager (#475) * Document how to use with Cert-Manager Fixes #473 * Update docs/modules/secret-operator/pages/cert-manager.adoc Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> * Update docs/modules/secret-operator/pages/cert-manager.adoc Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> * Drop mention of node scope --------- Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> --- .../examples/cert-manager/certificate.yaml | 16 ++++ .../examples/cert-manager/issuer.yaml | 28 +++++++ .../examples/cert-manager/pod.yaml | 71 ++++++++++++++++ .../examples/cert-manager/secretclass.yaml | 10 +++ .../secret-operator/pages/cert-manager.adoc | 82 +++++++++++++++++++ .../modules/secret-operator/partials/nav.adoc | 2 + 6 files changed, 209 insertions(+) create mode 100644 docs/modules/secret-operator/examples/cert-manager/certificate.yaml create mode 100644 docs/modules/secret-operator/examples/cert-manager/issuer.yaml create mode 100644 docs/modules/secret-operator/examples/cert-manager/pod.yaml create mode 100644 docs/modules/secret-operator/examples/cert-manager/secretclass.yaml create mode 100644 docs/modules/secret-operator/pages/cert-manager.adoc diff --git a/docs/modules/secret-operator/examples/cert-manager/certificate.yaml b/docs/modules/secret-operator/examples/cert-manager/certificate.yaml new file mode 100644 index 00000000..0e838091 --- /dev/null +++ b/docs/modules/secret-operator/examples/cert-manager/certificate.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: my-app-tls # <1> +spec: + secretName: my-app-tls # <2> + secretTemplate: + labels: + secrets.stackable.tech/class: tls-cert-manager # <3> + secrets.stackable.tech/service: my-app # <4> + dnsNames: + - my-app # <5> + issuerRef: + kind: Issuer + name: secret-operator-demonstration # <6> diff --git a/docs/modules/secret-operator/examples/cert-manager/issuer.yaml b/docs/modules/secret-operator/examples/cert-manager/issuer.yaml new file mode 100644 index 00000000..67e8fa14 --- /dev/null +++ b/docs/modules/secret-operator/examples/cert-manager/issuer.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: secret-operator-demonstration # <1> +spec: + ca: + secretName: secret-operator-demonstration-ca +# Create a self-signed CA for secret-operator-demonstration to use +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: secret-operator-demonstration-ca +spec: + secretName: secret-operator-demonstration-ca + isCA: true + commonName: Stackable Secret Operator/Cert-Manager Demonstration CA + issuerRef: + kind: Issuer + name: secret-operator-demonstration-ca +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: secret-operator-demonstration-ca +spec: + selfSigned: {} diff --git a/docs/modules/secret-operator/examples/cert-manager/pod.yaml b/docs/modules/secret-operator/examples/cert-manager/pod.yaml new file mode 100644 index 00000000..3011b752 --- /dev/null +++ b/docs/modules/secret-operator/examples/cert-manager/pod.yaml @@ -0,0 +1,71 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-app +spec: + replicas: 1 + selector: + matchLabels: + app: my-app + template: + metadata: + labels: + app: my-app + spec: + containers: + - name: nginx + image: nginx + volumeMounts: + - name: tls + mountPath: /tls + - name: config + mountPath: /etc/nginx/conf.d + ports: + - name: https + containerPort: 443 + volumes: + - name: tls # <1> + ephemeral: + volumeClaimTemplate: + metadata: + annotations: + secrets.stackable.tech/class: tls-cert-manager # <2> + secrets.stackable.tech/scope: service=my-app # <3> + spec: + storageClassName: secrets.stackable.tech + accessModes: + - ReadWriteOnce + resources: + requests: + storage: "1" + - name: config + configMap: + name: my-app +--- # <4> +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-app +data: + default.conf: | + server { + listen 443 ssl; + ssl_certificate /tls/tls.crt; + ssl_certificate_key /tls/tls.key; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + } +--- # <5> +apiVersion: v1 +kind: Service +metadata: + name: my-app +spec: + selector: + app: my-app + ports: + - name: https + port: 443 diff --git a/docs/modules/secret-operator/examples/cert-manager/secretclass.yaml b/docs/modules/secret-operator/examples/cert-manager/secretclass.yaml new file mode 100644 index 00000000..cb8ef427 --- /dev/null +++ b/docs/modules/secret-operator/examples/cert-manager/secretclass.yaml @@ -0,0 +1,10 @@ +--- +apiVersion: secrets.stackable.tech/v1alpha1 +kind: SecretClass +metadata: + name: tls-cert-manager # <1> +spec: + backend: + k8sSearch: + searchNamespace: + pod: {} # <2> diff --git a/docs/modules/secret-operator/pages/cert-manager.adoc b/docs/modules/secret-operator/pages/cert-manager.adoc new file mode 100644 index 00000000..a03d7ae6 --- /dev/null +++ b/docs/modules/secret-operator/pages/cert-manager.adoc @@ -0,0 +1,82 @@ += Cert-Manager Integration + +https://cert-manager.io/[Cert-Manager] is a common tool to manage certificates in Kubernetes, especially when backed by an external +Certificate Authority (CA) such as https://letsencrypt.org/[Let\'s Encrypt]. + +The Stackable Secret Operator does not currently support managing Cert-Manager certificates directly, but it can be configured to consume certificates generated by it. + +[#caveats] +== Caveats + +Cert-Manager is designed to manage relatively long-lived certificates that are stored in Kubernetes Secrets. By contrast, +the Stackable Secret Operator is designed to generate temporary short-lived certificates. + +This has a couple of repercussions: + +- Longer-lived certificates mean that a leaked certificate has potential to be abused for longer. +- Application teams may have access to read Secrets in their respective applications' Namespaces. + +Where possible, we recommend using the xref:secretclass.adoc#backend-autotls[`autoTls` backend] instead. + +[#issuer] +== Configuring Cert-Manager + +NOTE: We recommend using the xref:secretclass.adoc#backend-autotls[`autoTls` backend] instead for self-signed PKIs. We use Cert-Manager's CA issuer here to show the broader concepts. + +To do this, you will first need to teach Cert-Manager how to create your certificates. + +In a production setup this will likely use an external CA such as ACME or OpenBao/Vault. However, to make this guide self-contained, Cert-Manager will create +a self-signed CA certificate instead. + +[source,yaml] +---- +include::example$cert-manager/issuer.yaml[] +---- +<1> This is the Issuer that our created certificates will reference later + +[#secretclass] +== Creating a SecretClass + +The Stackable Secret Operator needs to know how to find the certificates created by Cert-Manager. We do this by creating +a xref:secretclass.adoc[] using the xref:secretclass.adoc#backend-k8ssearch[`k8sSearch` backend], which can find arbitrary +Kubernetes Secret objects that have the correct labels. + +[source,yaml] +---- +include::example$cert-manager/secretclass.yaml[] +---- +<1> Both certificates and Pods will reference this name, to ensure that the correct certificates are found +<2> This informs the Secret Operator that certificates will be found in the same namespace as the Pod using it + +[#certificate] +== Requesting a certificate + +You can now use Cert-Manager to provision your first certificate. Use labels to inform the Stackable Secret Operator +about which xref:scope.adoc[scopes] the certificate fulfills. Which scopes must be provisioned is going to depend +on the design of the workload. This guide assumes the xref:scope.adoc#service[service] scope. + +[source,yaml] +---- +include::example$cert-manager/certificate.yaml[] +---- +<1> The Certificate name is irrelevant for the Stackable Secret Operator's, but must be unique (within the Namespace) +<2> The Secret name must also be unique within the Namespace +<3> This tells the Stackable Secret Operator that this secret corresponds to the SecretClass created xref:#secretclass[before] +<4> This secret fulfils the xref:scope.adoc#service[service] scope for `my-app` +<5> The list of DNS names that this certificate should apply to. +<6> The Cert-Manager Issuer that should sign this certificate, as created xref:#issuer[before] + +[#pod] +== Using the certificate + +Finally, we can create and expose a Pod that consumes the certificate! + +[source,yaml] +---- +include::example$cert-manager/pod.yaml[] +---- +<1> A secret xref:volume.adoc[volume] is created, where the certificate will be exposed to the app +<2> The volume references the SecretClass defined xref:#secretclass[before] +<3> The app is designated the scope xref:scope#service[`service=my-app`], matching the xref:#certificate[certificate's scope] +<4> nginx is configured to use the mounted certificate +<5> nginx is exposed as a Kubernetes Service diff --git a/docs/modules/secret-operator/partials/nav.adoc b/docs/modules/secret-operator/partials/nav.adoc index b9df9d2a..7ff84ca7 100644 --- a/docs/modules/secret-operator/partials/nav.adoc +++ b/docs/modules/secret-operator/partials/nav.adoc @@ -6,6 +6,8 @@ ** xref:secret-operator:secretclass.adoc[] ** xref:secret-operator:scope.adoc[] ** xref:secret-operator:volume.adoc[] +* Guides +** xref:secret-operator:cert-manager.adoc[] * xref:secret-operator:security.adoc[] * xref:secret-operator:reference/index.adoc[] ** xref:secret-operator:reference/crds.adoc[] From 4f0925a6079aded6738d2455c7751c8ab02b4a36 Mon Sep 17 00:00:00 2001 From: Lukas Voetmand Date: Tue, 23 Jul 2024 14:18:24 +0200 Subject: [PATCH 3/5] fix: always use the 24.7.0 version of the tools image because testing PR artifacts breaks otherwise (#480) --- deploy/helm/secret-operator/templates/secret_migration_job.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/helm/secret-operator/templates/secret_migration_job.yaml b/deploy/helm/secret-operator/templates/secret_migration_job.yaml index 525c7613..09f46c5d 100644 --- a/deploy/helm/secret-operator/templates/secret_migration_job.yaml +++ b/deploy/helm/secret-operator/templates/secret_migration_job.yaml @@ -30,7 +30,7 @@ spec: {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: - name: migrate-secret - image: "{{ .Values.secretMigrationJob.image.repository }}:1.0.0-stackable{{ .Chart.AppVersion }}" + image: "{{ .Values.secretMigrationJob.image.repository }}:1.0.0-stackable24.7.0" imagePullPolicy: {{ .Values.secretMigrationJob.image.pullPolicy }} resources: {{ .Values.secretMigrationJob.resources | toYaml | nindent 12 }} From 6c4b4d17dab57745a00f7439265274fa219d2160 Mon Sep 17 00:00:00 2001 From: Stacky McStackface <95074132+stackable-bot@users.noreply.github.com> Date: Wed, 24 Jul 2024 12:40:03 +0200 Subject: [PATCH 4/5] Generated commit to update templated files based on rev 09389cb in stackabletech/operator-templating repo. (#484) Triggered by: Manual run triggered by: Maleware with message [Updating release pipeline to only build helmcharts once] --- .github/pull_request_template.md | 4 ++-- .github/workflows/build.yml | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 68133283..034d32d3 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -11,7 +11,7 @@ # Author - [ ] Changes are OpenShift compatible - [ ] CRD changes approved -- [ ] CRD documentation for all fields, following the [style guide](https://docs.stackable.tech/home/nightly/contributor/docs-style-guide). +- [ ] CRD documentation for all fields, following the [style guide](https://docs.stackable.tech/home/nightly/contributor/docs/style-guide). - [ ] Helm chart can be installed and deployed operator works - [ ] Integration tests passed (for non trivial changes) - [ ] Changes need to be "offline" compatible @@ -22,7 +22,7 @@ - [ ] Code contains useful comments - [ ] Code contains useful logging statements - [ ] (Integration-)Test cases added -- [ ] Documentation added or updated. Follows the [style guide](https://docs.stackable.tech/home/nightly/contributor/docs-style-guide). +- [ ] Documentation added or updated. Follows the [style guide](https://docs.stackable.tech/home/nightly/contributor/docs/style-guide). - [ ] Changelog updated - [ ] Cargo.toml only contains references to git tags (not specific commits or branches) ``` diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bf0c4686..c779094d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -368,7 +368,13 @@ jobs: make -e build - name: Publish Docker image and Helm chart if: ${{ !github.event.pull_request.head.repo.fork }} - run: make -e publish + run: | + # We want to publish helmcharts only once as they have a common name, while still publishing both images with architecture specific tags + if [ "$(uname -m)" = "x86_64" ]; then + make -e publish + else + make -e docker-publish + fi # Output the name of the published image to the Job output for later use - id: printtag name: Output image name and tag From 4c324c73a0406e037a1d2061f7f642b1c6a5cd9b Mon Sep 17 00:00:00 2001 From: Nick <10092581+NickLarsenNZ@users.noreply.github.com> Date: Thu, 25 Jul 2024 00:22:05 +1200 Subject: [PATCH 5/5] Update CHANGELOG.md from release 24.7.0 (#485) --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07fdb5b3..a0d7d163 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [24.7.0] - 2024-07-24 + ### Added - The associated configuration is now logged for each issued secret ([#413]).