Skip to content

Commit e933908

Browse files
Configure pg_hba in the local postgresql configuration of Patroni. (#361)
Previously, the operator put pg_hba into the bootstrap/pg_hba key of Patroni. That had 2 adverse effects: - pg_hba.conf was shadowed by Spilo default section in the local postgresql configuration - when updating pg_hba in the cluster manifest, the updated lines were not propagated to DCS, since the key was defined in the boostrap section of Patroni. Include some minor refactoring, moving methods to unexported when possible and commenting out usage of md5, so that gosec won't complain. Per #330 Review by @zerg-junior
1 parent 199aa65 commit e933908

File tree

6 files changed

+55
-52
lines changed

6 files changed

+55
-52
lines changed

pkg/cluster/cluster.go

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec spec.Postgresql
119119
}
120120
cluster.logger = logger.WithField("pkg", "cluster").WithField("cluster-name", cluster.clusterName())
121121
cluster.teamsAPIClient = teams.NewTeamsAPI(cfg.OpConfig.TeamsAPIUrl, logger)
122-
cluster.oauthTokenGetter = NewSecretOauthTokenGetter(&kubeClient, cfg.OpConfig.OAuthTokenSecretName)
122+
cluster.oauthTokenGetter = newSecretOauthTokenGetter(&kubeClient, cfg.OpConfig.OAuthTokenSecretName)
123123
cluster.patroni = patroni.New(cluster.logger)
124124

125125
return cluster
@@ -404,15 +404,15 @@ func (c *Cluster) compareStatefulSetWith(statefulSet *v1beta1.StatefulSet) *comp
404404
return &compareStatefulsetResult{match: match, reasons: reasons, rollingUpdate: needsRollUpdate, replace: needsReplace}
405405
}
406406

407-
type ContainerCondition func(a, b v1.Container) bool
407+
type containerCondition func(a, b v1.Container) bool
408408

409-
type ContainerCheck struct {
410-
condition ContainerCondition
409+
type containerCheck struct {
410+
condition containerCondition
411411
reason string
412412
}
413413

414-
func NewCheck(msg string, cond ContainerCondition) ContainerCheck {
415-
return ContainerCheck{reason: msg, condition: cond}
414+
func newCheck(msg string, cond containerCondition) containerCheck {
415+
return containerCheck{reason: msg, condition: cond}
416416
}
417417

418418
// compareContainers: compare containers from two stateful sets
@@ -422,18 +422,18 @@ func NewCheck(msg string, cond ContainerCondition) ContainerCheck {
422422
func (c *Cluster) compareContainers(setA, setB *v1beta1.StatefulSet) (bool, []string) {
423423
reasons := make([]string, 0)
424424
needsRollUpdate := false
425-
checks := []ContainerCheck{
426-
NewCheck("new statefulset's container %d name doesn't match the current one",
425+
checks := []containerCheck{
426+
newCheck("new statefulset's container %d name doesn't match the current one",
427427
func(a, b v1.Container) bool { return a.Name != b.Name }),
428-
NewCheck("new statefulset's container %d image doesn't match the current one",
428+
newCheck("new statefulset's container %d image doesn't match the current one",
429429
func(a, b v1.Container) bool { return a.Image != b.Image }),
430-
NewCheck("new statefulset's container %d ports don't match the current one",
430+
newCheck("new statefulset's container %d ports don't match the current one",
431431
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.Ports, b.Ports) }),
432-
NewCheck("new statefulset's container %d resources don't match the current ones",
432+
newCheck("new statefulset's container %d resources don't match the current ones",
433433
func(a, b v1.Container) bool { return !compareResources(&a.Resources, &b.Resources) }),
434-
NewCheck("new statefulset's container %d environment doesn't match the current one",
434+
newCheck("new statefulset's container %d environment doesn't match the current one",
435435
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.Env, b.Env) }),
436-
NewCheck("new statefulset's container %d environment sources don't match the current one",
436+
newCheck("new statefulset's container %d environment sources don't match the current one",
437437
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.EnvFrom, b.EnvFrom) }),
438438
}
439439

@@ -630,6 +630,7 @@ func (c *Cluster) Delete() {
630630
}
631631
}
632632

633+
//NeedsRepair returns true if the cluster should be included in the repair scan (based on its in-memory status).
633634
func (c *Cluster) NeedsRepair() (bool, spec.PostgresStatus) {
634635
c.specMu.RLock()
635636
defer c.specMu.RUnlock()
@@ -905,9 +906,9 @@ func (c *Cluster) shouldDeleteSecret(secret *v1.Secret) (delete bool, userName s
905906

906907
type simpleActionWithResult func() error
907908

908-
type ClusterObjectGet func(name string) (spec.NamespacedName, error)
909+
type clusterObjectGet func(name string) (spec.NamespacedName, error)
909910

910-
type ClusterObjectDelete func(name string) error
911+
type clusterObjectDelete func(name string) error
911912

912913
func (c *Cluster) deletePatroniClusterObjects() error {
913914
// TODO: figure out how to remove leftover patroni objects in other cases
@@ -924,8 +925,8 @@ func (c *Cluster) deletePatroniClusterObjects() error {
924925
}
925926

926927
func (c *Cluster) deleteClusterObject(
927-
get ClusterObjectGet,
928-
del ClusterObjectDelete,
928+
get clusterObjectGet,
929+
del clusterObjectDelete,
929930
objType string) error {
930931
for _, suffix := range patroniObjectSuffixes {
931932
name := fmt.Sprintf("%s-%s", c.Name, suffix)

pkg/cluster/k8sres.go

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const (
2525
pgBinariesLocationTemplate = "/usr/lib/postgresql/%s/bin"
2626
patroniPGBinariesParameterName = "bin_dir"
2727
patroniPGParametersParameterName = "parameters"
28+
patroniPGHBAConfParameterName = "pg_hba"
2829
localHost = "127.0.0.1/32"
2930
)
3031

@@ -44,7 +45,6 @@ type patroniDCS struct {
4445
type pgBootstrap struct {
4546
Initdb []interface{} `json:"initdb"`
4647
Users map[string]pgUser `json:"users"`
47-
PgHBA []string `json:"pg_hba"`
4848
DCS patroniDCS `json:"dcs,omitempty"`
4949
}
5050

@@ -202,19 +202,6 @@ PatroniInitDBParams:
202202
config.Bootstrap.Initdb = append(config.Bootstrap.Initdb, map[string]string{k: v})
203203
}
204204

205-
// pg_hba parameters in the manifest replace the default ones. We cannot
206-
// reasonably merge them automatically, because pg_hba parsing stops on
207-
// a first successfully matched rule.
208-
if len(patroni.PgHba) > 0 {
209-
config.Bootstrap.PgHBA = patroni.PgHba
210-
} else {
211-
config.Bootstrap.PgHBA = []string{
212-
"hostnossl all all all reject",
213-
fmt.Sprintf("hostssl all +%s all pam", pamRoleName),
214-
"hostssl all all all md5",
215-
}
216-
}
217-
218205
if patroni.MaximumLagOnFailover >= 0 {
219206
config.Bootstrap.DCS.MaximumLagOnFailover = patroni.MaximumLagOnFailover
220207
}
@@ -231,23 +218,23 @@ PatroniInitDBParams:
231218
config.PgLocalConfiguration = make(map[string]interface{})
232219
config.PgLocalConfiguration[patroniPGBinariesParameterName] = fmt.Sprintf(pgBinariesLocationTemplate, pg.PgVersion)
233220
if len(pg.Parameters) > 0 {
234-
localParameters := make(map[string]string)
235-
bootstrapParameters := make(map[string]string)
236-
for param, val := range pg.Parameters {
237-
if isBootstrapOnlyParameter(param) {
238-
bootstrapParameters[param] = val
239-
} else {
240-
localParameters[param] = val
241-
}
242-
}
243-
if len(localParameters) > 0 {
244-
config.PgLocalConfiguration[patroniPGParametersParameterName] = localParameters
221+
local, bootstrap := getLocalAndBoostrapPostgreSQLParameters(pg.Parameters)
222+
223+
if len(local) > 0 {
224+
config.PgLocalConfiguration[patroniPGParametersParameterName] = local
245225
}
246-
if len(bootstrapParameters) > 0 {
226+
if len(bootstrap) > 0 {
247227
config.Bootstrap.DCS.PGBootstrapConfiguration = make(map[string]interface{})
248-
config.Bootstrap.DCS.PGBootstrapConfiguration[patroniPGParametersParameterName] = bootstrapParameters
228+
config.Bootstrap.DCS.PGBootstrapConfiguration[patroniPGParametersParameterName] = bootstrap
249229
}
250230
}
231+
// Patroni gives us a choice of writing pg_hba.conf to either the bootstrap section or to the local postgresql one.
232+
// We choose the local one, because we need Patroni to change pg_hba.conf in PostgreSQL after the user changes the
233+
// relevant section in the manifest.
234+
if len(patroni.PgHba) > 0 {
235+
config.PgLocalConfiguration[patroniPGHBAConfParameterName] = patroni.PgHba
236+
}
237+
251238
config.Bootstrap.Users = map[string]pgUser{
252239
pamRoleName: {
253240
Password: "",
@@ -262,6 +249,19 @@ PatroniInitDBParams:
262249
return string(result)
263250
}
264251

252+
func getLocalAndBoostrapPostgreSQLParameters(parameters map[string]string) (local, bootstrap map[string]string) {
253+
local = make(map[string]string)
254+
bootstrap = make(map[string]string)
255+
for param, val := range parameters {
256+
if isBootstrapOnlyParameter(param) {
257+
bootstrap[param] = val
258+
} else {
259+
local[param] = val
260+
}
261+
}
262+
return
263+
}
264+
265265
func nodeAffinity(nodeReadinessLabel map[string]string) *v1.Affinity {
266266
matchExpressions := make([]v1.NodeSelectorRequirement, 0)
267267
if len(nodeReadinessLabel) == 0 {
@@ -736,7 +736,7 @@ func (c *Cluster) generateStatefulSet(spec *spec.PostgresSpec) (*v1beta1.Statefu
736736
Name: c.statefulSetName(),
737737
Namespace: c.Namespace,
738738
Labels: c.labelsSet(true),
739-
Annotations: map[string]string{RollingUpdateStatefulsetAnnotationKey: "false"},
739+
Annotations: map[string]string{rollingUpdateStatefulsetAnnotationKey: "false"},
740740
},
741741
Spec: v1beta1.StatefulSetSpec{
742742
Replicas: &numberOfInstances,

pkg/cluster/resources.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
)
1919

2020
const (
21-
RollingUpdateStatefulsetAnnotationKey = "zalando-postgres-operator-rolling-update-required"
21+
rollingUpdateStatefulsetAnnotationKey = "zalando-postgres-operator-rolling-update-required"
2222
)
2323

2424
func (c *Cluster) listResources() error {
@@ -140,7 +140,7 @@ func (c *Cluster) setRollingUpdateFlagForStatefulSet(sset *v1beta1.StatefulSet,
140140
if anno == nil {
141141
anno = make(map[string]string)
142142
}
143-
anno[RollingUpdateStatefulsetAnnotationKey] = strconv.FormatBool(val)
143+
anno[rollingUpdateStatefulsetAnnotationKey] = strconv.FormatBool(val)
144144
sset.SetAnnotations(anno)
145145
}
146146

@@ -162,12 +162,12 @@ func (c *Cluster) getRollingUpdateFlagFromStatefulSet(sset *v1beta1.StatefulSet,
162162
anno := sset.GetAnnotations()
163163
flag = defaultValue
164164

165-
stringFlag, exists := anno[RollingUpdateStatefulsetAnnotationKey]
165+
stringFlag, exists := anno[rollingUpdateStatefulsetAnnotationKey]
166166
if exists {
167167
var err error
168168
if flag, err = strconv.ParseBool(stringFlag); err != nil {
169169
c.logger.Warnf("error when parsing %q annotation for the statefulset %q: expected boolean value, got %q\n",
170-
RollingUpdateStatefulsetAnnotationKey,
170+
rollingUpdateStatefulsetAnnotationKey,
171171
types.NamespacedName{Namespace: sset.Namespace, Name: sset.Name},
172172
stringFlag)
173173
flag = defaultValue

pkg/cluster/util.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ type SecretOauthTokenGetter struct {
3535
OAuthTokenSecretName spec.NamespacedName
3636
}
3737

38-
func NewSecretOauthTokenGetter(kubeClient *k8sutil.KubernetesClient,
38+
func newSecretOauthTokenGetter(kubeClient *k8sutil.KubernetesClient,
3939
OAuthTokenSecretName spec.NamespacedName) *SecretOauthTokenGetter {
4040
return &SecretOauthTokenGetter{kubeClient, OAuthTokenSecretName}
4141
}

pkg/spec/postgresql.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ type Sidecar struct {
7171
Env []v1.EnvVar `json:"env,omitempty"`
7272
}
7373

74+
// UserFlags defines flags (such as superuser, nologin) that could be assigned to individual users
7475
type UserFlags []string
7576

7677
// PostgresStatus contains status of the PostgreSQL cluster (running, creation failed etc.)

pkg/util/util.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package util
22

33
import (
4-
"crypto/md5"
4+
"crypto/md5" // #nosec we need it to for PostgreSQL md5 passwords
55
"encoding/hex"
66
"math/rand"
77
"regexp"
@@ -48,7 +48,7 @@ func PGUserPassword(user spec.PgUser) string {
4848
// Avoid processing already encrypted or empty passwords
4949
return user.Password
5050
}
51-
s := md5.Sum([]byte(user.Password + user.Name))
51+
s := md5.Sum([]byte(user.Password + user.Name)) // #nosec, using md5 since PostgreSQL uses it for hashing passwords.
5252
return md5prefix + hex.EncodeToString(s[:])
5353
}
5454

@@ -120,6 +120,7 @@ func MapContains(haystack, needle map[string]string) bool {
120120
return true
121121
}
122122

123+
// Coalesce returns the first argument if it is not null, otherwise the second one.
123124
func Coalesce(val, defaultVal string) string {
124125
if val == "" {
125126
return defaultVal

0 commit comments

Comments
 (0)