Skip to content

Commit

Permalink
mount volumes for tmp directories, apply best practice to sc (#38)
Browse files Browse the repository at this point in the history
* mount volumes for tmp directories, apply best practice to sc

* bump version

* use safe sc for bundled apps (postgres, memcached) too

* use read-only filesystem with mounted tmp volumes unless in development mode

* revert version bump to let this be handled by changeset flow

* amend dev readme

* Create bright-students-eat.md

---------

Co-authored-by: Oliver Günther <[email protected]>
  • Loading branch information
machisuji and oliverguenther authored Nov 29, 2023
1 parent b59f5fe commit 7fc8ea3
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 70 deletions.
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

0 comments on commit 7fc8ea3

Please sign in to comment.