diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index ff0698eb87..8ce945a1b3 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -38,4 +38,6 @@ spec: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault serviceAccountName: pgo diff --git a/hack/update-pgmonitor-installer.sh b/hack/update-pgmonitor-installer.sh new file mode 100755 index 0000000000..00591f131f --- /dev/null +++ b/hack/update-pgmonitor-installer.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash + +# Copyright 2022 Crunchy Data Solutions, Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script updates the Kustomize installer for monitoring with the latest Grafana, +# Prometheus and Alert Manager configuration per the pgMonitor tag specified + +directory=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) + +# The pgMonitor tag to use to refresh the current monitoring installer +pgmonitor_tag=v4.6-RC1 + +# Set the directory for the monitoring Kustomize installer +pgo_examples_monitoring_dir="${directory}/../../postgres-operator-examples/kustomize/monitoring" + +# Create a tmp directory for checking out the pgMonitor tag +tmp_dir="${directory}/pgmonitor_tmp/" +mkdir -p "${tmp_dir}" + +# Clone the pgMonitor repo and checkout the tag provided +git -C "${tmp_dir}" clone https://github.com/CrunchyData/pgmonitor.git +cd "${tmp_dir}/pgmonitor" +git checkout "${pgmonitor_tag}" + +# Deviation from pgMonitor default! +# Update "${DS_PROMETHEUS}" to "PROMETHEUS" in all containers dashboards +find "grafana/containers" -type f -exec \ + sed -i 's/${DS_PROMETHEUS}/PROMETHEUS/' {} \; +# Copy Grafana dashboards for containers +cp -r "grafana/containers/." "${pgo_examples_monitoring_dir}/config/grafana/dashboards" + +# Deviation from pgMonitor default! +# Update the dashboard location to the default for the Grafana container. +sed -i 's#/etc/grafana/crunchy_dashboards#/etc/grafana/provisioning/dashboards#' \ + "grafana/linux/crunchy_grafana_dashboards.yml" +cp "grafana/linux/crunchy_grafana_dashboards.yml" "${pgo_examples_monitoring_dir}/config/grafana" + +# Deviation from pgMonitor default! +# Update the URL for the Grafana data source configuration to use env vars for the Prometheus host +# and port. +sed -i 's#localhost:9090#$PROM_HOST:$PROM_PORT#' \ + "grafana/common/crunchy_grafana_datasource.yml" +cp "grafana/common/crunchy_grafana_datasource.yml" "${pgo_examples_monitoring_dir}/config/grafana" + +# Deviation from pgMonitor default! +# Update the URL for the Grafana data source configuration to use env vars for the Prometheus host +# and port. +cp "prometheus/containers/crunchy-prometheus.yml.containers" "prometheus/containers/crunchy-prometheus.yml" +cat << EOF >> prometheus/containers/crunchy-prometheus.yml +alerting: + alertmanagers: + - scheme: http + static_configs: + - targets: + - "crunchy-alertmanager:9093" +EOF +cp "prometheus/containers/crunchy-prometheus.yml" "${pgo_examples_monitoring_dir}/config/prometheus" + +# Copy the default Alert Manager configuration +cp "alertmanager/common/crunchy-alertmanager.yml" "${pgo_examples_monitoring_dir}/config/alertmanager" +cp "prometheus/containers/alert-rules.d/crunchy-alert-rules-pg.yml.containers.example" \ + "${pgo_examples_monitoring_dir}/config/alertmanager/crunchy-alert-rules-pg.yml" + +# Cleanup any temporary resources +rm -rf "${tmp_dir}" diff --git a/internal/controller/postgrescluster/instance_test.go b/internal/controller/postgrescluster/instance_test.go index c20bd9fe10..48546f1e9b 100644 --- a/internal/controller/postgrescluster/instance_test.go +++ b/internal/controller/postgrescluster/instance_test.go @@ -563,6 +563,8 @@ func TestAddPGBackRestToInstancePodSpec(t *testing.T) { privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgbackrest/server name: pgbackrest-server @@ -610,6 +612,8 @@ func TestAddPGBackRestToInstancePodSpec(t *testing.T) { privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgbackrest/server name: pgbackrest-server @@ -665,6 +669,8 @@ func TestAddPGBackRestToInstancePodSpec(t *testing.T) { privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgbackrest/server name: pgbackrest-server @@ -712,6 +718,8 @@ func TestAddPGBackRestToInstancePodSpec(t *testing.T) { privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgbackrest/server name: pgbackrest-server diff --git a/internal/controller/postgrescluster/pgbackrest.go b/internal/controller/postgrescluster/pgbackrest.go index 4c9396ad52..0e8c505060 100644 --- a/internal/controller/postgrescluster/pgbackrest.go +++ b/internal/controller/postgrescluster/pgbackrest.go @@ -2270,8 +2270,8 @@ func (r *Reconciler) reconcileReplicaCreateBackup(ctx context.Context, replicaCreateRepo v1beta1.PGBackRestRepo) error { var replicaCreateRepoStatus *v1beta1.RepoStatus - for i, r := range postgresCluster.Status.PGBackRest.Repos { - if r.Name == replicaCreateRepo.Name { + for i, repo := range postgresCluster.Status.PGBackRest.Repos { + if repo.Name == replicaCreateRepo.Name { replicaCreateRepoStatus = &postgresCluster.Status.PGBackRest.Repos[i] break } @@ -2494,8 +2494,8 @@ func (r *Reconciler) reconcileStanzaCreate(ctx context.Context, return } replicaCreateRepoName := postgresCluster.Spec.Backups.PGBackRest.Repos[0].Name - for i, r := range postgresCluster.Status.PGBackRest.Repos { - if r.Name == replicaCreateRepoName { + for i, repo := range postgresCluster.Status.PGBackRest.Repos { + if repo.Name == replicaCreateRepoName { replicaCreateRepoStatus = &postgresCluster.Status.PGBackRest.Repos[i] break } diff --git a/internal/controller/postgrescluster/pgbackrest_test.go b/internal/controller/postgrescluster/pgbackrest_test.go index 74478155c0..5d68f727fe 100644 --- a/internal/controller/postgrescluster/pgbackrest_test.go +++ b/internal/controller/postgrescluster/pgbackrest_test.go @@ -848,6 +848,11 @@ func TestGetPGBackRestExecSelector(t *testing.T) { } func TestReconcileReplicaCreateBackup(t *testing.T) { + // Garbage collector cleans up test resources before the test completes + if strings.EqualFold(os.Getenv("USE_EXISTING_CLUSTER"), "true") { + t.Skip("USE_EXISTING_CLUSTER: Test fails due to garbage collection") + } + ctx := context.Background() _, tClient := setupKubernetes(t) require.ParallelCapacity(t, 1) @@ -908,6 +913,7 @@ func TestReconcileReplicaCreateBackup(t *testing.T) { // now find the expected job jobs := &batchv1.JobList{} err = tClient.List(ctx, jobs, &client.ListOptions{ + Namespace: postgresCluster.Namespace, LabelSelector: naming.PGBackRestBackupJobSelector(clusterName, replicaCreateRepo.Name, naming.BackupReplicaCreate), }) @@ -994,8 +1000,8 @@ func TestReconcileReplicaCreateBackup(t *testing.T) { // verify the status has been updated properly var replicaCreateRepoStatus *v1beta1.RepoStatus - for i, r := range postgresCluster.Status.PGBackRest.Repos { - if r.Name == replicaCreateRepo.Name { + for i, repo := range postgresCluster.Status.PGBackRest.Repos { + if repo.Name == replicaCreateRepo.Name { replicaCreateRepoStatus = &postgresCluster.Status.PGBackRest.Repos[i] break } @@ -2504,6 +2510,8 @@ containers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgbackrest/conf.d name: pgbackrest-config diff --git a/internal/controller/postgrescluster/volumes_test.go b/internal/controller/postgrescluster/volumes_test.go index b02b136a26..bef2765ac2 100644 --- a/internal/controller/postgrescluster/volumes_test.go +++ b/internal/controller/postgrescluster/volumes_test.go @@ -991,6 +991,8 @@ containers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: @@ -1044,6 +1046,8 @@ containers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: @@ -1099,6 +1103,8 @@ containers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: diff --git a/internal/initialize/security.go b/internal/initialize/security.go index b7cbba14c7..2d17c9881a 100644 --- a/internal/initialize/security.go +++ b/internal/initialize/security.go @@ -20,6 +20,9 @@ import ( ) // RestrictedPodSecurityContext returns a v1.PodSecurityContext with safe defaults. +// Note: All current containers have security context set by `RestrictedSecurityContext` +// which has recommended limits; if more pods/containers are added +// make sure to set the SC on the container // See https://docs.k8s.io/concepts/security/pod-security-standards/ func RestrictedPodSecurityContext() *corev1.PodSecurityContext { return &corev1.PodSecurityContext{ @@ -43,5 +46,12 @@ func RestrictedSecurityContext() *corev1.SecurityContext { // Fail to start the container if its image runs as UID 0 (root). RunAsNonRoot: Bool(true), + + // Restrict syscalls with RuntimeDefault seccomp. + // Set this on the container-level to avoid interfering + // with sidecars and injected containers. + SeccompProfile: &corev1.SeccompProfile{ + Type: corev1.SeccompProfileTypeRuntimeDefault, + }, } } diff --git a/internal/initialize/security_test.go b/internal/initialize/security_test.go index 0ed976eb53..f2473ecd63 100644 --- a/internal/initialize/security_test.go +++ b/internal/initialize/security_test.go @@ -97,8 +97,11 @@ func TestRestrictedSecurityContext(t *testing.T) { "Containers must be required to run as non-root users.") } - assert.Assert(t, sc.SeccompProfile == nil, - "The RuntimeDefault seccomp profile must be required, or allow specific additional profiles.") + if assert.Check(t, sc.SeccompProfile != nil) { + assert.Assert(t, sc.SeccompProfile.Type == "RuntimeDefault", + "Seccomp profile must be explicitly set to one of the allowed values.") + } + }) if assert.Check(t, sc.ReadOnlyRootFilesystem != nil) { diff --git a/internal/pgadmin/reconcile_test.go b/internal/pgadmin/reconcile_test.go index dd981ef5d7..e69393afde 100644 --- a/internal/pgadmin/reconcile_test.go +++ b/internal/pgadmin/reconcile_test.go @@ -241,6 +241,8 @@ containers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgadmin name: pgadmin-startup @@ -278,6 +280,8 @@ initContainers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgadmin name: pgadmin-startup @@ -473,6 +477,8 @@ containers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgadmin name: pgadmin-startup @@ -514,6 +520,8 @@ initContainers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgadmin name: pgadmin-startup diff --git a/internal/pgbackrest/reconcile_test.go b/internal/pgbackrest/reconcile_test.go index 853191432e..171b0cfd0f 100644 --- a/internal/pgbackrest/reconcile_test.go +++ b/internal/pgbackrest/reconcile_test.go @@ -571,6 +571,8 @@ func TestAddServerToInstancePod(t *testing.T) { privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgbackrest/server name: pgbackrest-server @@ -617,6 +619,8 @@ func TestAddServerToInstancePod(t *testing.T) { privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgbackrest/server name: pgbackrest-server @@ -701,6 +705,8 @@ func TestAddServerToRepoPod(t *testing.T) { privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgbackrest/server name: pgbackrest-server @@ -743,6 +749,8 @@ func TestAddServerToRepoPod(t *testing.T) { privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgbackrest/server name: pgbackrest-server diff --git a/internal/pgbouncer/reconcile_test.go b/internal/pgbouncer/reconcile_test.go index 84bdde0b8b..5aec210b9c 100644 --- a/internal/pgbouncer/reconcile_test.go +++ b/internal/pgbouncer/reconcile_test.go @@ -141,6 +141,8 @@ containers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgbouncer name: pgbouncer-config @@ -169,6 +171,8 @@ containers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgbouncer name: pgbouncer-config @@ -245,6 +249,8 @@ containers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgbouncer name: pgbouncer-config @@ -278,6 +284,8 @@ containers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgbouncer name: pgbouncer-config @@ -345,6 +353,8 @@ containers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgbouncer name: pgbouncer-config @@ -377,6 +387,8 @@ containers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /etc/pgbouncer name: pgbouncer-config diff --git a/internal/postgres/reconcile_test.go b/internal/postgres/reconcile_test.go index 2ff453cd41..80f296399b 100644 --- a/internal/postgres/reconcile_test.go +++ b/internal/postgres/reconcile_test.go @@ -143,6 +143,8 @@ containers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /pgconf/tls name: cert-volume @@ -181,6 +183,8 @@ containers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /pgconf/tls name: cert-volume @@ -247,6 +251,8 @@ initContainers: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault volumeMounts: - mountPath: /pgconf/tls name: cert-volume diff --git a/testing/kuttl/e2e/security-context/00-assert.yaml b/testing/kuttl/e2e/security-context/00-assert.yaml index a6a5f48b6a..69f7f85cf6 100644 --- a/testing/kuttl/e2e/security-context/00-assert.yaml +++ b/testing/kuttl/e2e/security-context/00-assert.yaml @@ -35,6 +35,8 @@ spec: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault --- # instance apiVersion: v1 @@ -54,30 +56,40 @@ spec: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault - name: replication-cert-copy securityContext: allowPrivilegeEscalation: false privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault - name: pgbackrest securityContext: allowPrivilegeEscalation: false privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault - name: pgbackrest-config securityContext: allowPrivilegeEscalation: false privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault - name: exporter securityContext: allowPrivilegeEscalation: false privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault initContainers: - name: postgres-startup securityContext: @@ -85,12 +97,16 @@ spec: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault - name: nss-wrapper-init securityContext: allowPrivilegeEscalation: false privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault --- # pgAdmin apiVersion: v1 @@ -110,6 +126,8 @@ spec: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault initContainers: - name: pgadmin-startup securityContext: @@ -117,12 +135,16 @@ spec: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault - name: nss-wrapper-init securityContext: allowPrivilegeEscalation: false privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault --- # pgBouncer apiVersion: v1 @@ -139,12 +161,16 @@ spec: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault - name: pgbouncer-config securityContext: allowPrivilegeEscalation: false privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault --- # pgBackRest repo apiVersion: v1 @@ -165,12 +191,16 @@ spec: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault - name: pgbackrest-config securityContext: allowPrivilegeEscalation: false privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault initContainers: - name: pgbackrest-log-dir securityContext: @@ -178,9 +208,13 @@ spec: privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault - name: nss-wrapper-init securityContext: allowPrivilegeEscalation: false privileged: false readOnlyRootFilesystem: true runAsNonRoot: true + seccompProfile: + type: RuntimeDefault