diff --git a/api/v1/mongodbcommunity_types.go b/api/v1/mongodbcommunity_types.go index 0153e22e6..dc32ad380 100644 --- a/api/v1/mongodbcommunity_types.go +++ b/api/v1/mongodbcommunity_types.go @@ -471,6 +471,10 @@ type MongoDBUser struct { // +optional ConnectionStringSecretName string `json:"connectionStringSecretName,omitempty"` + // ConnectionStringSecretNamespace is the namespace of the secret object created by the operator which exposes the connection strings for the user. + // +optional + ConnectionStringSecretNamespace string `json:"connectionStringSecretNamespace,omitempty"` + // Additional options to be appended to the connection string. // These options apply only to this user and will override any existing options in the resource. // +kubebuilder:validation:Type=object @@ -503,6 +507,16 @@ func (m MongoDBUser) GetConnectionStringSecretName(resourceName string) string { return normalizeName(fmt.Sprintf("%s-%s-%s", resourceName, m.DB, m.Name)) } +// GetConnectionStringSecretNamespace gets the connection string secret namespace provided by the user or generated +// from the SCRAM user configuration. +func (m MongoDBUser) GetConnectionStringSecretNamespace(resourceNamespace string) string { + if m.ConnectionStringSecretNamespace != "" { + return m.ConnectionStringSecretNamespace + } + + return resourceNamespace +} + // normalizeName returns a string that conforms to RFC-1123 func normalizeName(name string) string { errors := validation.IsDNS1123Subdomain(name) @@ -765,6 +779,7 @@ func (m *MongoDBCommunity) GetAuthUsers() []authtypes.User { Database: u.DB, Roles: roles, ConnectionStringSecretName: u.GetConnectionStringSecretName(m.Name), + ConnectionStringSecretNamespace: u.GetConnectionStringSecretName(m.Namespace), ConnectionStringOptions: u.AdditionalConnectionStringConfig.Object, } diff --git a/api/v1/mongodbcommunity_types_test.go b/api/v1/mongodbcommunity_types_test.go index efdd042c3..997266e00 100644 --- a/api/v1/mongodbcommunity_types_test.go +++ b/api/v1/mongodbcommunity_types_test.go @@ -282,6 +282,35 @@ func TestGetScramCredentialsSecretName(t *testing.T) { }, "scram-credential-secret-name-1-scram-credentials", }, + { + MongoDBUser{ + Name: "mdb-2", + DB: "admin", + Roles: []Role{ + // roles on testing db for general connectivity + { + DB: "testing", + Name: "readWrite", + }, + { + DB: "testing", + Name: "clusterAdmin", + }, + // admin roles for reading FCV + { + DB: "admin", + Name: "readWrite", + }, + { + DB: "admin", + Name: "clusterAdmin", + }, + }, + ScramCredentialsSecretName: "scram-credential-secret-name-2", + ConnectionStringSecretNamespace: "other-namespace" + }, + "scram-credential-secret-name-2-scram-credentials", + }, } for _, tt := range testusers { @@ -328,6 +357,16 @@ func TestGetConnectionStringSecretName(t *testing.T) { }, "connection-string-secret", }, + { + MongoDBUser{ + Name: "mdb-2", + DB: "admin", + ScramCredentialsSecretName: "scram-credential-secret-name-2", + ConnectionStringSecretName: "connection-string-secret-2", + ConnectionStringSecretNamespace: "other-namespace", + }, + "connection-string-secret", + }, } for _, tt := range testusers { diff --git a/config/crd/bases/mongodbcommunity.mongodb.com_mongodbcommunity.yaml b/config/crd/bases/mongodbcommunity.mongodb.com_mongodbcommunity.yaml index f903a1b53..fbff9edcb 100644 --- a/config/crd/bases/mongodbcommunity.mongodb.com_mongodbcommunity.yaml +++ b/config/crd/bases/mongodbcommunity.mongodb.com_mongodbcommunity.yaml @@ -486,6 +486,11 @@ spec: strings for the user. If provided, this secret must be different for each user in a deployment. type: string + connectionStringSecretNamespace: + description: ConnectionStringSecretNamespace is the namespace of the secret + object created by the operator which exposes the connection + strings for the user. + type: string db: default: admin description: DB is the database the user is stored in. Defaults diff --git a/config/samples/mongodb.com_v1_mongodbcommunity_connection_string_secret_namespace.yaml b/config/samples/mongodb.com_v1_mongodbcommunity_connection_string_secret_namespace.yaml new file mode 100644 index 000000000..47e55aaae --- /dev/null +++ b/config/samples/mongodb.com_v1_mongodbcommunity_connection_string_secret_namespace.yaml @@ -0,0 +1,37 @@ +--- +apiVersion: mongodbcommunity.mongodb.com/v1 +kind: MongoDBCommunity +metadata: + name: example-mongodb +spec: + members: 3 + type: ReplicaSet + version: "6.0.5" + security: + authentication: + modes: ["SCRAM"] + users: + - name: my-user + db: admin + passwordSecretRef: # a reference to the secret that will be used to generate the user's password + name: my-user-password + roles: + - name: clusterAdmin + db: admin + - name: userAdminAnyDatabase + db: admin + scramCredentialsSecretName: my-scram + connectionStringSecretNamespace: other-namespace + additionalMongodConfig: + storage.wiredTiger.engineConfig.journalCompressor: zlib + +# the user credentials will be generated from this secret +# once the credentials are generated, this secret is no longer required +--- +apiVersion: v1 +kind: Secret +metadata: + name: my-user-password +type: Opaque +stringData: + password: diff --git a/controllers/mongodb_users.go b/controllers/mongodb_users.go index 058ae43d8..15c4c3d39 100644 --- a/controllers/mongodb_users.go +++ b/controllers/mongodb_users.go @@ -41,9 +41,10 @@ func (r ReplicaSetReconciler) ensureUserResources(mdb mdbv1.MongoDBCommunity) er func (r ReplicaSetReconciler) updateConnectionStringSecrets(mdb mdbv1.MongoDBCommunity, clusterDomain string) error { for _, user := range mdb.GetAuthUsers() { secretName := user.ConnectionStringSecretName + secretNamespace := user.connectionStringSecretNamespace existingSecret, err := r.client.GetSecret(types.NamespacedName{ Name: secretName, - Namespace: mdb.Namespace, + Namespace: secretNamespace, }) if err != nil && !apiErrors.IsNotFound(err) { return err @@ -55,7 +56,7 @@ func (r ReplicaSetReconciler) updateConnectionStringSecrets(mdb mdbv1.MongoDBCom pwd := "" if user.Database != constants.ExternalDB { - secretNamespacedName := types.NamespacedName{Name: user.PasswordSecretName, Namespace: mdb.Namespace} + secretNamespacedName := types.NamespacedName{Name: user.PasswordSecretName, Namespace: secretNamespace} pwd, err = secret.ReadKey(r.client, user.PasswordSecretKey, secretNamespacedName) if err != nil { return err @@ -64,7 +65,7 @@ func (r ReplicaSetReconciler) updateConnectionStringSecrets(mdb mdbv1.MongoDBCom connectionStringSecret := secret.Builder(). SetName(secretName). - SetNamespace(mdb.Namespace). + SetNamespace(secretNamespace). SetField("connectionString.standard", mdb.MongoAuthUserURI(user, pwd, clusterDomain)). SetField("connectionString.standardSrv", mdb.MongoAuthUserSRVURI(user, pwd, clusterDomain)). SetField("username", user.Username).