From 19e9b07e6e2adabffc570a00724660c34ee5a39f Mon Sep 17 00:00:00 2001 From: justusbunsi Date: Wed, 18 Jan 2023 00:58:10 +0800 Subject: [PATCH] Re-add GPG configuration feature (#374) This reverts d5ce1a47eaad935ea709b7a03bfdf6c69bac8f32 and therefore adds the GPG feature back into main. As it is a breaking change, this PR now also contains the required upgrade notes. Closes #107 again. Co-authored-by: justusbunsi Reviewed-on: https://gitea.com/gitea/helm-chart/pulls/374 Reviewed-by: pat-s Reviewed-by: John Olheiser --- .drone.yml | 9 ++ .gitignore | 1 + .helmignore | 3 +- CONTRIBUTING.md | 10 ++ Makefile | 4 + README.md | 78 +++++++++++----- templates/_helpers.tpl | 4 + templates/gitea/gpg-secret.yaml | 16 ++++ templates/gitea/init.yaml | 13 +++ templates/gitea/statefulset.yaml | 43 +++++++++ unittests/gpg-secret/signing-disabled.yaml | 13 +++ unittests/gpg-secret/signing-enabled.yaml | 40 ++++++++ unittests/init/basic.yaml | 15 +++ .../init/init_directory_structure.sh.yaml | 64 +++++++++++++ unittests/statefulset/basic.yaml | 17 ++++ unittests/statefulset/signing-disabled.yaml | 40 ++++++++ unittests/statefulset/signing-enabled.yaml | 93 +++++++++++++++++++ values.yaml | 8 ++ 18 files changed, 448 insertions(+), 23 deletions(-) create mode 100644 templates/gitea/gpg-secret.yaml create mode 100644 unittests/gpg-secret/signing-disabled.yaml create mode 100644 unittests/gpg-secret/signing-enabled.yaml create mode 100644 unittests/init/basic.yaml create mode 100644 unittests/init/init_directory_structure.sh.yaml create mode 100644 unittests/statefulset/basic.yaml create mode 100644 unittests/statefulset/signing-disabled.yaml create mode 100644 unittests/statefulset/signing-enabled.yaml diff --git a/.drone.yml b/.drone.yml index 972cee4..4f78db2 100644 --- a/.drone.yml +++ b/.drone.yml @@ -23,6 +23,15 @@ steps: - helm dependency update - helm template --debug gitea-helm . +- name: helm unittests + pull: always + image: alpine:3.17 + commands: + - apk add --no-cache -X http://dl-cdn.alpinelinux.org/alpine/edge/testing make helm git bash + - helm plugin install https://github.com/heyhabito/helm-unittest + - helm dependency update + - make unittests + - name: verify readme pull: always image: alpine:3.17 diff --git a/.gitignore b/.gitignore index 22b7fa6..10261af 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ charts/ node_modules/ .DS_Store +unittests/*/__snapshot__/ diff --git a/.helmignore b/.helmignore index fe6af29..e608c23 100644 --- a/.helmignore +++ b/.helmignore @@ -29,4 +29,5 @@ Makefile .markdownlintignore .markdownlint.yaml .drone.yml -CONTRIBUTING.md \ No newline at end of file +CONTRIBUTING.md +unittests/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d06973c..78f77d9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,3 +50,13 @@ be used: forwarded first from `minikube` to localhost first via `kubectl --namespace default port-forward svc/gitea-http 3000:3000`. Now Gitea is accessible at [http://localhost:3000](http://localhost:3000). + +### Unit tests + +```bash +# install the unittest plugin +$ helm plugin install https://github.com/heyhabito/helm-unittest + +# run the unittests +make unittests +``` diff --git a/Makefile b/Makefile index 720a657..2b61849 100644 --- a/Makefile +++ b/Makefile @@ -6,3 +6,7 @@ prepare-environment: readme: prepare-environment npm run readme:parameters npm run readme:lint + +.PHONY: unittests +unittests: + helm unittest --helm3 --strict -f 'unittests/**/*.yaml' ./ diff --git a/README.md b/README.md index 2fc73f7..a5c1ce6 100644 --- a/README.md +++ b/README.md @@ -41,24 +41,6 @@ of this document for major and breaking changes. - Helm 3.0+ - PV provisioner for persistent data support -## Configure Commit Signing - -When using the rootless image the gpg key folder was is not persistent by -default. If you consider using signed commits for internal Gitea activities -(e.g. initial commit), you'd need to provide a signing key. Prior to -[PR186](https://gitea.com/gitea/helm-chart/pulls/186), imported keys had to be -re-imported once the container got replaced by another. - -The mentioned PR introduced a new configuration object `signing` allowing you to -configure prerequisites for commit signing. By default this section is disabled -to maintain backwards compatibility. - -```yaml -signing: - enabled: false - gpgHome: /data/git/.gnupg -``` - ## Examples ### Gitea Configuration @@ -525,6 +507,49 @@ gitea: ... ``` +## Configure commit signing + +When using the rootless image the gpg key folder is not persistent by +default. If you consider using signed commits for internal Gitea activities +(e.g. initial commit), you'd need to provide a signing key. Prior to +[PR186](https://gitea.com/gitea/helm-chart/pulls/186), imported keys had to be +re-imported once the container got replaced by another. + +The mentioned PR introduced a new configuration object `signing` allowing you to +configure prerequisites for commit signing. By default this section is disabled +to maintain backwards compatibility. + +```yaml +signing: + enabled: false + gpgHome: /data/git/.gnupg +``` + +Regardless of the used container image the `signing` object allows to specify a +private gpg key. Either using the `signing.privateKey` to define the key inline, +or refer to an existing secret containing the key data by using `signing.existingKey`. + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: custom-gitea-gpg-key +type: Opaque +stringData: + privateKey: |- + -----BEGIN PGP PRIVATE KEY BLOCK----- + ... + -----END PGP PRIVATE KEY BLOCK----- +``` + +```yaml +signing: + existingSecret: custom-gitea-gpg-key +``` + +To use the gpg key, Gitea needs to be configured accordingly. A detailed description +can be found in the [official Gitea documentation](https://docs.gitea.io/en-us/signing/#general-configuration). + ### Metrics and profiling A Prometheus `/metrics` endpoint on the `HTTP_PORT` and `pprof` profiling @@ -669,10 +694,12 @@ gitea: ### Signing -| Name | Description | Value | -| ----------------- | ---------------------------- | ------------------ | -| `signing.enabled` | Enable commit/action signing | `false` | -| `signing.gpgHome` | GPG home directory | `/data/git/.gnupg` | +| Name | Description | Value | +| ------------------------ | ----------------------------------------------------------------- | ------------------ | +| `signing.enabled` | Enable commit/action signing | `false` | +| `signing.gpgHome` | GPG home directory | `/data/git/.gnupg` | +| `signing.privateKey` | Inline private gpg key for signed Gitea actions | `""` | +| `signing.existingSecret` | Use an existing secret to store the value of `signing.privateKey` | `""` | ### Gitea @@ -786,6 +813,13 @@ See [CONTRIBUTORS GUIDE](CONTRIBUTING.md) for details. This section lists major and breaking changes of each Helm Chart version. Please read them carefully to upgrade successfully. +### To 7.0.0 + +#### Private GPG key configuration for Gitea signing actions + +Having `signing.enabled=true` now requires to use either `signing.privateKey` or `signing.existingSecret` so that the Chart can automatically prepare the GPG key for Gitea internal signing actions. +See [Configure commit signing](#configure-commit-signing) for details. + ### To 6.0.0 #### Different volume mounts for init-containers and runtime container diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 0e481e0..5bdcca9 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -331,3 +331,7 @@ https {{- toYaml .Values.extraVolumeMounts -}} {{- end -}} {{- end -}} + +{{- define "gitea.gpg-key-secret-name" -}} +{{ default (printf "%s-gpg-key" (include "gitea.fullname" .)) .Values.signing.existingSecret }} +{{- end -}} diff --git a/templates/gitea/gpg-secret.yaml b/templates/gitea/gpg-secret.yaml new file mode 100644 index 0000000..29b6d4f --- /dev/null +++ b/templates/gitea/gpg-secret.yaml @@ -0,0 +1,16 @@ +{{- if .Values.signing.enabled -}} +{{- if and (empty .Values.signing.privateKey) (empty .Values.signing.existingSecret) -}} + {{- fail "Either specify `signing.privateKey` or `signing.existingKey`" -}} +{{- end }} +{{- if and (not (empty .Values.signing.privateKey)) (empty .Values.signing.existingSecret) -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "gitea.gpg-key-secret-name" . }} + labels: + {{- include "gitea.labels" . | nindent 4 }} +type: Opaque +data: + privateKey: {{ .Values.signing.privateKey | b64enc }} +{{- end }} +{{- end }} diff --git a/templates/gitea/init.yaml b/templates/gitea/init.yaml index 8ea3aa9..838460b 100644 --- a/templates/gitea/init.yaml +++ b/templates/gitea/init.yaml @@ -6,6 +6,11 @@ metadata: {{- include "gitea.labels" . | nindent 4 }} type: Opaque stringData: + configure_gpg_environment.sh: |- + #!/usr/bin/env bash + set -eu + + gpg --batch --import /raw/private.asc init_directory_structure.sh: |- #!/usr/bin/env bash @@ -35,6 +40,14 @@ stringData: {{- end }} chmod ug+rwx "${GITEA_TEMP}" + {{ if .Values.signing.enabled -}} + if [ ! -d "${GNUPGHOME}" ]; then + mkdir -p "${GNUPGHOME}" + chmod 700 "${GNUPGHOME}" + chown 1000:1000 "${GNUPGHOME}" + fi + {{- end }} + configure_gitea.sh: |- #!/usr/bin/env bash diff --git a/templates/gitea/statefulset.yaml b/templates/gitea/statefulset.yaml index ed9a887..ce6f550 100644 --- a/templates/gitea/statefulset.yaml +++ b/templates/gitea/statefulset.yaml @@ -59,6 +59,10 @@ spec: {{- if .Values.statefulset.env }} {{- toYaml .Values.statefulset.env | nindent 12 }} {{- end }} + {{- if .Values.signing.enabled }} + - name: GNUPGHOME + value: {{ .Values.signing.gpgHome }} + {{- end }} volumeMounts: - name: init mountPath: /usr/sbin @@ -110,6 +114,36 @@ spec: {{- include "gitea.init-additional-mounts" . | nindent 12 }} securityContext: {{- toYaml .Values.containerSecurityContext | nindent 12 }} + {{- if .Values.signing.enabled }} + - name: configure-gpg + image: "{{ include "gitea.image" . }}" + command: ["/usr/sbin/configure_gpg_environment.sh"] + imagePullPolicy: {{ .Values.image.pullPolicy }} + securityContext: + {{- /* By default this container runs as user 1000 unless otherwise stated */ -}} + {{- $csc := deepCopy .Values.containerSecurityContext -}} + {{- if not (hasKey $csc "runAsUser") -}} + {{- $_ := set $csc "runAsUser" 1000 -}} + {{- end -}} + {{- toYaml $csc | nindent 12 }} + env: + - name: GNUPGHOME + value: {{ .Values.signing.gpgHome }} + volumeMounts: + - name: init + mountPath: /usr/sbin + - name: data + mountPath: /data + {{- if .Values.persistence.subPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + - name: gpg-private-key + mountPath: /raw + readOnly: true + {{- if .Values.extraVolumeMounts }} + {{- toYaml .Values.extraVolumeMounts | nindent 12 }} + {{- end }} + {{- end }} - name: configure-gitea image: "{{ include "gitea.image" . }}" command: ["/usr/sbin/configure_gitea.sh"] @@ -305,6 +339,15 @@ spec: {{- end }} - name: temp emptyDir: {} + {{- if .Values.signing.enabled }} + - name: gpg-private-key + secret: + secretName: {{ include "gitea.gpg-key-secret-name" . }} + items: + - key: privateKey + path: private.asc + defaultMode: 0100 + {{- end }} {{- if and .Values.persistence.enabled .Values.persistence.existingClaim }} - name: data persistentVolumeClaim: diff --git a/unittests/gpg-secret/signing-disabled.yaml b/unittests/gpg-secret/signing-disabled.yaml new file mode 100644 index 0000000..3b1aba4 --- /dev/null +++ b/unittests/gpg-secret/signing-disabled.yaml @@ -0,0 +1,13 @@ +suite: GPG secret template (signing disabled) +release: + name: gitea-unittests + namespace: testing +templates: + - templates/gitea/gpg-secret.yaml +tests: + - it: renders nothing + set: + signing.enabled: false + asserts: + - hasDocuments: + count: 0 diff --git a/unittests/gpg-secret/signing-enabled.yaml b/unittests/gpg-secret/signing-enabled.yaml new file mode 100644 index 0000000..3c742e9 --- /dev/null +++ b/unittests/gpg-secret/signing-enabled.yaml @@ -0,0 +1,40 @@ +suite: GPG secret template (signing enabled) +release: + name: gitea-unittests + namespace: testing +templates: + - templates/gitea/gpg-secret.yaml +tests: + - it: fails rendering when nothing is configured + set: + signing: + enabled: true + asserts: + - failedTemplate: + errorMessage: Either specify `signing.privateKey` or `signing.existingKey` + - it: skips rendering using external secret reference + set: + signing: + enabled: true + existingSecret: "external-secret-reference" + asserts: + - hasDocuments: + count: 0 + - it: renders secret specification using inline gpg key + set: + signing: + enabled: true + privateKey: "gpg-key-placeholder" + asserts: + - hasDocuments: + count: 1 + - documentIndex: 0 + containsDocument: + kind: Secret + apiVersion: v1 + name: gitea-unittests-gpg-key + - isNotEmpty: + path: metadata.labels + - equal: + path: data.privateKey + value: "Z3BnLWtleS1wbGFjZWhvbGRlcg==" diff --git a/unittests/init/basic.yaml b/unittests/init/basic.yaml new file mode 100644 index 0000000..f2b746e --- /dev/null +++ b/unittests/init/basic.yaml @@ -0,0 +1,15 @@ +suite: Init template (basic) +release: + name: gitea-unittests + namespace: testing +templates: + - templates/gitea/init.yaml +tests: + - it: renders a secret + asserts: + - hasDocuments: + count: 1 + - containsDocument: + kind: Secret + apiVersion: v1 + name: gitea-unittests-init diff --git a/unittests/init/init_directory_structure.sh.yaml b/unittests/init/init_directory_structure.sh.yaml new file mode 100644 index 0000000..7be2336 --- /dev/null +++ b/unittests/init/init_directory_structure.sh.yaml @@ -0,0 +1,64 @@ +suite: Init template +release: + name: gitea-unittests + namespace: testing +templates: + - templates/gitea/init.yaml +tests: + - it: runs gpg in batch mode + set: + signing.enabled: true + asserts: + - equal: + path: stringData.[configure_gpg_environment.sh] + value: |- + #!/usr/bin/env bash + set -eu + + gpg --batch --import /raw/private.asc + - it: skips gpg script block for disabled signing + asserts: + - equal: + path: stringData.[init_directory_structure.sh] + value: |- + #!/usr/bin/env bash + + set -euo pipefail + + set -x + chown 1000:1000 /data + mkdir -p /data/git/.ssh + chmod -R 700 /data/git/.ssh + [ ! -d /data/gitea/conf ] && mkdir -p /data/gitea/conf + + # prepare temp directory structure + mkdir -p "${GITEA_TEMP}" + chown 1000:1000 "${GITEA_TEMP}" + chmod ug+rwx "${GITEA_TEMP}" + - it: adds gpg script block for enabled signing + set: + signing.enabled: true + asserts: + - equal: + path: stringData.[init_directory_structure.sh] + value: |- + #!/usr/bin/env bash + + set -euo pipefail + + set -x + chown 1000:1000 /data + mkdir -p /data/git/.ssh + chmod -R 700 /data/git/.ssh + [ ! -d /data/gitea/conf ] && mkdir -p /data/gitea/conf + + # prepare temp directory structure + mkdir -p "${GITEA_TEMP}" + chown 1000:1000 "${GITEA_TEMP}" + chmod ug+rwx "${GITEA_TEMP}" + + if [ ! -d "${GNUPGHOME}" ]; then + mkdir -p "${GNUPGHOME}" + chmod 700 "${GNUPGHOME}" + chown 1000:1000 "${GNUPGHOME}" + fi diff --git a/unittests/statefulset/basic.yaml b/unittests/statefulset/basic.yaml new file mode 100644 index 0000000..00fb684 --- /dev/null +++ b/unittests/statefulset/basic.yaml @@ -0,0 +1,17 @@ +suite: Statefulset template (basic) +release: + name: gitea-unittests + namespace: testing +templates: + - templates/gitea/statefulset.yaml + - templates/gitea/config.yaml +tests: + - it: renders a statefulset + template: templates/gitea/statefulset.yaml + asserts: + - hasDocuments: + count: 1 + - containsDocument: + kind: StatefulSet + apiVersion: apps/v1 + name: gitea-unittests diff --git a/unittests/statefulset/signing-disabled.yaml b/unittests/statefulset/signing-disabled.yaml new file mode 100644 index 0000000..4f9f2ce --- /dev/null +++ b/unittests/statefulset/signing-disabled.yaml @@ -0,0 +1,40 @@ +suite: Statefulset template (signing disabled) +release: + name: gitea-unittests + namespace: testing +templates: + - templates/gitea/statefulset.yaml + - templates/gitea/config.yaml +tests: + - it: skips gpg init container + template: templates/gitea/statefulset.yaml + asserts: + - notContains: + path: spec.template.spec.initContainers + any: true + content: + name: configure-gpg + - it: skips gpg env in `init-directories` init container + template: templates/gitea/statefulset.yaml + set: + signing.enabled: true + asserts: + - contains: + path: spec.template.spec.initContainers[0].env + content: + name: GNUPGHOME + value: /data/git/.gnupg + - it: skips gpg env in runtime container + template: templates/gitea/statefulset.yaml + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: GNUPGHOME + - it: skips gpg volume spec + template: templates/gitea/statefulset.yaml + asserts: + - notContains: + path: spec.template.spec.volumes + content: + name: gpg-private-key diff --git a/unittests/statefulset/signing-enabled.yaml b/unittests/statefulset/signing-enabled.yaml new file mode 100644 index 0000000..ecb237f --- /dev/null +++ b/unittests/statefulset/signing-enabled.yaml @@ -0,0 +1,93 @@ +suite: Statefulset template (signing enabled) +release: + name: gitea-unittests + namespace: testing +templates: + - templates/gitea/statefulset.yaml + - templates/gitea/config.yaml +tests: + - it: adds gpg init container + template: templates/gitea/statefulset.yaml + set: + signing: + enabled: true + existingSecret: "custom-gpg-secret" + asserts: + - equal: + path: spec.template.spec.initContainers[2].name + value: configure-gpg + - equal: + path: spec.template.spec.initContainers[2].command + value: ["/usr/sbin/configure_gpg_environment.sh"] + - equal: + path: spec.template.spec.initContainers[2].securityContext + value: + runAsUser: 1000 + - equal: + path: spec.template.spec.initContainers[2].env + value: + - name: GNUPGHOME + value: /data/git/.gnupg + - equal: + path: spec.template.spec.initContainers[2].volumeMounts + value: + - name: init + mountPath: /usr/sbin + - name: data + mountPath: /data + - name: gpg-private-key + mountPath: /raw + readOnly: true + - it: adds gpg env in `init-directories` init container + template: templates/gitea/statefulset.yaml + set: + signing.enabled: true + asserts: + - contains: + path: spec.template.spec.initContainers[0].env + content: + name: GNUPGHOME + value: /data/git/.gnupg + - it: adds gpg env in runtime container + template: templates/gitea/statefulset.yaml + set: + signing.enabled: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: GNUPGHOME + value: /data/git/.gnupg + - it: adds gpg volume spec + template: templates/gitea/statefulset.yaml + set: + signing: + enabled: true + asserts: + - contains: + path: spec.template.spec.volumes + content: + name: gpg-private-key + secret: + secretName: gitea-unittests-gpg-key + items: + - key: privateKey + path: private.asc + defaultMode: 0100 + - it: supports gpg volume spec with external reference + template: templates/gitea/statefulset.yaml + set: + signing: + enabled: true + existingSecret: custom-gpg-secret + asserts: + - contains: + path: spec.template.spec.volumes + content: + name: gpg-private-key + secret: + secretName: custom-gpg-secret + items: + - key: privateKey + path: private.asc + defaultMode: 0100 diff --git a/values.yaml b/values.yaml index ad0ca31..ebd2e50 100644 --- a/values.yaml +++ b/values.yaml @@ -253,9 +253,17 @@ initPreScript: "" # ## @param signing.enabled Enable commit/action signing ## @param signing.gpgHome GPG home directory +## @param signing.privateKey Inline private gpg key for signed Gitea actions +## @param signing.existingSecret Use an existing secret to store the value of `signing.privateKey` signing: enabled: false gpgHome: /data/git/.gnupg + privateKey: "" + # privateKey: |- + # -----BEGIN PGP PRIVATE KEY BLOCK----- + # ... + # -----END PGP PRIVATE KEY BLOCK----- + existingSecret: "" ## @section Gitea #