diff --git a/charts/timescaledb-single/Chart.yaml b/charts/timescaledb-single/Chart.yaml index 84e86776..6214f604 100644 --- a/charts/timescaledb-single/Chart.yaml +++ b/charts/timescaledb-single/Chart.yaml @@ -4,7 +4,7 @@ apiVersion: v1 name: timescaledb-single description: 'TimescaleDB HA Deployment.' -version: 0.33.2 +version: 0.34.0 icon: https://cdn.iconscout.com/icon/free/png-256/timescaledb-1958407-1651618.png # appVersion specifies the version of the software, which can vary wildly, diff --git a/charts/timescaledb-single/docs/admin-guide.md b/charts/timescaledb-single/docs/admin-guide.md index fb0c228f..14ea4759 100644 --- a/charts/timescaledb-single/docs/admin-guide.md +++ b/charts/timescaledb-single/docs/admin-guide.md @@ -69,6 +69,7 @@ The following table lists the configurable parameters of the TimescaleDB Helm ch | `pgBouncer.pg_hba` | The `pg_hba` to be used by pgBouncer | A `pg_hba` allowing non-superuser ssl-only connections | | `pgBouncer.userListSecretName` | If set, a [user authentication file](https://www.pgbouncer.org/config.html#authentication-file-format) to be used by pgBouncer. | `nil` | | `podManagementPolicy` | Either [`OrderedReady` or `Parallel`](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-management-policies) | `OrderedReady` | +| `podSecurityContext` | Security Context of the timescaledb pod. | `{ fsGroup: 1000, runAsGroup: 1000, runAsNonRoot: true, runAsUser: 1000 }` | | `podMonitor.enabled` | Enable deployment of podMonitor used with prometheus-operator. | `false` | | `podMonitor.path` | Path on which prometheus metrics are exposed. | `/metrics` | | `podMonitor.interval` | Prometheus scrape interval. Lower values increase resolution, higher values reduce prometheus memory consumption. Do not set over 2m. | `10s` | diff --git a/charts/timescaledb-single/scripts/pgbackrest_restore.sh b/charts/timescaledb-single/scripts/pgbackrest_restore.sh index 4e07f9eb..cd85da34 100644 --- a/charts/timescaledb-single/scripts/pgbackrest_restore.sh +++ b/charts/timescaledb-single/scripts/pgbackrest_restore.sh @@ -16,7 +16,8 @@ fi # A missing PGDATA points to Patroni removing a botched PGDATA, or manual # intervention. In this scenario, we need to recreate the DATA and WALDIRs # to keep pgBackRest happy -[ -d "${PGDATA}" ] || install -o postgres -g postgres -d -m 0700 "${PGDATA}" -[ -d "${WALDIR}" ] || install -o postgres -g postgres -d -m 0700 "${WALDIR}" +uid="$(id -u)"; gid="$(id -g)" +[ -d "${PGDATA}" ] || install -o $uid -g $gid -d -m 0700 "${PGDATA}" +[ -d "${WALDIR}" ] || install -o $uid -g $gid -d -m 0700 "${WALDIR}" exec pgbackrest --force --delta --log-level-console=detail restore diff --git a/charts/timescaledb-single/scripts/post_init.sh b/charts/timescaledb-single/scripts/post_init.sh index aa5f2dc3..76b304c5 100644 --- a/charts/timescaledb-single/scripts/post_init.sh +++ b/charts/timescaledb-single/scripts/post_init.sh @@ -27,6 +27,8 @@ __SQL__ for tablespace in $POSTGRES_TABLESPACES do log "Creating tablespace ${tablespace}" + # FIX ME: PGDATA points to the wrong directory + # "${PGDATA}/tablespaces" should be replaced with {{ include "tablespaces_dir" . }} tablespacedir="${PGDATA}/tablespaces/${tablespace}/data" psql -d "$URL" --set tablespace="${tablespace}" --set directory="${tablespacedir}" --set ON_ERROR_STOP=1 <<__SQL__ SET synchronous_commit to 'off'; diff --git a/charts/timescaledb-single/scripts/restore_or_initdb.sh b/charts/timescaledb-single/scripts/restore_or_initdb.sh index 7b9a9e97..0632099e 100644 --- a/charts/timescaledb-single/scripts/restore_or_initdb.sh +++ b/charts/timescaledb-single/scripts/restore_or_initdb.sh @@ -17,8 +17,9 @@ log() { # A missing PGDATA points to Patroni removing a botched PGDATA, or manual # intervention. In this scenario, we need to recreate the DATA and WALDIRs # to keep pgBackRest happy -[ -d "${PGDATA}" ] || install -o postgres -g postgres -d -m 0700 "${PGDATA}" -[ -d "${WALDIR}" ] || install -o postgres -g postgres -d -m 0700 "${WALDIR}" +uid="$(id -u)"; gid="$(id -g)" +[ -d "${PGDATA}" ] || install -o $uid -g $gid -d -m 0700 "${PGDATA}" +[ -d "${WALDIR}" ] || install -o $uid -g $gid -d -m 0700 "${WALDIR}" if [ "${BOOTSTRAP_FROM_BACKUP}" = "1" ]; then log "Attempting restore from backup" @@ -104,7 +105,11 @@ else log "Invoking initdb" # shellcheck disable=SC2086 - initdb --auth-local=peer --auth-host=md5 --pgdata="${PGDATA}" --waldir="${WALDIR}" ${initdb_args} + initdb --username="$POSTGRES_USER" --auth-local=peer --auth-host=md5 --pgdata="${PGDATA}" --waldir="${WALDIR}" ${initdb_args} + EXITCODE=$? + if [ ${EXITCODE} -ne 0 ]; then + exit $EXITCODE + fi fi echo "include_if_exists = '${TSTUNE_FILE}'" >> "${PGDATA}/postgresql.conf" diff --git a/charts/timescaledb-single/templates/_helpers.tpl b/charts/timescaledb-single/templates/_helpers.tpl index d0610b6c..e6ee3bd5 100644 --- a/charts/timescaledb-single/templates/_helpers.tpl +++ b/charts/timescaledb-single/templates/_helpers.tpl @@ -39,10 +39,6 @@ Create the name of the service account to use. {{- end -}} {{- end -}} -{{- define "postgres.uid" -}} -{{- default .Values.uid "1000" -}} -{{- end -}} - {{- define "data_directory" -}} {{ printf "%s/data" .Values.persistentVolumes.data.mountPath }} {{- end -}} diff --git a/charts/timescaledb-single/templates/statefulset-timescaledb.yaml b/charts/timescaledb-single/templates/statefulset-timescaledb.yaml index 7e63437d..6538714b 100644 --- a/charts/timescaledb-single/templates/statefulset-timescaledb.yaml +++ b/charts/timescaledb-single/templates/statefulset-timescaledb.yaml @@ -38,12 +38,7 @@ spec: spec: serviceAccountName: {{ template "timescaledb.serviceAccountName" . }} securityContext: - # The postgres user inside the TimescaleDB image has uid=1000. - # This configuration ensures the permissions of the mounts are suitable - fsGroup: {{ template "postgres.uid" }} - runAsGroup: {{ template "postgres.uid" }} - runAsNonRoot: true - runAsUser: {{ template "postgres.uid" }} + {{- toYaml .Values.podSecurityContext | nindent 8 }} initContainers: {{- if .Values.timescaledbTune.enabled }} - name: tstune @@ -133,10 +128,12 @@ spec: - "-c" - | {{ .Values.debug.execStartPre }} - install -o postgres -g postgres -d -m 0700 {{ include "data_directory" . | quote }} {{ include "wal_directory" . | quote }} || exit 1 - TABLESPACES={{ $.Values.persistentVolumes.tablespaces | default dict | keys | join " " | quote }} - for tablespace in {{ $.Values.persistentVolumes.tablespaces | default dict | keys | join " " }}; do - install -o postgres -g postgres -d -m 0700 "{{ include "tablespaces_dir" . }}/${tablespace}/data" + uid="$(id -u)"; gid="$(id -g)" + install -o $uid -g $gid -d -m 0700 {{ include "data_directory" . | quote }} {{ include "wal_directory" . | quote }} || exit 1 + + : "${POSTGRES_TABLESPACES:=""}" + for tablespace in $POSTGRES_TABLESPACES; do + install -o $uid -g $gid -d -m 0700 "{{ include "tablespaces_dir" . }}/${tablespace}/data" done # Environment variables can be read by regular users of PostgreSQL. Especially in a Kubernetes @@ -154,7 +151,7 @@ spec: export -p > "${HOME}/.pod_environment" export -p | grep PGBACKREST > "${HOME}/.pgbackrest_environment" - for UNKNOWNVAR in $(env | awk -F '=' '!/^(PATRONI_.*|HOME|PGDATA|PGHOST|LC_.*|LANG|PATH|KUBERNETES_SERVICE_.*|AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE)=/ {print $1}') + for UNKNOWNVAR in $(env | awk -F '=' '!/^(PATRONI_.*|HOME|PGDATA|PGHOST|PGUSER|LC_.*|LANG|PATH|KUBERNETES_SERVICE_.*|AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE)=/ {print $1}') do unset "${UNKNOWNVAR}" done @@ -167,6 +164,11 @@ spec: export PATRONI_POSTGRESQL_PGPASS="${HOME}/.pgpass.patroni" + # "patroni" does not set the user correctly on connect to postgres and authentication will be rejected + # "initdb" is particular about the current user existing in "/etc/passwd" + # so we use "nss_wrapper" to fake that + . docker-entrypoint.sh && setup_nss_wrapper + exec patroni /etc/timescaledb/patroni.yaml env: # We use mixed case environment variables for Patroni User management, @@ -178,6 +180,8 @@ spec: value: "/etc/timescaledb/patroni.yaml" - name: PATRONI_admin_OPTIONS value: createrole,createdb + - name: PATRONI_SUPERUSER_USERNAME + value: postgres - name: PATRONI_REPLICATION_USERNAME value: standby # To specify the PostgreSQL and Rest API connect addresses we need @@ -223,6 +227,10 @@ spec: value: "/var/run/postgresql" - name: WALDIR value: {{ include "wal_directory" . | quote }} + - name: PGUSER + value: postgres + - name: POSTGRES_USER + value: postgres - name: BOOTSTRAP_FROM_BACKUP value: {{ .Values.bootstrapFromBackup.enabled | int | quote }} {{- if .Values.bootstrapFromBackup.enabled }} @@ -233,6 +241,8 @@ spec: value: {{ .Values.backup.enabled | quote }} - name: TSTUNE_FILE value: {{ template "tstune_config" . }} + - name: HOME + value: /home/postgres {{- if .Values.env }}{{ .Values.env | default list | toYaml | nindent 8 }}{{- end }} # pgBackRest is also called using the archive_command if the backup is enabled. # this script will also need access to the environment variables specified for @@ -304,6 +314,8 @@ spec: - mountPath: /etc/certificate name: certificate readOnly: true + - name: home + mountPath: /home/postgres - name: socket-directory mountPath: /var/run/postgresql {{ if .Values.callbacks.configMap -}} @@ -473,6 +485,8 @@ spec: {{- .Values.affinity | toYaml | nindent 8 }} {{- end }} volumes: + - name: home + emptyDir: {} - name: socket-directory emptyDir: {} - name: patroni-config diff --git a/charts/timescaledb-single/values.schema.json b/charts/timescaledb-single/values.schema.json index a65ec74b..4d192d61 100644 --- a/charts/timescaledb-single/values.schema.json +++ b/charts/timescaledb-single/values.schema.json @@ -571,6 +571,24 @@ "podMonitor": { "type": "object" }, + "podSecurityContext": { + "additionalProperties": true, + "properties": { + "fsGroup": { + "type": "integer" + }, + "runAsGroup": { + "type": "integer" + }, + "runAsNonRoot": { + "type": "boolean" + }, + "runAsUser": { + "type": "integer" + } + }, + "type": "object" + }, "postInit": { "description": "postInit allows you to configure additional scripts that will be run once\ndirectly after initialization of the database. It takes a list of VolumeProjection,\nevery .sh, .sql, and .sql.gz script will be executed in sorted order and they should\nreturn exitcode 0 on success.\n", "items": { @@ -826,6 +844,7 @@ "podAnnotations", "podLabels", "podManagementPolicy", + "podSecurityContext", "prometheus", "rbac", "readinessProbe", diff --git a/charts/timescaledb-single/values.schema.yaml b/charts/timescaledb-single/values.schema.yaml index dca9835f..d8e71b97 100644 --- a/charts/timescaledb-single/values.schema.yaml +++ b/charts/timescaledb-single/values.schema.yaml @@ -26,6 +26,7 @@ required: - podAnnotations - podLabels - podManagementPolicy + - podSecurityContext - prometheus - rbac - readinessProbe @@ -45,6 +46,18 @@ properties: podManagementPolicy: type: - string + podSecurityContext: + type: object + additionalProperties: true + properties: + fsGroup: + type: integer + runAsGroup: + type: integer + runAsNonRoot: + type: boolean + runAsUser: + type: integer postInit: description: | postInit allows you to configure additional scripts that will be run once diff --git a/charts/timescaledb-single/values.yaml b/charts/timescaledb-single/values.yaml index 00296895..8d94f78b 100644 --- a/charts/timescaledb-single/values.yaml +++ b/charts/timescaledb-single/values.yaml @@ -405,6 +405,14 @@ persistentVolumes: # size: 5Gi # storageClass: gp2 +podSecurityContext: + # The postgres user inside the TimescaleDB image has uid=1000. + # This configuration ensures the permissions of the mounts are suitable + fsGroup: 1000 + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + resources: {} # If you do want to specify resources, uncomment the following # lines, adjust them as necessary, and remove the curly braces after 'resources:'.