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

PWB: Support defining sensitive config values via k8s secrets #535

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion charts/rstudio-workbench/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: rstudio-workbench
description: Official Helm chart for Posit Workbench
version: 0.8.7
version: 1.0.0
apiVersion: v2
appVersion: 2024.09.1
icon: https://rstudio.com/wp-content/uploads/2018/10/RStudio-Logo-Flat.png
Expand Down
15 changes: 10 additions & 5 deletions charts/rstudio-workbench/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ To ensure a stable production deployment:
* "Pin" the version of the Helm chart that you are using. You can do this using the:
* `helm dependency` command *and* the associated "Chart.lock" files *or*
* the `--version` flag.

::: {.callout-important}
This protects you from breaking changes.
:::
Expand Down Expand Up @@ -452,6 +451,8 @@ Use of [Sealed secrets](https://github.com/bitnami-labs/sealed-secrets) disables
| affinity | object | `{}` | A map used verbatim as the pod's "affinity" definition |
| args | list | `[]` | args is the pod container's run arguments. |
| command | list | `[]` | command is the pod container's run command. By default, it uses the container's default. However, the chart expects a container using `supervisord` for startup |
| config.database.conf.value | string | 0644 | Database connection config |
| config.database.conf.existingSecret | string | `""` | Secret for database connection config |
| config.defaultMode.jobJsonOverrides | int | 0644 | default mode for jobJsonOverrides config |
| config.defaultMode.pam | int | 0644 | default mode for pam scripts |
| config.defaultMode.prestart | int | 0755 | default mode for prestart config |
Expand All @@ -475,7 +476,8 @@ Use of [Sealed secrets](https://github.com/bitnami-labs/sealed-secrets) disables
| diagnostics | object | `{"directory":"/var/log/rstudio","enabled":false}` | Settings for enabling server diagnostics |
| extraObjects | list | `[]` | Extra objects to deploy (value evaluated as a template) |
| fullnameOverride | string | `""` | the full name of the release (can be overridden) |
| global.secureCookieKey | string | `""` | |
| global.secureCookieKey.value | string | `""` | |
| global.secureCookieKey.existingSecret | string | `""` | Secret containing secureCookieKey |
| homeStorage.accessModes | list | `["ReadWriteMany"]` | accessModes defined for the storage PVC (represented as YAML) |
| homeStorage.create | bool | `false` | whether to create the persistentVolumeClaim for homeStorage |
| homeStorage.mount | bool | `false` | Whether the persistentVolumeClaim should be mounted (even if not created) |
Expand Down Expand Up @@ -507,7 +509,8 @@ Use of [Sealed secrets](https://github.com/bitnami-labs/sealed-secrets) disables
| launcher.templateValues | object | `{"job":{"annotations":{},"labels":{},"ttlSecondsAfterFinished":null},"pod":{"affinity":{},"annotations":{},"command":[],"containerSecurityContext":{},"defaultSecurityContext":{},"env":[],"extraContainers":[],"imagePullPolicy":"","imagePullSecrets":[],"initContainers":[],"labels":{},"nodeSelector":{},"securityContext":{},"serviceAccountName":"","tolerations":[],"volumeMounts":[],"volumes":[]},"service":{"annotations":{},"labels":{},"type":"ClusterIP"}}` | values that are passed along to the launcher job rendering process as a data object (in JSON). These values are then used within session templates. |
| launcher.templateValues.pod.command | list | `[]` | command for all pods. This is really not something we should expose and will be removed once we have a better option |
| launcher.useTemplates | bool | `false` | whether to render and use templates in the job launching process |
| launcherPem | string | `""` | An inline launcher.pem key. If not provided, one will be auto-generated. See README for more details. |
| launcherPem.value | string | `""` | An inline launcher.pem key. If not provided, one will be auto-generated. See README for more details. |
| launcherPem.existingSecret | string | `""` | Existing Secret for launcherPem |
| launcherPub | bool | `false` | An inline launcher.pub key to pair with launcher.pem. If `false` (the default), we will try to generate a `launcher.pub` from the provided `launcher.pem` |
| license.file | object | `{"contents":false,"mountPath":"/etc/rstudio-licensing","mountSubPath":false,"secret":false,"secretKey":"license.lic"}` | the file section is used for licensing with a license file |
| license.file.contents | bool | `false` | contents is an in-line license file |
Expand Down Expand Up @@ -552,7 +555,8 @@ Use of [Sealed secrets](https://github.com/bitnami-labs/sealed-secrets) disables
| revisionHistoryLimit | int | `10` | The revisionHistoryLimit to use for the pod deployment. Do not set to 0 |
| sealedSecret.annotations | object | `{}` | annotations for SealedSecret resources |
| sealedSecret.enabled | bool | `false` | use SealedSecret instead of Secret to deploy secrets |
| secureCookieKey | string | `""` | |
| secureCookieKey.value | string | `""` | |
| secureCookieKey.existingSecret | string | `""` | Secret containing secureCookieKey |
| securityContext | object | `{}` | |
| service.annotations | object | `{}` | Annotations for the service, for example to specify [an internal load balancer](https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer) |
| service.clusterIP | string | `""` | The cluster-internal IP to use with `service.type` ClusterIP |
Expand Down Expand Up @@ -587,7 +591,8 @@ Use of [Sealed secrets](https://github.com/bitnami-labs/sealed-secrets) disables
| topologySpreadConstraints | list | `[]` | An array used verbatim as the pod's "topologySpreadConstraints" definition |
| userCreate | bool | `false` | userCreate determines whether a user should be created at startup (if true) |
| userName | string | `"rstudio"` | userName determines the username of the created user |
| userPassword | string | `"rstudio"` | userPassword determines the password of the created user |
| userPassword.value | string | `"rstudio"` | userPassword determines the password of the created user |
| userPassword.existingSecret | string | `""` | Existing Secret for userPassword |
| userUid | string | `"10000"` | userUid determines the UID of the created user |
| versionOverride | string | `""` | A Workbench version to override the "tag" for the RStudio Workbench image and the session images. Necessary until https://github.com/helm/helm/issues/8194 |
| xdgConfigDirs | string | `"/mnt/dynamic:/mnt/session-configmap:/mnt/secret-configmap:/mnt/configmap:/mnt/load-balancer/"` | The XDG config dirs (directories where configuration will be read from). Do not change without good reason. |
Expand Down
8 changes: 4 additions & 4 deletions charts/rstudio-workbench/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@

{{ include "rstudio-workbench.fullname" . }} successfully deployed to namespace {{ $.Release.Namespace }}

{{- if eq .Values.launcherPem "" }}
{{- if eq .Values.launcherPem.value "" }}

NOTE: Using auto-generated value for "launcher.pem"
- We recommend making this value persistent by setting `.Values.launcherPem`
- We recommend making this value persistent by setting `.Values.launcherPem.value` or `.Values.launcherPem.existingSecret`
- If the value changes, sessions started before the change will not be accessible
- You can get the current value with:
```
kubectl -n {{ $.Release.Namespace }} get secret {{ include "rstudio-workbench.fullname" . }}-secret --template='{{print "{{" }}index .data "launcher.pem" | base64decode {{print "}}" }}'
```
{{- end }}
{{- if eq (default .Values.secureCookieKey .Values.global.secureCookieKey) "" }}
{{- if eq (default .Values.secureCookieKey.value .Values.global.secureCookieKey.value) "" }}

NOTE: Using an auto-generated value for "secure-cookie-key"
- We recommend making this value persistent by setting `.Values.global.secureCookieKey`
- We recommend making this value persistent by setting `.Values.global.secureCookieKey.value`
- If the value changes, authenticated sessions will be invalidated (all users will be logged out) and some old sessions will not be accessible
- You can get the current value with:
```
Expand Down
54 changes: 50 additions & 4 deletions charts/rstudio-workbench/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,17 @@ containers:
value: "{{ .Values.userName }}"
- name: RSW_TESTUSER_UID
value: "{{ .Values.userUid }}"
{{- if .Values.userPassword.existingSecret }}
- name: RSW_TESTUSER_PASSWD
value: "{{ .Values.userPassword }}"
valueFrom:
secretKeyRef:
key: password
name: {{ .Values.userPassword.existingSecret }}
{{- end }}
{{- if .Values.userPassword.value }}
- name: RSW_TESTUSER_PASSWD
value: {{ .Values.userPassword.value }}
{{- end }}
{{- else }}
- name: RSW_TESTUSER
value: ""
Expand Down Expand Up @@ -122,8 +131,25 @@ containers:
- name: rstudio-session-secret
mountPath: {{ .Values.session.defaultSecretMountPath }}
{{- end }}
{{- if or (not .Values.launcherPem.existingSecret) (not .Values.secureCookieKey.existingSecret) }}
- name: rstudio-secret
mountPath: "/mnt/secret-configmap/rstudio/"
{{- end }}
{{- if .Values.launcherPem.existingSecret }}
- name: launcher-pem-secret
mountPath: "/mnt/secret-configmap/rstudio/launcher.pem"
subPath: "launcher.pem"
{{- end }}
{{- if .Values.secureCookieKey.existingSecret }}
- name: secure-cookie-key-secret
mountPath: "/mnt/secret-configmap/rstudio/secure-cookie-key"
subPath: "secure-cookie-key"
{{- end }}
{{- if .Values.config.database.conf.existingSecret }}
- name: database-conf-secret
mountPath: "/mnt/secret-configmap/rstudio/database.conf"
subPath: "database.conf"
{{- end }}
{{- if .Values.config.userProvisioning }}
- name: rstudio-user
mountPath: "/etc/sssd/conf.d/"
Expand Down Expand Up @@ -303,10 +329,30 @@ volumes:
name: {{ include "rstudio-workbench.fullname" . }}-pam
defaultMode: {{ .Values.config.defaultMode.pam }}
{{- end }}
{{- if or (not .Values.launcherPem.existingSecret) (not .Values.secureCookieKey.existingSecret) (not .Values.config.database.conf.existingSecret) }}
- name: rstudio-secret
secret:
secretName: {{ include "rstudio-workbench.fullname" . }}-secret
defaultMode: {{ .Values.config.defaultMode.secret }}
{{- end }}
{{- if .Values.launcherPem.existingSecret }}
- name: launcher-pem-secret
secret:
secretName: {{ .Values.launcherPem.existingSecret }}
defaultMode: {{ .Values.config.defaultMode.secret }}
{{- end }}
{{- if .Values.secureCookieKey.existingSecret }}
- name: secure-cookie-key-secret
secret:
secretName: {{ .Values.secureCookieKey.existingSecret }}
defaultMode: {{ .Values.config.defaultMode.secret }}
{{- end }}
{{- if .Values.config.database.conf.existingSecret }}
- name: database-conf-secret
secret:
secretName: {{ .Values.config.database.conf.existingSecret }}
defaultMode: {{ .Values.config.defaultMode.secret }}
{{- end }}
{{- if .Values.config.userProvisioning }}
- name: rstudio-user
secret:
Expand Down Expand Up @@ -440,8 +486,8 @@ app.kubernetes.io/instance: {{ .Release.Name }}
- if it is, we warn and leave it alone
*/}}
{{- define "rstudio-workbench.launcherPem" -}}
{{- $pemVar := $.Values.launcherPem -}}
{{- if eq ($.Values.launcherPem) ("") -}}
{{- $pemVar := $.Values.launcherPem.value -}}
{{- if and (eq $.Values.launcherPem.value "") (eq $.Values.launcherPem.existingSecret "") -}}
{{- $secretName := print (include "rstudio-workbench.fullname" .) "-secret" }}
{{- $currentSecret := lookup "v1" "Secret" $.Release.Namespace $secretName }}
{{- if and $currentSecret (not .Values.dangerRegenerateAutomatedValues) }}
Expand All @@ -462,7 +508,7 @@ app.kubernetes.io/instance: {{ .Release.Name }}
- if it is, we warn and leave it alone
*/}}
{{- define "rstudio-workbench.secureCookieKey" -}}
{{- $cookieVar := default .Values.secureCookieKey .Values.global.secureCookieKey -}}
{{- $cookieVar := default .Values.secureCookieKey.value .Values.global.secureCookieKey.value -}}
{{- if eq ($cookieVar) ("") -}}
{{- $secretName := print (include "rstudio-workbench.fullname" .) "-secret" }}
{{- $currentSecret := lookup "v1" "Secret" $.Release.Namespace $secretName }}
Expand Down
12 changes: 11 additions & 1 deletion charts/rstudio-workbench/templates/configmap-secret.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,21 @@ spec:
encryptedData:
{{- include "rstudio-library.config.ini" .Values.config.secret | nindent 4 }}
{{- /* do not auto-generate value as the secret will not be encrypted */}}
{{- if .Values.launcherPem.existingSecret }}
launcher.pem: |
{{- $launcherPemSecret := (lookup "v1" "Secret" $.Release.Namespace .Values.launcherPem.existingSecret) }}
{{- if $launcherPemSecret }}
{{- $launcherPem := index $launcherPemSecret.data "private_key" | b64dec }}
{{- $launcherPem | nindent 6 }}
{{- end }}
{{- end }}
{{- if .Values.launcherPem.value }}
launcher.pem: |
{{- .Values.launcherPem | nindent 6 }}
{{- end }}
{{- /* do not auto-generate value as the secret will not be encrypted */}}
secure-cookie-key: |
{{- default .Values.secureCookieKey .Values.global.secureCookieKey | nindent 6 }}
{{- default .Values.secureCookieKey.value .Values.global.secureCookieKey.value | nindent 6 }}
template:
data:
{{- if .Values.launcherPub }}
Expand Down
26 changes: 22 additions & 4 deletions charts/rstudio-workbench/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,10 @@ userName: "rstudio"
# -- userUid determines the UID of the created user
userUid: "10000"
# -- userPassword determines the password of the created user
userPassword: "rstudio"
userPassword:
value: "rstudio"
# key 'password'
existingSecret: ""

# -- The XDG config dirs (directories where configuration will be read from). Do not change without good reason.
xdgConfigDirs: "/mnt/dynamic:/mnt/session-configmap:/mnt/secret-configmap:/mnt/configmap:/mnt/load-balancer/"
Expand Down Expand Up @@ -376,16 +379,25 @@ extraObjects: []
jobJsonOverridesFiles: {}

# -- An inline launcher.pem key. If not provided, one will be auto-generated. See README for more details.
launcherPem: ""
launcherPem:
value: ""
# key 'launcher.pem'
existingSecret: ""
# -- An inline launcher.pub key to pair with launcher.pem. If `false` (the default), we will try to generate a `launcher.pub` from the provided `launcher.pem`
launcherPub: false

secureCookieKey: ""
secureCookieKey:
value: ""
# key 'secure-cookie-key'
existingSecret: ""

dangerRegenerateAutomatedValues: false

global:
secureCookieKey: ""
secureCookieKey:
value: ""
# key 'secure-cookie-key'
existingSecret: ""

config:
defaultMode:
Expand Down Expand Up @@ -417,6 +429,12 @@ config:
# @default -- 0644
pam: 0644

database:
conf:
value: ""
# key 'database.conf'
existingSecret: ""

# -- a map of session-scoped config files. Mounted to `/mnt/session-configmap/rstudio/` on both server and session, by default.
session:
repos.conf:
Expand Down
Loading