Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mount volumes for tmp directories, apply best practice to sc #38

Merged
merged 8 commits into from
Nov 29, 2023
7 changes: 7 additions & 0 deletions .changeset/bright-students-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@fake-scope/fake-pkg": major
---

* rename `securityContext` to `containerSecurityContext` in `values.yaml`
* mount volumes for tmp directories to make containers work in accordance with best practices, that is with read-only file systems
* use secure defaults for container security policy
29 changes: 24 additions & 5 deletions charts/openproject/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,31 @@ This is the chart for OpenProject itself.
To install or update from this directory run the following command.

```bash
helm upgrade \
--create-namespace --namespace openproject \
--install --reuse-values openproject-dev .
bin/install-dev
```

### TLS
This will install the chart with `--set develop=true` which is recommended
on local clusters such as **minikube** or **kind**.

This will also set `OPENPROJECT_HTTPS` to false so no TLS certificate is required
to access it.

You can set other options just like when installing via `--set`
(e.g. `bin/install-dev --set persistence.enabled=false`).

### Debugging

Changes to the chart can be debugged using the following.

```bash
bin/debug
```

This will try to render the templates and show any errors.
You can set values just like when installing via `--set`
(e.g. `bin/debug --set persistence.enabled=false`).

## TLS

Create a TLS certificate, e.g. using [mkcert](https://github.com/FiloSottile/mkcert).

Expand All @@ -34,7 +53,7 @@ Set the tls secret value during installation or an upgrade by adding the followi
--set ingress.tls.enabled=true --set tls.secretName=openproject-tls
```

#### Root CA
### Root CA

If you want to add your own root CA for outgoing TLS connection, do the following.

Expand Down
46 changes: 46 additions & 0 deletions charts/openproject/bin/debug
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/bash

# Outputs the generated helm configurations after templating.

yaml_output=/tmp/op-hc-yaml-output.txt
error_output=/tmp/op-hc-error-output.txt
section_output=/tmp/op-hc-section-output.yml
vimrc=/tmp/op-hc-vim-rc

rm $yaml_output $error_output $section_output $vimrc &>/dev/null

helm template --debug "$@" . 1> $yaml_output 2> $error_output

if [ $? -gt 0 ]; then
section=`cat $error_output | grep 'Error: YAML parse error on' | cut -d: -f2 | cut -d' ' -f6-`

if [ -n "$section" ]; then
cat $yaml_output | sed -e "0,/\# Source: ${section//\//\\/}/d" | tail -n+2 | sed -e '/---/,$d' > $section_output

line=`cat $error_output | grep line | head -n1 | perl -nle 'm/line (\d+)/; print $1'`

if [ -n "$line" ]; then
echo "autocmd VimEnter * echo '`cat $error_output | grep line | head -n1`'" > $vimrc
vim +$line -u $vimrc $section_output
else
echo
echo "Template error: "
echo
echo ---
cat $section_output
cat $error_output
fi
else
echo
echo "Template error: "
echo
echo ---
cat $yaml_output
cat $error_output
fi
else
cat $yaml_output

echo
echo "Syntax ok"
fi
6 changes: 6 additions & 0 deletions charts/openproject/bin/install-dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# !/bin/bash

# Install OpenProject in development mode, that is without https and allowing writes
# to the container file system.

helm upgrade --create-namespace --namespace openproject --install openproject --set develop=true "$@" .
40 changes: 40 additions & 0 deletions charts/openproject/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{{/*
Returns the OpenProject image to be used including the respective registry and image tag.
*/}}
{{- define "openproject.image" -}}
{{ .Values.image.registry }}/{{ .Values.image.repository }}{{ if .Values.image.sha256 }}@sha256:{{ .Values.image.sha256 }}{{ else }}:{{ .Values.image.tag }}{{ end }}
{{- end -}}

{{/*
Yields the configured container security context if enabled.

Allows writing to the container file system in development mode
This way the OpenProject container works without mounted tmp volumes
which may not work correctly in local development clusters.
*/}}
{{- define "openproject.containerSecurityContext" }}
{{- if .Values.containerSecurityContext.enabled }}
securityContext:
{{-
mergeOverwrite
(omit .Values.containerSecurityContext "enabled" | deepCopy)
(dict "readOnlyRootFilesystem" (not .Values.develop))
| toYaml
| nindent 2
}}
{{- end }}
{{- end }}

{{/* Yields the configured pod security context if enabled. */}}
{{- define "openproject.podSecurityContext" }}
{{- if .Values.podSecurityContext.enabled }}
securityContext:
{{ omit .Values.podSecurityContext "enabled" | toYaml | nindent 2 | trim }}
{{- end }}
{{- end }}

{{- define "openproject.useTmpVolumes" -}}
{{- if not .Values.develop -}}
{{- true -}}
{{- end -}}
{{- end -}}
2 changes: 1 addition & 1 deletion charts/openproject/templates/secrets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ stringData:
OPENPROJECT_SEED_ADMIN_USER_PASSWORD_RESET: {{ .Values.openproject.admin_user.password_reset | quote }}
OPENPROJECT_SEED_ADMIN_USER_NAME: {{ .Values.openproject.admin_user.name | quote }}
OPENPROJECT_SEED_ADMIN_USER_MAIL: {{ .Values.openproject.admin_user.mail | quote }}
OPENPROJECT_HTTPS: {{ .Values.openproject.https | quote }}
OPENPROJECT_HTTPS: {{ (.Values.develop | ternary "false" .Values.openproject.https) | quote }}
OPENPROJECT_SEED_LOCALE: {{ .Values.openproject.seed_locale | quote }}
OPENPROJECT_HOST__NAME: {{ .Values.openproject.host | default .Values.ingress.host | quote }}
OPENPROJECT_HSTS: {{ .Values.openproject.hsts | quote }}
Expand Down
41 changes: 24 additions & 17 deletions charts/openproject/templates/seeder-job.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,44 @@ spec:
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- if .Values.podSecurityContext.enabled }}
securityContext:
{{ omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 | trim }}
{{- end }}
{{- include "openproject.podSecurityContext" . | indent 6 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | nindent 8 | trim }}
{{- end }}
{{- if .Values.persistence.enabled }}
volumes:
{{- if (include "openproject.useTmpVolumes" .) }}
- name: tmp
# we can't use emptyDir due to the sticky bit issue
# see: https://github.com/kubernetes/kubernetes/issues/110835
ephemeral:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
{{- end }}
{{- if .Values.persistence.enabled }}
- name: "data"
persistentVolumeClaim:
claimName: {{ if .Values.persistence.existingClaim }}{{ .Values.persistence.existingClaim }}{{- else }}{{ include "common.names.fullname" . }}{{- end }}
{{- end }}
{{- end }}
initContainers:
- name: check-db-ready
image: "{{ .Values.initdb.image.registry }}/{{ .Values.initdb.image.repository }}:{{ .Values.initdb.image.tag }}"
imagePullPolicy: {{ .Values.initdb.image.imagePullPolicy }}
command: [
'sh',
'-c',
'until pg_isready -h $DATABASE_HOST -p $DATABASE_PORT; do echo "waiting for database $DATABASE_HOST:$DATABASE_PORT"; sleep 2; done;'
'until pg_isready -h $DATABASE_HOST -p $DATABASE_PORT -U {{ .Values.postgresql.auth.username }}; do echo "waiting for database $DATABASE_HOST:$DATABASE_PORT"; sleep 2; done;'
]
envFrom:
- secretRef:
name: {{ include "common.names.fullname" . }}
resources:
{{- toYaml .Values.initdb.resources | nindent 12 }}
{{- if .Values.securityContext.enabled }}
securityContext:
{{- omit .Values.securityContext "enabled" | toYaml | nindent 12 }}
{{- end }}
{{- include "openproject.containerSecurityContext" . | indent 10 }}
containers:
- name: seeder
image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}{{ if .Values.image.sha256 }}@sha256:{{ .Values.image.sha256 }}{{ else }}:{{ .Values.image.tag }}{{ end }}"
Expand All @@ -61,13 +67,14 @@ spec:
envFrom:
- secretRef:
name: {{ include "common.names.fullname" . }}
{{- if .Values.persistence.enabled }}
volumeMounts:
{{- if (include "openproject.useTmpVolumes" .) }}
- mountPath: /tmp
name: tmp
{{- end }}
{{- if .Values.persistence.enabled }}
- name: "data"
mountPath: "/var/openproject/assets"
{{- end }}
{{- if .Values.securityContext.enabled }}
securityContext:
{{- omit .Values.securityContext "enabled" | toYaml | nindent 12 }}
{{- end }}
{{- end }}
{{- include "openproject.containerSecurityContext" . | indent 10 }}
restartPolicy: OnFailure
63 changes: 41 additions & 22 deletions charts/openproject/templates/web-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,29 +46,45 @@ spec:
nodeSelector:
{{ toYaml . | nindent 8 | trim }}
{{- end }}
{{- if .Values.podSecurityContext.enabled }}
securityContext:
{{ omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 | trim }}
{{- end }}
{{- include "openproject.podSecurityContext" . | indent 6 }}
serviceAccountName: {{ include "common.names.fullname" . }}
volumes:
{{- if .Values.egress.tls.rootCA.fileName }}
{{- if (include "openproject.useTmpVolumes" .) }}
- name: tmp
# we can't use emptyDir due to the sticky bit issue
# see: https://github.com/kubernetes/kubernetes/issues/110835
ephemeral:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
- name: app-tmp
# we can't use emptyDir due to the sticky bit / world writable issue
# see: https://github.com/kubernetes/kubernetes/issues/110835
ephemeral:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
{{- end }}
{{- if .Values.egress.tls.rootCA.fileName }}
- name: ca-pemstore
configMap:
name: "{{- .Values.egress.tls.rootCA.configMap }}"
{{- end }}
{{- if .Values.persistence.enabled }}
{{- end }}
{{- if .Values.persistence.enabled }}
- name: "data"
persistentVolumeClaim:
claimName: {{ if .Values.persistence.existingClaim }}{{ .Values.persistence.existingClaim }}{{- else }}{{ include "common.names.fullname" . }}{{- end }}
{{- end }}
{{- end }}
initContainers:
- name: wait-for-db
{{- if .Values.securityContext.enabled }}
securityContext:
{{- omit .Values.securityContext "enabled" | toYaml | nindent 12 }}
{{- end }}
image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}{{ if .Values.image.sha256 }}@sha256:{{ .Values.image.sha256 }}{{ else }}:{{ .Values.image.tag }}{{ end }}"
{{- include "openproject.containerSecurityContext" . | indent 10 }}
image: {{ include "openproject.image" . }}
imagePullPolicy: {{ .Values.image.imagePullPolicy }}
envFrom:
- secretRef:
Expand All @@ -78,11 +94,8 @@ spec:
- /app/docker/prod/wait-for-db
containers:
- name: "openproject"
{{- if .Values.securityContext.enabled }}
securityContext:
{{- omit .Values.securityContext "enabled" | toYaml | nindent 12 }}
{{- end }}
image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}{{ if .Values.image.sha256 }}@sha256:{{ .Values.image.sha256 }}{{ else }}:{{ .Values.image.tag }}{{ end }}"
{{- include "openproject.containerSecurityContext" . | indent 10 }}
image: {{ include "openproject.image" . }}
imagePullPolicy: {{ .Values.image.imagePullPolicy }}
envFrom:
- secretRef:
Expand All @@ -93,16 +106,22 @@ spec:
value: "/etc/ssl/certs/custom-ca.pem"
{{- end }}
volumeMounts:
{{- if .Values.persistence.enabled }}
{{- if (include "openproject.useTmpVolumes" .) }}
- mountPath: /tmp
name: tmp
- mountPath: /app/tmp
name: app-tmp
{{- end }}
{{- if .Values.persistence.enabled }}
- name: "data"
mountPath: "/var/openproject/assets"
{{- end }}
{{- if .Values.egress.tls.rootCA.fileName }}
{{- end }}
{{- if .Values.egress.tls.rootCA.fileName }}
- name: ca-pemstore
mountPath: /etc/ssl/certs/custom-ca.pem
subPath: {{ .Values.egress.tls.rootCA.fileName }}
readOnly: false
{{- end }}
{{- end }}
ports:
{{- range $key, $value := .Values.service.ports }}
- name: {{ $key }}
Expand Down
Loading
Loading