Skip to content

Commit f940e0f

Browse files
authored
use secret refs for database user passwords (#3005)
1 parent c13420a commit f940e0f

File tree

9 files changed

+173
-18
lines changed

9 files changed

+173
-18
lines changed

config/openapi2crd.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,5 @@ spec:
210210
schema: 'CloudDatabaseUser'
211211
filters:
212212
readWriteOnly: true
213+
sensitiveProperties:
214+
- $.password

internal/generated/controller/databaseuser/handler.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"errors"
2020
"fmt"
2121

22+
v1 "k8s.io/api/core/v1"
2223
controllerruntime "sigs.k8s.io/controller-runtime"
2324
builder "sigs.k8s.io/controller-runtime/pkg/builder"
2425
client "sigs.k8s.io/controller-runtime/pkg/client"
@@ -152,7 +153,7 @@ func (h *Handler) For() (client.Object, builder.Predicates) {
152153
}
153154
func (h *Handler) SetupWithManager(mgr controllerruntime.Manager, rec reconcile.Reconciler, defaultOptions controller.Options) error {
154155
h.Client = mgr.GetClient()
155-
return controllerruntime.NewControllerManagedBy(mgr).Named("DatabaseUser").For(h.For()).Watches(&akov2generated.Group{}, handler.EnqueueRequestsFromMapFunc(indexers.NewDatabaseUserByGroupMapFunc(h.Client)), builder.WithPredicates(predicate.ResourceVersionChangedPredicate{})).WithOptions(defaultOptions).Complete(rec)
156+
return controllerruntime.NewControllerManagedBy(mgr).Named("DatabaseUser").For(h.For()).Watches(&v1.Secret{}, handler.EnqueueRequestsFromMapFunc(indexers.NewDatabaseUserBySecretMapFunc(h.Client)), builder.WithPredicates(predicate.ResourceVersionChangedPredicate{})).Watches(&akov2generated.Group{}, handler.EnqueueRequestsFromMapFunc(indexers.NewDatabaseUserByGroupMapFunc(h.Client)), builder.WithPredicates(predicate.ResourceVersionChangedPredicate{})).WithOptions(defaultOptions).Complete(rec)
156157
}
157158

158159
// getSDKClientSet creates an Atlas SDK client set using credentials from the resource's connection secret

internal/generated/crds/crds.yaml

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1838,6 +1838,20 @@ metadata:
18381838
properties:
18391839
v20250312:
18401840
properties:
1841+
entry:
1842+
properties:
1843+
passwordSecretRef:
1844+
x-kubernetes-mapping:
1845+
nameSelector: .name
1846+
propertySelectors:
1847+
- $.data.#
1848+
type:
1849+
kind: Secret
1850+
resource: secrets
1851+
version: v1
1852+
x-openapi-mapping:
1853+
property: .password
1854+
type: string
18411855
groupRef:
18421856
x-kubernetes-mapping:
18431857
nameSelector: .name
@@ -1956,12 +1970,26 @@ spec:
19561970
authentication group, specify the value of IDP_GROUP in
19571971
this field.
19581972
type: string
1959-
password:
1960-
description: Alphanumeric string that authenticates this database
1961-
user against the database specified in `databaseName`. To
1962-
authenticate with SCRAM-SHA, you must specify this parameter.
1963-
This parameter doesn't appear in this response.
1964-
type: string
1973+
passwordSecretRef:
1974+
description: |-
1975+
SENSITIVE FIELD
1976+
1977+
Reference to a secret containing data for the "password" field:
1978+
1979+
Alphanumeric string that authenticates this database user against the database specified in `databaseName`. To authenticate with SCRAM-SHA, you must specify this parameter. This parameter doesn't appear in this response.
1980+
properties:
1981+
key:
1982+
default: .data.password
1983+
description: Key of the secret data containing the sensitive
1984+
field value, defaults to "password".
1985+
type: string
1986+
name:
1987+
description: Name of the secret containing the sensitive
1988+
field value.
1989+
type: string
1990+
required:
1991+
- name
1992+
type: object
19651993
roles:
19661994
description: List that provides the pairings of one role with
19671995
one applicable database.

internal/generated/indexers/clusterbygroup.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func (i *ClusterByGroupIndexer) Keys(object client.Object) []string {
5353
return nil
5454
}
5555
var keys []string
56-
if resource.Spec.V20250312.GroupRef != nil && resource.Spec.V20250312.GroupRef.Name != "" {
56+
if resource.Spec.V20250312 != nil && resource.Spec.V20250312.GroupRef != nil && resource.Spec.V20250312.GroupRef.Name != "" {
5757
keys = append(keys, types.NamespacedName{
5858
Name: resource.Spec.V20250312.GroupRef.Name,
5959
Namespace: resource.Namespace,

internal/generated/indexers/databaseuserbygroup.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func (i *DatabaseUserByGroupIndexer) Keys(object client.Object) []string {
5353
return nil
5454
}
5555
var keys []string
56-
if resource.Spec.V20250312.GroupRef != nil && resource.Spec.V20250312.GroupRef.Name != "" {
56+
if resource.Spec.V20250312 != nil && resource.Spec.V20250312.GroupRef != nil && resource.Spec.V20250312.GroupRef.Name != "" {
5757
keys = append(keys, types.NamespacedName{
5858
Name: resource.Spec.V20250312.GroupRef.Name,
5959
Namespace: resource.Namespace,
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright 2025 MongoDB Inc
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package indexer
16+
17+
import (
18+
"context"
19+
20+
zap "go.uber.org/zap"
21+
fields "k8s.io/apimachinery/pkg/fields"
22+
types "k8s.io/apimachinery/pkg/types"
23+
client "sigs.k8s.io/controller-runtime/pkg/client"
24+
handler "sigs.k8s.io/controller-runtime/pkg/handler"
25+
log "sigs.k8s.io/controller-runtime/pkg/log"
26+
reconcile "sigs.k8s.io/controller-runtime/pkg/reconcile"
27+
28+
v1 "github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/nextapi/generated/v1"
29+
)
30+
31+
// nolint:dupl
32+
const DatabaseUserBySecretIndex = "databaseuser.passwordSecretRef"
33+
34+
type DatabaseUserBySecretIndexer struct {
35+
logger *zap.SugaredLogger
36+
}
37+
38+
func NewDatabaseUserBySecretIndexer(logger *zap.Logger) *DatabaseUserBySecretIndexer {
39+
return &DatabaseUserBySecretIndexer{logger: logger.Named(DatabaseUserBySecretIndex).Sugar()}
40+
}
41+
func (*DatabaseUserBySecretIndexer) Object() client.Object {
42+
return &v1.DatabaseUser{}
43+
}
44+
func (*DatabaseUserBySecretIndexer) Name() string {
45+
return DatabaseUserBySecretIndex
46+
}
47+
48+
// Keys extracts the index key(s) from the given object
49+
func (i *DatabaseUserBySecretIndexer) Keys(object client.Object) []string {
50+
resource, ok := object.(*v1.DatabaseUser)
51+
if !ok {
52+
i.logger.Errorf("expected *v1.DatabaseUser but got %T", object)
53+
return nil
54+
}
55+
var keys []string
56+
if resource.Spec.V20250312 != nil && resource.Spec.V20250312.Entry != nil && resource.Spec.V20250312.Entry.PasswordSecretRef != nil && resource.Spec.V20250312.Entry.PasswordSecretRef.Name != "" {
57+
keys = append(keys, types.NamespacedName{
58+
Name: resource.Spec.V20250312.Entry.PasswordSecretRef.Name,
59+
Namespace: resource.Namespace,
60+
}.String())
61+
}
62+
return keys
63+
}
64+
65+
func NewDatabaseUserBySecretMapFunc(kubeClient client.Client) handler.MapFunc {
66+
return func(ctx context.Context, obj client.Object) []reconcile.Request {
67+
logger := log.FromContext(ctx)
68+
69+
listOpts := &client.ListOptions{FieldSelector: fields.OneTermEqualSelector(DatabaseUserBySecretIndex, types.NamespacedName{
70+
Name: obj.GetName(),
71+
Namespace: obj.GetNamespace(),
72+
}.String())}
73+
74+
list := &v1.DatabaseUserList{}
75+
err := kubeClient.List(ctx, list, listOpts)
76+
if err != nil {
77+
logger.Error(err, "failed to list DatabaseUser objects")
78+
return nil
79+
}
80+
81+
requests := make([]reconcile.Request, 0, len(list.Items))
82+
for _, item := range list.Items {
83+
requests = append(requests, reconcile.Request{NamespacedName: types.NamespacedName{
84+
Name: item.Name,
85+
Namespace: item.Namespace,
86+
}})
87+
}
88+
89+
return requests
90+
}
91+
}

internal/generated/indexers/flexclusterbygroup.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func (i *FlexClusterByGroupIndexer) Keys(object client.Object) []string {
5353
return nil
5454
}
5555
var keys []string
56-
if resource.Spec.V20250312.GroupRef != nil && resource.Spec.V20250312.GroupRef.Name != "" {
56+
if resource.Spec.V20250312 != nil && resource.Spec.V20250312.GroupRef != nil && resource.Spec.V20250312.GroupRef.Name != "" {
5757
keys = append(keys, types.NamespacedName{
5858
Name: resource.Spec.V20250312.GroupRef.Name,
5959
Namespace: resource.Namespace,

internal/nextapi/generated/v1/databaseuser.go

Lines changed: 17 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/nextapi/generated/v1/zz_generated.deepcopy.go

Lines changed: 24 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)