Skip to content

Commit 38a0eff

Browse files
irajdeepaexei
andauthored
feat: set namespace connectionStringSecret per user (mongodb#1463)
--------- Co-authored-by: Alexander Heinrich <[email protected]>
1 parent d4cc3d3 commit 38a0eff

8 files changed

+114
-30
lines changed

api/v1/mongodbcommunity_types.go

+20-5
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,10 @@ type MongoDBUser struct {
471471
// +optional
472472
ConnectionStringSecretName string `json:"connectionStringSecretName,omitempty"`
473473

474+
// ConnectionStringSecretNamespace is the namespace of the secret object created by the operator which exposes the connection strings for the user.
475+
// +optional
476+
ConnectionStringSecretNamespace string `json:"connectionStringSecretNamespace,omitempty"`
477+
474478
// Additional options to be appended to the connection string.
475479
// These options apply only to this user and will override any existing options in the resource.
476480
// +kubebuilder:validation:Type=object
@@ -503,6 +507,16 @@ func (m MongoDBUser) GetConnectionStringSecretName(resourceName string) string {
503507
return normalizeName(fmt.Sprintf("%s-%s-%s", resourceName, m.DB, m.Name))
504508
}
505509

510+
// GetConnectionStringSecretNamespace gets the connection string secret namespace provided by the user or generated
511+
// from the SCRAM user configuration.
512+
func (m MongoDBUser) GetConnectionStringSecretNamespace(resourceNamespace string) string {
513+
if m.ConnectionStringSecretNamespace != "" {
514+
return m.ConnectionStringSecretNamespace
515+
}
516+
517+
return resourceNamespace
518+
}
519+
506520
// normalizeName returns a string that conforms to RFC-1123
507521
func normalizeName(name string) string {
508522
errors := validation.IsDNS1123Subdomain(name)
@@ -761,11 +775,12 @@ func (m *MongoDBCommunity) GetAuthUsers() []authtypes.User {
761775
}
762776

763777
users[i] = authtypes.User{
764-
Username: u.Name,
765-
Database: u.DB,
766-
Roles: roles,
767-
ConnectionStringSecretName: u.GetConnectionStringSecretName(m.Name),
768-
ConnectionStringOptions: u.AdditionalConnectionStringConfig.Object,
778+
Username: u.Name,
779+
Database: u.DB,
780+
Roles: roles,
781+
ConnectionStringSecretName: u.GetConnectionStringSecretName(m.Name),
782+
ConnectionStringSecretNamespace: u.GetConnectionStringSecretNamespace(m.Namespace),
783+
ConnectionStringOptions: u.AdditionalConnectionStringConfig.Object,
769784
}
770785

771786
if u.DB != constants.ExternalDB {

api/v1/mongodbcommunity_types_test.go

+22-10
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,16 @@ func TestGetConnectionStringSecretName(t *testing.T) {
328328
},
329329
"connection-string-secret",
330330
},
331+
{
332+
MongoDBUser{
333+
Name: "mdb-2",
334+
DB: "admin",
335+
ScramCredentialsSecretName: "scram-credential-secret-name-2",
336+
ConnectionStringSecretName: "connection-string-secret-2",
337+
ConnectionStringSecretNamespace: "other-namespace",
338+
},
339+
"connection-string-secret-2",
340+
},
331341
}
332342

333343
for _, tt := range testusers {
@@ -521,11 +531,12 @@ func TestMongoDBCommunity_GetAuthUsers(t *testing.T) {
521531
Database: "admin",
522532
Name: "readWriteAnyDatabase",
523533
}},
524-
PasswordSecretKey: "password",
525-
PasswordSecretName: "my-user-password",
526-
ScramCredentialsSecretName: "my-scram-scram-credentials",
527-
ConnectionStringSecretName: "mdb-admin-my-user",
528-
ConnectionStringOptions: nil,
534+
PasswordSecretKey: "password",
535+
PasswordSecretName: "my-user-password",
536+
ScramCredentialsSecretName: "my-scram-scram-credentials",
537+
ConnectionStringSecretName: "mdb-admin-my-user",
538+
ConnectionStringSecretNamespace: mdb.Namespace,
539+
ConnectionStringOptions: nil,
529540
}, authUsers[0])
530541
assert.Equal(t, authtypes.User{
531542
Username: "CN=my-x509-authenticated-user,OU=organizationalunit,O=organization",
@@ -534,11 +545,12 @@ func TestMongoDBCommunity_GetAuthUsers(t *testing.T) {
534545
Database: "admin",
535546
Name: "readWriteAnyDatabase",
536547
}},
537-
PasswordSecretKey: "",
538-
PasswordSecretName: "",
539-
ScramCredentialsSecretName: "",
540-
ConnectionStringSecretName: "mdb-external-cn-my-x509-authenticated-user-ou-organizationalunit-o-organization",
541-
ConnectionStringOptions: nil,
548+
PasswordSecretKey: "",
549+
PasswordSecretName: "",
550+
ScramCredentialsSecretName: "",
551+
ConnectionStringSecretName: "mdb-external-cn-my-x509-authenticated-user-ou-organizationalunit-o-organization",
552+
ConnectionStringSecretNamespace: mdb.Namespace,
553+
ConnectionStringOptions: nil,
542554
}, authUsers[1])
543555
}
544556

config/crd/bases/mongodbcommunity.mongodb.com_mongodbcommunity.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
---
23
apiVersion: apiextensions.k8s.io/v1
34
kind: CustomResourceDefinition
@@ -486,6 +487,11 @@ spec:
486487
strings for the user. If provided, this secret must be different
487488
for each user in a deployment.
488489
type: string
490+
connectionStringSecretNamespace:
491+
description: ConnectionStringSecretNamespace is the namespace
492+
of the secret object created by the operator which exposes
493+
the connection strings for the user.
494+
type: string
489495
db:
490496
default: admin
491497
description: DB is the database the user is stored in. Defaults
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
apiVersion: mongodbcommunity.mongodb.com/v1
3+
kind: MongoDBCommunity
4+
metadata:
5+
name: example-mongodb
6+
spec:
7+
members: 3
8+
type: ReplicaSet
9+
version: "6.0.5"
10+
security:
11+
authentication:
12+
modes: ["SCRAM"]
13+
users:
14+
- name: my-user
15+
db: admin
16+
passwordSecretRef: # a reference to the secret that will be used to generate the user's password
17+
name: my-user-password
18+
roles:
19+
- name: clusterAdmin
20+
db: admin
21+
- name: userAdminAnyDatabase
22+
db: admin
23+
scramCredentialsSecretName: my-scram
24+
connectionStringSecretNamespace: other-namespace
25+
additionalMongodConfig:
26+
storage.wiredTiger.engineConfig.journalCompressor: zlib
27+
28+
# the user credentials will be generated from this secret
29+
# once the credentials are generated, this secret is no longer required
30+
---
31+
apiVersion: v1
32+
kind: Secret
33+
metadata:
34+
name: my-user-password
35+
type: Opaque
36+
stringData:
37+
password: <your-password-here>

controllers/mongodb_users.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,15 @@ func (r ReplicaSetReconciler) ensureUserResources(mdb mdbv1.MongoDBCommunity) er
4141
func (r ReplicaSetReconciler) updateConnectionStringSecrets(mdb mdbv1.MongoDBCommunity, clusterDomain string) error {
4242
for _, user := range mdb.GetAuthUsers() {
4343
secretName := user.ConnectionStringSecretName
44+
45+
secretNamespace := mdb.Namespace
46+
if user.ConnectionStringSecretNamespace != "" {
47+
secretNamespace = user.ConnectionStringSecretNamespace
48+
}
49+
4450
existingSecret, err := r.client.GetSecret(types.NamespacedName{
4551
Name: secretName,
46-
Namespace: mdb.Namespace,
52+
Namespace: secretNamespace,
4753
})
4854
if err != nil && !apiErrors.IsNotFound(err) {
4955
return err
@@ -55,7 +61,7 @@ func (r ReplicaSetReconciler) updateConnectionStringSecrets(mdb mdbv1.MongoDBCom
5561
pwd := ""
5662

5763
if user.Database != constants.ExternalDB {
58-
secretNamespacedName := types.NamespacedName{Name: user.PasswordSecretName, Namespace: mdb.Namespace}
64+
secretNamespacedName := types.NamespacedName{Name: user.PasswordSecretName, Namespace: secretNamespace}
5965
pwd, err = secret.ReadKey(r.client, user.PasswordSecretKey, secretNamespacedName)
6066
if err != nil {
6167
return err
@@ -64,7 +70,7 @@ func (r ReplicaSetReconciler) updateConnectionStringSecrets(mdb mdbv1.MongoDBCom
6470

6571
connectionStringSecret := secret.Builder().
6672
SetName(secretName).
67-
SetNamespace(mdb.Namespace).
73+
SetNamespace(secretNamespace).
6874
SetField("connectionString.standard", mdb.MongoAuthUserURI(user, pwd, clusterDomain)).
6975
SetField("connectionString.standardSrv", mdb.MongoAuthUserSRVURI(user, pwd, clusterDomain)).
7076
SetField("username", user.Username).

controllers/replicaset_controller_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7-
"github.com/mongodb/mongodb-kubernetes-operator/pkg/authentication/x509"
87
"os"
98
"reflect"
109
"testing"
1110
"time"
1211

12+
"github.com/mongodb/mongodb-kubernetes-operator/pkg/authentication/x509"
13+
1314
"github.com/mongodb/mongodb-kubernetes-operator/pkg/util/constants"
1415

1516
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/statefulset"
@@ -673,7 +674,7 @@ func assertConnectionStringSecretPorts(t *testing.T, c k8sClient.Client, mdb mdb
673674
connectionStringSecret := corev1.Secret{}
674675
scramUsers := mdb.GetAuthUsers()
675676
require.Len(t, scramUsers, 1)
676-
secretNamespacedName := types.NamespacedName{Name: scramUsers[0].ConnectionStringSecretName, Namespace: mdb.Namespace}
677+
secretNamespacedName := types.NamespacedName{Name: scramUsers[0].ConnectionStringSecretName, Namespace: scramUsers[0].ConnectionStringSecretNamespace}
677678
err := c.Get(context.TODO(), secretNamespacedName, &connectionStringSecret)
678679
require.NoError(t, err)
679680
require.Contains(t, connectionStringSecret.Data, "connectionString.standard")

docs/install-upgrade.md

+14-10
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ Use one of the following procedures to install the Operator using Helm:
7676

7777
##### Install in the Default Namespace using Helm
7878

79-
To install the Custom Resource Definitions and the Community Operator in
80-
the `default` namespace using Helm, run the install command from the
79+
To install the Custom Resource Definitions and the Community Operator in
80+
the `default` namespace using Helm, run the install command from the
8181
terminal:
8282
```
8383
helm install community-operator mongodb/community-operator
@@ -91,9 +91,9 @@ include `--set community-operator-crds.enabled=false` when installing the Operat
9191

9292
##### Install in a Different Namespace using Helm
9393

94-
To install the Custom Resource Definitions and the Community Operator in
95-
a different namespace using Helm, run the install
96-
command with the `--namespace` flag from the terminal. Include the `--create-namespace`
94+
To install the Custom Resource Definitions and the Community Operator in
95+
a different namespace using Helm, run the install
96+
command with the `--namespace` flag from the terminal. Include the `--create-namespace`
9797
flag if you are creating a new namespace.
9898
```
9999
helm install community-operator mongodb/community-operator --namespace mongodb [--create-namespace]
@@ -159,6 +159,10 @@ To configure the Operator to watch resources in other namespaces:
159159
kubectl apply -k config/rbac --namespace <my-namespace>
160160
```
161161

162+
*Note: If you need the operator to have permission over multiple namespaces, for ex: when configuring the operator to have the `connectionStringSecret` in a different `namespace`, make sure
163+
to apply the `RBAC` in all the relevant namespaces.*
164+
165+
162166
5. [Install the operator](#procedure-using-kubectl).
163167

164168
##### Configure the MongoDB Docker Image or Container Registry
@@ -170,11 +174,11 @@ for MongoDB Docker images:
170174

171175
1. In the Operator [resource definition](../config/manager/manager.yaml), set the `MONGODB_IMAGE` and `MONGODB_REPO_URL` environment variables:
172176

173-
**NOTE:** Use the official
174-
[MongoDB Community Server images](https://hub.docker.com/r/mongodb/mongodb-community-server).
177+
**NOTE:** Use the official
178+
[MongoDB Community Server images](https://hub.docker.com/r/mongodb/mongodb-community-server).
175179
Official images provide the following advantages:
176180

177-
- They are rebuilt daily for the latest upstream
181+
- They are rebuilt daily for the latest upstream
178182
vulnerability fixes.
179183
- MongoDB tests, maintains, and supports them.
180184

@@ -290,7 +294,7 @@ Make sure you run commands in the correct namespace.
290294
```
291295
kubectl delete pod <sts-name>-0
292296
```
293-
d. You're done. Now Kubernetes will create the pod fresh, causing the migration to run and then the pod to start up. Then kubernetes will proceed creating the next pod until it reaches the number specified in your cr.
297+
d. You're done. Now Kubernetes will create the pod fresh, causing the migration to run and then the pod to start up. Then kubernetes will proceed creating the next pod until it reaches the number specified in your cr.
294298

295299
## Rotating TLS certificate for the MongoDB deployment
296300

@@ -306,4 +310,4 @@ kubectl apply -f -
306310
*`secret_name` is what you've specified under `Spec.Security.TLS.CertificateKeySecret.Name`*.
307311
308312
If you're using a tool like cert-manager, you can follow [these instructions](https://cert-manager.io/docs/usage/certificate/#renewal) to rotate the certificate.
309-
The operator should would watch the secret change and re-trigger a reconcile process.
313+
The operator should would watch the secret change and re-trigger a reconcile process.

pkg/authentication/authtypes/authtypes.go

+3
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ type User struct {
7070
// Note: there will be one secret with connection strings per user created.
7171
ConnectionStringSecretName string
7272

73+
// ConnectionStringSecretNamespace is the namespace of the secret object created by the operator which exposes the connection strings for the user.
74+
ConnectionStringSecretNamespace string `json:"connectionStringSecretNamespace,omitempty"`
75+
7376
// ConnectionStringOptions contains connection string options for this user
7477
// These options will be appended at the end of the connection string and
7578
// will override any existing options from the resources.

0 commit comments

Comments
 (0)