Add gpg configuration settings (#343) All checks were successful continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
### Description of the change This PR adds support for gpg key setup. It allows to pass the gpg private key content inline inside `values.yaml` or refer to an existing secret containing the key content data. ### Benefits Administrators don't need to manually setup the gpg environment from inside a running container. It also eliminates the breaking change of Gitea 1.17 regarding `[git].HOME` as the `GNUPGHOME` environment variable is used consistently to relocate the `.gnupg` directory to its former location. ### Applicable issues - fixes #107 ### Additional information This PR add the first unit tests to this Helm Chart, ensuring templating integrity for signing related configuration. ### Checklist - [x] Parameters are documented in the `values.yaml` and added to the `README.md` using [readme-generator-for-helm](https://github.com/bitnami-labs/readme-generator-for-helm) Co-authored-by: justusbunsi <sk.bunsenbrenner@gmail.com> Co-authored-by: pat-s <pat-s@noreply.gitea.io> Reviewed-on: gitea/helm-chart#343 Reviewed-by: luhahn <luhahn@noreply.gitea.io> Reviewed-by: techknowlogick <techknowlogick@gitea.io> Co-authored-by: justusbunsi <justusbunsi@noreply.gitea.io> Co-committed-by: justusbunsi <justusbunsi@noreply.gitea.io>
This commit was merged in pull request #343.
This commit is contained in:
@@ -23,6 +23,15 @@ steps: | ||||
- helm dependency update | ||||
- helm template --debug gitea-helm . | ||||
| ||||
- name: helm unittests | ||||
pull: always | ||||
image: alpine:3.16 | ||||
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.16 | ||||
| ||||
1 .gitignore vendored
1
.gitignore vendored @@ -1,3 +1,4 @@ | ||||
charts/ | ||||
node_modules/ | ||||
.DS_Store | ||||
unittests/*/__snapshot__/ | ||||
| ||||
@@ -25,3 +25,4 @@ node_modules/ | ||||
package.json | ||||
package-lock.json | ||||
.gitea/ | ||||
unittests/ | ||||
| ||||
@@ -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 | ||||
``` | ||||
| ||||
4 Makefile
4
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' ./ | ||||
| ||||
71 README.md
71
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 | ||||
| ||||
| ||||
@@ -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 -}} | ||||
| ||||
16 templates/gitea/gpg-secret.yaml Normal file
16
templates/gitea/gpg-secret.yaml Normal file @@ -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 }} | ||||
@@ -6,6 +6,11 @@ metadata: | ||||
{{- include "gitea.labels" . | nindent 4 }} | ||||
type: Opaque | ||||
stringData: | ||||
configure_gpg_environment.sh: |- | ||||
#!/usr/bin/env bash | ||||
set -eu | ||||
| ||||
gpg --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 | ||||
| ||||
| ||||
@@ -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: | ||||
| ||||
13 unittests/gpg-secret/signing-disabled.yaml Normal file
13
unittests/gpg-secret/signing-disabled.yaml Normal file @@ -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 | ||||
40 unittests/gpg-secret/signing-enabled.yaml Normal file
40
unittests/gpg-secret/signing-enabled.yaml Normal file @@ -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==" | ||||
15 unittests/init/basic.yaml Normal file
15
unittests/init/basic.yaml Normal file @@ -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 | ||||
53 unittests/init/init_directory_structure.sh.yaml Normal file
53
unittests/init/init_directory_structure.sh.yaml Normal file @@ -0,0 +1,53 @@ | ||||
suite: Init template | ||||
release: | ||||
name: gitea-unittests | ||||
namespace: testing | ||||
templates: | ||||
- templates/gitea/init.yaml | ||||
tests: | ||||
- 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 | ||||
17 unittests/statefulset/basic.yaml Normal file
17
unittests/statefulset/basic.yaml Normal file @@ -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 | ||||
40 unittests/statefulset/signing-disabled.yaml Normal file
40
unittests/statefulset/signing-disabled.yaml Normal file @@ -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 | ||||
93 unittests/statefulset/signing-enabled.yaml Normal file
93
unittests/statefulset/signing-enabled.yaml Normal file @@ -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 | ||||
@@ -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 | ||||
# | ||||
| ||||
Reference in New Issue
Block a user