diff --git a/docs/migrate.md b/docs/migrate.md index 33aa847c6..5504ffd9b 100644 --- a/docs/migrate.md +++ b/docs/migrate.md @@ -4,9 +4,11 @@ Version 2.0 changes some default settings and removes deprecated fields. Please ## scram-sha-256 by default -The new operator will default password encryption to `scram-sha-256`. Unless you configure `password_encryption: md5` in the manifest under `spec.postgresql.parameters` the operator will encrypt existing passwords in the secrets with `scram-sha-256` and alter the database passwords. Make sure that your used clients and drivers support `scram-sha-256` as pods will get rotated in rolling fashion after updating to Postgres Operator v2. +The v2 operator will default password encryption to `scram-sha-256`. Unless you configure `password_encryption: md5` in the manifest under `spec.postgresql.parameters` the operator will encrypt existing passwords in the managed K8s secrets with `scram-sha-256` and alter the respective database users. Make sure that your clients and drivers who rely on these credentials support `scram-sha-256` as pods will get rotated in rolling fashion after updating to Postgres Operator v2. -The default Spilo image (`spilo-18:4.1-p2`) still configures the pg_hba.conf file to allow `md5` passwords but Postgres will validate `scram-sha-256` passwords correctly. Passwords of users that are not managed by the operator and are still `md5` encrypted need be altered before the next tagged Spilo image which will drop `md5` completely. +For backwards compatibility, the current default Spilo image (`spilo-18:4.1-p2`) still configures the pg_hba.conf file to allow `md5` passwords but Postgres will validate new `scram-sha-256` passwords correctly. This means you can switch to `scram-sha-256` for manifest users, while still allowing unmanaged users to connect via `md5`. The compatibility does not work for connections via pgBouncer that rely on `md5`. In this case you have to configure `password_encryption: md5` in the manifest. + +In general, make sure to alter passwords of users that are not managed by the operator and are still `md5` encrypted before the release of next tagged Spilo image which will drop `md5` completely. ## K8s Endpoints are deprecated diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index d65aae20d..fa798c4d7 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -137,10 +137,7 @@ func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec acidv1.Postgres podEventsStore := cache.NewStore(keyFn) podEventsQueue := cache.NewFIFO(keyFn) - passwordEncryption, ok := pgSpec.Spec.PostgresqlParam.Parameters["password_encryption"] - if !ok { - passwordEncryption = "scram-sha-256" - } + passwordEncryption := passwordEncryptionFromSpec(&pgSpec.Spec) cluster := &Cluster{ Config: cfg, @@ -158,7 +155,7 @@ func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec acidv1.Postgres Streams: make(map[string]*zalandov1.FabricEventStream), }, userSyncStrategy: users.DefaultUserSyncStrategy{ - PasswordEncryption: passwordEncryption, + PasswordEncryption: string(passwordEncryption), RoleDeletionSuffix: cfg.OpConfig.RoleDeletionSuffix, AdditionalOwnerRoles: cfg.OpConfig.AdditionalOwnerRoles, }, diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index 89325e1fc..4a05a1143 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -253,6 +253,8 @@ func (c *Cluster) getConnectionPoolerEnvVars() []v1.EnvVar { minSize := defaultSize / 2 reserveSize := minSize + passwordEncryption := passwordEncryptionFromSpec(spec) + return []v1.EnvVar{ { Name: "CONNECTION_POOLER_PORT", @@ -262,6 +264,10 @@ func (c *Cluster) getConnectionPoolerEnvVars() []v1.EnvVar { Name: "CONNECTION_POOLER_MODE", Value: effectiveMode, }, + { + Name: "CONNECTION_POOLER_AUTH_TYPE", + Value: string(passwordEncryption), + }, { Name: "CONNECTION_POOLER_DEFAULT_SIZE", Value: fmt.Sprint(defaultSize), @@ -912,6 +918,8 @@ func (c *Cluster) needSyncConnectionPoolerDefaults(Config *Config, spec *acidv1. return false, reasons } + authTypeFound := false + passwordEncryption := passwordEncryptionFromSpec(&c.Spec) for _, env := range poolerContainer.Env { if spec.User == "" && env.Name == "PGUSER" { ref := env.ValueFrom.SecretKeyRef.LocalObjectReference @@ -935,6 +943,22 @@ func (c *Cluster) needSyncConnectionPoolerDefaults(Config *Config, spec *acidv1. env.Value, config.Schema) reasons = append(reasons, msg) } + + if env.Name == "CONNECTION_POOLER_AUTH_TYPE" { + authTypeFound = true + if string(passwordEncryption) != env.Value { + sync = true + msg := fmt.Sprintf("pooler auth type is different (having %s, required %s)", + env.Value, passwordEncryption) + reasons = append(reasons, msg) + } + } + } + + // env var is missing on deployments created before it was introduced + if !authTypeFound { + sync = true + reasons = append(reasons, "pooler auth type env variable is missing") } return sync, reasons diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 6cb0e085c..3200dc2e7 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -539,13 +539,14 @@ func TestCronjobEnvironmentSecretVariables(t *testing.T) { func testEnvs(cluster *Cluster, podSpec *v1.PodTemplateSpec, role PostgresRole) error { required := map[string]bool{ - "PGHOST": false, - "PGPORT": false, - "PGUSER": false, - "PGSCHEMA": false, - "PGPASSWORD": false, - "CONNECTION_POOLER_MODE": false, - "CONNECTION_POOLER_PORT": false, + "PGHOST": false, + "PGPORT": false, + "PGUSER": false, + "PGSCHEMA": false, + "PGPASSWORD": false, + "CONNECTION_POOLER_MODE": false, + "CONNECTION_POOLER_PORT": false, + "CONNECTION_POOLER_AUTH_TYPE": false, } container := getPostgresContainer(&podSpec.Spec) diff --git a/pkg/cluster/types.go b/pkg/cluster/types.go index 06c1674e3..da3fd29f6 100644 --- a/pkg/cluster/types.go +++ b/pkg/cluster/types.go @@ -82,5 +82,23 @@ type InstallFunction func(schema string, user string) error type SyncReason []string +// PasswordEncryption is the password hashing method used by Postgres and the pooler +type PasswordEncryption string + +const ( + PasswordEncryptionMD5 PasswordEncryption = "md5" + PasswordEncryptionScramSHA256 PasswordEncryption = "scram-sha-256" +) + +// passwordEncryptionFromSpec returns the password_encryption parameter, falling back to scram-sha-256 for unset or unsupported values +func passwordEncryptionFromSpec(spec *acidv1.PostgresSpec) PasswordEncryption { + switch pe := PasswordEncryption(spec.PostgresqlParam.Parameters["password_encryption"]); pe { + case PasswordEncryptionMD5, PasswordEncryptionScramSHA256: + return pe + default: + return PasswordEncryptionScramSHA256 + } +} + // no sync happened, empty value var NoSync SyncReason = []string{} diff --git a/pooler/pgbouncer.ini.tmpl b/pooler/pgbouncer.ini.tmpl index 285caac36..123c39e70 100644 --- a/pooler/pgbouncer.ini.tmpl +++ b/pooler/pgbouncer.ini.tmpl @@ -13,7 +13,7 @@ stats_users = $INFRASTRUCTURE_ROLES auth_dbname = postgres auth_file = /etc/pgbouncer/userlist.txt auth_query = SELECT * FROM $PGSCHEMA.user_lookup($1) -auth_type = scram-sha-256 +auth_type = $CONNECTION_POOLER_AUTH_TYPE logfile = /var/log/pgbouncer/pgbouncer.log pidfile = /var/run/pgbouncer/pgbouncer.pid