Skip to content

Commit

Permalink
PGAdmin user passwords are now stored in secrets created by customer …
Browse files Browse the repository at this point in the history
…and secret is referenced in the user spec. Updated/added appropriate go and kuttl tests.
  • Loading branch information
dsessler7 committed Apr 23, 2024
1 parent e5dd23f commit 6180d3c
Show file tree
Hide file tree
Showing 24 changed files with 720 additions and 63 deletions.
3 changes: 3 additions & 0 deletions build/crd/pgadmins/todos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@
- op: copy
from: /work
path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/config/properties/ldapBindPassword/properties/name/description
- op: copy
from: /work
path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/users/items/properties/passwordRef/properties/name/description
- op: remove
path: /work
19 changes: 19 additions & 0 deletions config/crd/bases/postgres-operator.crunchydata.com_pgadmins.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1448,6 +1448,24 @@ spec:
not show up here.
items:
properties:
passwordRef:
description: A reference to the secret that holds the user's
password.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
role:
description: Role determines whether the user has admin privileges
or not. Defaults to User. Valid options are Administrator
Expand All @@ -1461,6 +1479,7 @@ spec:
in the pgAdmin's users list.
type: string
required:
- passwordRef
- username
type: object
type: array
Expand Down
32 changes: 4 additions & 28 deletions internal/controller/standalone_pgadmin/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,9 @@ import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/source"

controllerruntime "github.com/crunchydata/postgres-operator/internal/controller/runtime"
Expand Down Expand Up @@ -75,34 +72,13 @@ func (r *PGAdminReconciler) SetupWithManager(mgr ctrl.Manager) error {
&source.Kind{Type: v1beta1.NewPostgresCluster()},
r.watchPostgresClusters(),
).
Watches(
&source.Kind{Type: &corev1.Secret{}},
r.watchForRelatedSecret(),
).
Complete(r)
}

// watchPostgresClusters returns a [handler.EventHandler] for PostgresClusters.
func (r *PGAdminReconciler) watchPostgresClusters() handler.Funcs {
handle := func(cluster client.Object, q workqueue.RateLimitingInterface) {
ctx := context.Background()
for _, pgadmin := range r.findPGAdminsForPostgresCluster(ctx, cluster) {

q.Add(ctrl.Request{
NamespacedName: client.ObjectKeyFromObject(pgadmin),
})
}
}

return handler.Funcs{
CreateFunc: func(e event.CreateEvent, q workqueue.RateLimitingInterface) {
handle(e.Object, q)
},
UpdateFunc: func(e event.UpdateEvent, q workqueue.RateLimitingInterface) {
handle(e.ObjectNew, q)
},
DeleteFunc: func(e event.DeleteEvent, q workqueue.RateLimitingInterface) {
handle(e.Object, q)
},
}
}

//+kubebuilder:rbac:groups="postgres-operator.crunchydata.com",resources="pgadmins",verbs={get}
//+kubebuilder:rbac:groups="postgres-operator.crunchydata.com",resources="pgadmins/status",verbs={patch}

Expand Down
69 changes: 48 additions & 21 deletions internal/controller/standalone_pgadmin/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ import (

"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/crunchydata/postgres-operator/internal/logging"
"github.com/crunchydata/postgres-operator/internal/naming"
"github.com/crunchydata/postgres-operator/internal/util"
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
)

Expand Down Expand Up @@ -196,25 +196,45 @@ cd $PGADMIN_DIR
isAdmin = true
}

// Get password from secret
userPasswordSecret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{
Namespace: pgadmin.Namespace,
Name: user.PasswordRef.LocalObjectReference.Name,
}}
err := errors.WithStack(
r.Client.Get(ctx, client.ObjectKeyFromObject(userPasswordSecret), userPasswordSecret))
if err != nil {
log.Error(err, "Could not get user password secret")
continue
}

// Make sure the password isn't nil or empty
password := userPasswordSecret.Data[user.PasswordRef.Key]
if password == nil {
log.Error(nil, `Could not retrieve password from secret. Make sure secret name and key are correct.`)
continue
}
if len(password) == 0 {
log.Error(nil, `Password must not be empty.`)
continue
}

// Assemble user that will be used in add/update command and in updating
// the users.json file in the secret
intentUser := pgAdminUserForJson{
Username: user.Username,
Password: "",
Password: string(password),
IsAdmin: isAdmin,
}
// If the user already exists in users.json, and isAdmin has changed, run
// the update-user command. If the user already exists in users.json, but
// it hasn't changed, do nothing. If the user doesn't exist in users.json,
// run the add-user command.
// If the user already exists in users.json and isAdmin or password has
// changed, run the update-user command. If the user already exists in
// users.json, but it hasn't changed, do nothing. If the user doesn't
// exist in users.json, run the add-user command.
if existingUser, present := existingUsersMap[user.Username]; present {
// Set password for intentUser
intentUser.Password = existingUser.Password

if intentUser.IsAdmin != existingUser.IsAdmin {
// Attempt update-user command
script := setupScript + fmt.Sprintf(`python3 setup.py update-user %s "%s"`,
typeFlag, intentUser.Username) + "\n"
// If Password or IsAdmin have changed, attempt update-user command
if intentUser.IsAdmin != existingUser.IsAdmin || intentUser.Password != existingUser.Password {
script := setupScript + fmt.Sprintf(`python3 setup.py update-user %s --password "%s" "%s"`,
typeFlag, intentUser.Password, intentUser.Username) + "\n"
err = exec(ctx, &stdin, &stdout, &stderr,
[]string{"bash", "-ceu", "--", script}...)

Expand All @@ -231,16 +251,23 @@ cd $PGADMIN_DIR
intentUsers = append(intentUsers, existingUser)
continue
}
// If update user fails due to user not found or password length:
// https://github.com/pgadmin-org/pgadmin4/blob/REL-8_5/web/setup.py#L263
// https://github.com/pgadmin-org/pgadmin4/blob/REL-8_5/web/setup.py#L246
if strings.Contains(stdout.String(), "User not found") ||
strings.Contains(stdout.String(), "Password must be") {

log.Info("Failed to update pgAdmin user", "user", intentUser.Username, "error", stdout.String())
r.Recorder.Event(pgadmin,
corev1.EventTypeWarning, "InvalidUserWarning",
fmt.Sprintf("Failed to update pgAdmin user %s: %s",
intentUser.Username, stdout.String()))
intentUsers = append(intentUsers, existingUser)
continue
}
}
} else {
// New user, so generate a password and set it on intentUser
password, err := util.GenerateASCIIPassword(util.DefaultGeneratedPasswordLength)
if err != nil {
return err
}
intentUser.Password = password

// Attempt add-user command
// New user, so attempt add-user command
script := setupScript + fmt.Sprintf(`python3 setup.py add-user %s -- "%s" "%s"`,
typeFlag, intentUser.Username, intentUser.Password) + "\n"
err = exec(ctx, &stdin, &stdout, &stderr,
Expand Down

0 comments on commit 6180d3c

Please sign in to comment.