diff --git a/.evergreen-tasks.yml b/.evergreen-tasks.yml index 5059e8a8a..9bd869a1e 100644 --- a/.evergreen-tasks.yml +++ b/.evergreen-tasks.yml @@ -584,6 +584,11 @@ tasks: tags: [ "patch-run" ] commands: - func: "e2e_test" + + - name: e2e_sharded_cluster_scram_sha_256_switch_project + tags: [ "patch-run" ] + commands: + - func: "e2e_test" - name: e2e_sharded_cluster_scram_sha_1_user_connectivity tags: [ "patch-run" ] diff --git a/.evergreen.yml b/.evergreen.yml index ee2d544b0..6813dcdc5 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -667,6 +667,7 @@ task_groups: - e2e_replica_set_scram_x509_ic_manual_certs - e2e_sharded_cluster_scram_sha_1_upgrade - e2e_sharded_cluster_scram_sha_256_user_connectivity + - e2e_sharded_cluster_scram_sha_256_switch_project - e2e_sharded_cluster_scram_sha_1_user_connectivity - e2e_sharded_cluster_scram_x509_ic_manual_certs - e2e_sharded_cluster_external_access diff --git a/api/v1/search/zz_generated.deepcopy.go b/api/v1/search/zz_generated.deepcopy.go index c66322146..d18d025f8 100644 --- a/api/v1/search/zz_generated.deepcopy.go +++ b/api/v1/search/zz_generated.deepcopy.go @@ -159,7 +159,7 @@ func (in *MongoDBSearchSpec) DeepCopyInto(out *MongoDBSearchSpec) { *out = new(v1.ResourceRequirements) (*in).DeepCopyInto(*out) } - out.Security = in.Security + in.Security.DeepCopyInto(&out.Security) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MongoDBSearchSpec. @@ -231,7 +231,11 @@ func (in *MongoDBSource) DeepCopy() *MongoDBSource { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Security) DeepCopyInto(out *Security) { *out = *in - out.TLS = in.TLS + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLS) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Security. diff --git a/controllers/om/automation_config.go b/controllers/om/automation_config.go index bfb61c369..229c7af54 100644 --- a/controllers/om/automation_config.go +++ b/controllers/om/automation_config.go @@ -1,19 +1,28 @@ package om import ( + "context" "encoding/json" + "fmt" "github.com/google/go-cmp/cmp" "github.com/spf13/cast" "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" "github.com/mongodb/mongodb-kubernetes/controllers/operator/ldap" "github.com/mongodb/mongodb-kubernetes/controllers/operator/oidc" + "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/secret" "github.com/mongodb/mongodb-kubernetes/pkg/util" "github.com/mongodb/mongodb-kubernetes/pkg/util/generate" "github.com/mongodb/mongodb-kubernetes/pkg/util/maputil" ) +// The constants for the authentication secret +const agentAuthenticationSecretSuffix = "-agent-auth-secret" +const autoPwdSecretKey = "automation-agent-password" + // AutomationConfig maintains the raw map in the Deployment field // and constructs structs to make use of go's type safety // Dev notes: actually, this object is just a wrapper for the `Deployment` object which is received from Ops Manager, @@ -415,7 +424,7 @@ type MongoDbVersionConfig struct { } // EnsureKeyFileContents makes sure a valid keyfile is generated and used for internal cluster authentication -func (ac *AutomationConfig) EnsureKeyFileContents() error { +func (ac *AutomationConfig) EnsureKeyFileContents(k8sClient secret.GetUpdateCreator, ctx context.Context, mdbNamespacedName *types.NamespacedName) error { if ac.Auth.Key == "" || ac.Auth.Key == util.InvalidKeyFileContents { keyfileContents, err := generate.KeyFileContents() if err != nil { @@ -429,16 +438,63 @@ func (ac *AutomationConfig) EnsureKeyFileContents() error { // EnsurePassword makes sure that there is an Automation Agent password // that the agents will use to communicate with the deployments. The password // is returned, so it can be provided to the other agents -func (ac *AutomationConfig) EnsurePassword() (string, error) { - if ac.Auth.AutoPwd == "" || ac.Auth.AutoPwd == util.InvalidAutomationAgentPassword { - automationAgentBackupPassword, err := generate.KeyFileContents() +// EnsurePassword makes sure that there is an Automation Agent password +// that the agents will use to communicate with the deployments. The password +// is returned, so it can be provided to the other agents. +func (ac *AutomationConfig) EnsurePassword(k8sClient secret.GetUpdateCreator, ctx context.Context, mdbNamespacedName *types.NamespacedName) (string, error) { + secretNamespacedName := client.ObjectKey{Name: mdbNamespacedName.Name + agentAuthenticationSecretSuffix, Namespace: mdbNamespacedName.Namespace} + + var password string + + data, err := secret.ReadStringData(ctx, k8sClient, secretNamespacedName) + if err == nil { + if val, ok := data[autoPwdSecretKey]; ok && len(val) > 0 { + password = val + } + } else if secret.SecretNotExist(err) { + if ac.Auth.AutoPwd != "" && ac.Auth.AutoPwd != util.InvalidAutomationAgentPassword { + password = ac.Auth.AutoPwd + } + + err := EnsureEmptySecret(ctx, k8sClient, secretNamespacedName) if err != nil { return "", err } - ac.Auth.AutoPwd = automationAgentBackupPassword - return automationAgentBackupPassword, nil } - return ac.Auth.AutoPwd, nil + + if password == "" { + generatedPassword, genErr := generate.KeyFileContents() + if genErr != nil { + return "", genErr + } + password = generatedPassword + } + + ac.Auth.AutoPwd = password + err = secret.UpdateField(ctx, k8sClient, secretNamespacedName, autoPwdSecretKey, password) + if err != nil { + return "", fmt.Errorf("failed to update password field in shared secret %s/%s: %w", secretNamespacedName.Namespace, secretNamespacedName.Name, err) + } + + return password, nil +} + +func EnsureEmptySecret(ctx context.Context, k8sClient secret.GetUpdateCreator, secretNamespacedName types.NamespacedName) error { + dataFields := map[string]string{ + autoPwdSecretKey: "", + } + + emptySecret := secret.Builder(). + SetName(secretNamespacedName.Name). + SetNamespace(secretNamespacedName.Namespace). + SetStringMapToData(dataFields). + Build() + + if err := secret.CreateOrUpdateIfNeeded(ctx, k8sClient, emptySecret); err != nil { + return fmt.Errorf("failed to create or update empty secret %s/%s: %w", secretNamespacedName.Namespace, secretNamespacedName.Name, err) + } + + return nil } func (ac *AutomationConfig) CanEnableX509ProjectAuthentication() (bool, string) { diff --git a/controllers/om/deployment.go b/controllers/om/deployment.go index fd7f7f776..054229e5b 100644 --- a/controllers/om/deployment.go +++ b/controllers/om/deployment.go @@ -4,6 +4,7 @@ import ( "encoding/gob" "encoding/json" "fmt" + "hash/fnv" "math" "regexp" @@ -468,6 +469,27 @@ func (d Deployment) GetProcessNames(kind interface{}, name string) []string { } } +// GenerateFNVSecretIdentifier creates a short, Kubernetes-compliant identifier with "secret-" prefix for the Deployment. +func (d Deployment) GenerateFNVSecretIdentifier() (string, error) { + // Serialize the Deployment structure to JSON + serializedDeployment, err := json.Marshal(d.ToCanonicalForm()) + if err != nil { + return "", fmt.Errorf("failed to serialize Deployment: %w", err) + } + + // Use FNV-1a hash algorithm + hash := fnv.New32a() // Initialize FNV-1a for 32-bit hash + _, err = hash.Write(serializedDeployment) + if err != nil { + return "", fmt.Errorf("failed to compute hash: %w", err) + } + + // Convert the hash into a hex string + identifier := fmt.Sprintf("secret-%x", hash.Sum32()) // Prepend prefix "secret-" to the hash + + return identifier, nil +} + // ConfigureInternalClusterAuthentication configures all processes in processNames to have the corresponding // clusterAuthenticationMode enabled func (d Deployment) ConfigureInternalClusterAuthentication(processNames []string, clusterAuthenticationMode string, internalClusterPath string) { diff --git a/controllers/operator/appdbreplicaset_controller.go b/controllers/operator/appdbreplicaset_controller.go index fe68e07c6..d2fe09d0d 100644 --- a/controllers/operator/appdbreplicaset_controller.go +++ b/controllers/operator/appdbreplicaset_controller.go @@ -1667,7 +1667,7 @@ func (r *ReconcileAppDbReplicaSet) tryConfigureMonitoringInOpsManager(ctx contex AutoPEMKeyFilePath: agentCertPath, CAFilePath: util.CAFilePathInContainer, } - err = authentication.Configure(conn, opts, false, log) + err = authentication.Configure(r.client, ctx, &types.NamespacedName{Namespace: opsManager.Namespace, Name: opsManager.Name}, conn, opts, false, log) if err != nil { log.Errorf("Could not set Automation Authentication options in Ops/Cloud Manager for the Application Database. "+ "Application Database is always configured with authentication enabled, but this will not be "+ diff --git a/controllers/operator/authentication/authentication.go b/controllers/operator/authentication/authentication.go index c2e36735b..30c56edc4 100644 --- a/controllers/operator/authentication/authentication.go +++ b/controllers/operator/authentication/authentication.go @@ -1,13 +1,17 @@ package authentication import ( + "context" + "go.uber.org/zap" "golang.org/x/xerrors" + "k8s.io/apimachinery/pkg/types" mdbv1 "github.com/mongodb/mongodb-kubernetes/api/v1/mdb" "github.com/mongodb/mongodb-kubernetes/controllers/om" "github.com/mongodb/mongodb-kubernetes/controllers/operator/ldap" "github.com/mongodb/mongodb-kubernetes/controllers/operator/oidc" + kubernetesClient "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/client" "github.com/mongodb/mongodb-kubernetes/pkg/util" ) @@ -82,7 +86,7 @@ type UserOptions struct { // Configure will configure all the specified authentication Mechanisms. We need to ensure we wait for // the agents to reach ready state after each operation as prematurely updating the automation config can cause the agents to get stuck. -func Configure(conn om.Connection, opts Options, isRecovering bool, log *zap.SugaredLogger) error { +func Configure(client kubernetesClient.Client, ctx context.Context, namespacedName *types.NamespacedName, conn om.Connection, opts Options, isRecovering bool, log *zap.SugaredLogger) error { log.Infow("ensuring correct deployment mechanisms", "ProcessNames", opts.ProcessNames, "Mechanisms", opts.Mechanisms) // In case we're recovering, we can push all changes at once, because the mechanism is triggered after 20min by default. @@ -113,7 +117,7 @@ func Configure(conn om.Connection, opts Options, isRecovering bool, log *zap.Sug // once we have made sure that the deployment authentication mechanism array contains the desired auth mechanism // we can then configure the agent authentication. - if err := enableAgentAuthentication(conn, opts, log); err != nil { + if err := enableAgentAuthentication(client, ctx, namespacedName, conn, opts, log); err != nil { return xerrors.Errorf("error enabling agent authentication: %w", err) } if err := waitForReadyStateIfNeeded(); err != nil { @@ -151,7 +155,7 @@ func Configure(conn om.Connection, opts Options, isRecovering bool, log *zap.Sug // Disable disables all authentication mechanisms, and waits for the agents to reach goal state. It is still required to provide // automation agent username, password and keyfile contents to ensure a valid Automation Config. -func Disable(conn om.Connection, opts Options, deleteUsers bool, log *zap.SugaredLogger) error { +func Disable(client kubernetesClient.Client, ctx context.Context, namespacedName *types.NamespacedName, conn om.Connection, opts Options, deleteUsers bool, log *zap.SugaredLogger) error { ac, err := conn.ReadAutomationConfig() if err != nil { return xerrors.Errorf("error reading automation config: %w", err) @@ -178,10 +182,10 @@ func Disable(conn om.Connection, opts Options, deleteUsers bool, log *zap.Sugare } err = conn.ReadUpdateAutomationConfig(func(ac *om.AutomationConfig) error { - if err := ac.EnsureKeyFileContents(); err != nil { + if err := ac.EnsureKeyFileContents(client, ctx, namespacedName); err != nil { return xerrors.Errorf("error ensuring keyfile contents: %w", err) } - if _, err := ac.EnsurePassword(); err != nil { + if _, err := ac.EnsurePassword(client, ctx, namespacedName); err != nil { return xerrors.Errorf("error ensuring agent password: %w", err) } @@ -258,7 +262,7 @@ func removeUnsupportedAgentMechanisms(conn om.Connection, opts Options, log *zap // enableAgentAuthentication determines which agent authentication mechanism should be configured // and enables it in Ops Manager -func enableAgentAuthentication(conn om.Connection, opts Options, log *zap.SugaredLogger) error { +func enableAgentAuthentication(client kubernetesClient.Client, ctx context.Context, namespacedName *types.NamespacedName, conn om.Connection, opts Options, log *zap.SugaredLogger) error { ac, err := conn.ReadAutomationConfig() if err != nil { return xerrors.Errorf("error reading automation config: %w", err) @@ -267,7 +271,7 @@ func enableAgentAuthentication(conn om.Connection, opts Options, log *zap.Sugare // we then configure the agent authentication for that type mechanism := convertToMechanismOrPanic(opts.AgentMechanism, ac) - if err := ensureAgentAuthenticationIsConfigured(conn, opts, ac, mechanism, log); err != nil { + if err := ensureAgentAuthenticationIsConfigured(client, ctx, namespacedName, conn, opts, ac, mechanism, log); err != nil { return xerrors.Errorf("error ensuring agent authentication is configured: %w", err) } @@ -365,14 +369,14 @@ func addOrRemoveAgentClientCertificate(conn om.Connection, opts Options, log *za } // ensureAgentAuthenticationIsConfigured will configure the agent authentication settings based on the desiredAgentAuthMechanism -func ensureAgentAuthenticationIsConfigured(conn om.Connection, opts Options, ac *om.AutomationConfig, mechanism Mechanism, log *zap.SugaredLogger) error { +func ensureAgentAuthenticationIsConfigured(client kubernetesClient.Client, ctx context.Context, namespacedName *types.NamespacedName, conn om.Connection, opts Options, ac *om.AutomationConfig, mechanism Mechanism, log *zap.SugaredLogger) error { if mechanism.IsAgentAuthenticationConfigured(ac, opts) { log.Infof("Agent authentication mechanism %s is already configured", mechanism.GetName()) return nil } log.Infof("Enabling %s agent authentication", mechanism.GetName()) - return mechanism.EnableAgentAuthentication(conn, opts, log) + return mechanism.EnableAgentAuthentication(client, ctx, namespacedName, conn, opts, log) } // ensureDeploymentMechanisms configures the given AutomationConfig to allow deployments to diff --git a/controllers/operator/authentication/authentication_mechanism.go b/controllers/operator/authentication/authentication_mechanism.go index 90e6877e8..4ebe34d75 100644 --- a/controllers/operator/authentication/authentication_mechanism.go +++ b/controllers/operator/authentication/authentication_mechanism.go @@ -1,19 +1,22 @@ package authentication import ( + "context" "slices" "strings" "go.uber.org/zap" "golang.org/x/xerrors" + "k8s.io/apimachinery/pkg/types" "github.com/mongodb/mongodb-kubernetes/controllers/om" + kubernetesClient "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/client" "github.com/mongodb/mongodb-kubernetes/pkg/util" ) // Mechanism is an interface that needs to be implemented for any Ops Manager authentication mechanism type Mechanism interface { - EnableAgentAuthentication(conn om.Connection, opts Options, log *zap.SugaredLogger) error + EnableAgentAuthentication(client kubernetesClient.Client, ctx context.Context, namespacedName *types.NamespacedName, conn om.Connection, opts Options, log *zap.SugaredLogger) error DisableAgentAuthentication(conn om.Connection, log *zap.SugaredLogger) error EnableDeploymentAuthentication(conn om.Connection, opts Options, log *zap.SugaredLogger) error DisableDeploymentAuthentication(conn om.Connection, log *zap.SugaredLogger) error diff --git a/controllers/operator/authentication/configure_authentication_test.go b/controllers/operator/authentication/configure_authentication_test.go deleted file mode 100644 index 8e8ae95df..000000000 --- a/controllers/operator/authentication/configure_authentication_test.go +++ /dev/null @@ -1,246 +0,0 @@ -package authentication - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - - "github.com/mongodb/mongodb-kubernetes/controllers/om" - "github.com/mongodb/mongodb-kubernetes/pkg/util" -) - -func init() { - logger, _ := zap.NewDevelopment() - zap.ReplaceGlobals(logger) -} - -func TestConfigureScramSha256(t *testing.T) { - dep := om.NewDeployment() - conn := om.NewMockedOmConnection(dep) - - opts := Options{ - AuthoritativeSet: true, - ProcessNames: []string{"process-1", "process-2", "process-3"}, - Mechanisms: []string{"SCRAM"}, - AgentMechanism: "SCRAM", - } - - if err := Configure(conn, opts, false, zap.S()); err != nil { - t.Fatal(err) - } - - ac, err := conn.ReadAutomationConfig() - if err != nil { - t.Fatal(err) - } - - assertAuthenticationEnabled(t, ac.Auth) - assertAuthenticationMechanism(t, ac.Auth, "SCRAM-SHA-256") -} - -func TestConfigureX509(t *testing.T) { - dep := om.NewDeployment() - conn := om.NewMockedOmConnection(dep) - - opts := Options{ - AuthoritativeSet: true, - ProcessNames: []string{"process-1", "process-2", "process-3"}, - Mechanisms: []string{"X509"}, - AgentMechanism: "X509", - ClientCertificates: util.RequireClientCertificates, - UserOptions: UserOptions{ - AutomationSubject: validSubject("automation"), - }, - } - - if err := Configure(conn, opts, false, zap.S()); err != nil { - t.Fatal(err) - } - - ac, err := conn.ReadAutomationConfig() - if err != nil { - t.Fatal(err) - } - - assertAuthenticationEnabled(t, ac.Auth) - assertAuthenticationMechanism(t, ac.Auth, "MONGODB-X509") -} - -func TestConfigureScramSha1(t *testing.T) { - dep := om.NewDeployment() - conn := om.NewMockedOmConnection(dep) - - opts := Options{ - AuthoritativeSet: true, - ProcessNames: []string{"process-1", "process-2", "process-3"}, - Mechanisms: []string{"SCRAM-SHA-1"}, - AgentMechanism: "SCRAM-SHA-1", - } - - if err := Configure(conn, opts, false, zap.S()); err != nil { - t.Fatal(err) - } - - ac, err := conn.ReadAutomationConfig() - assert.NoError(t, err) - - assertAuthenticationEnabled(t, ac.Auth) - assertAuthenticationMechanism(t, ac.Auth, "SCRAM-SHA-1") -} - -func TestConfigureMultipleAuthenticationMechanisms(t *testing.T) { - dep := om.NewDeployment() - conn := om.NewMockedOmConnection(dep) - - opts := Options{ - AuthoritativeSet: true, - ProcessNames: []string{"process-1", "process-2", "process-3"}, - Mechanisms: []string{"X509", "SCRAM"}, - AgentMechanism: "SCRAM", - UserOptions: UserOptions{ - AutomationSubject: validSubject("automation"), - }, - } - - if err := Configure(conn, opts, false, zap.S()); err != nil { - t.Fatal(err) - } - - ac, err := conn.ReadAutomationConfig() - if err != nil { - t.Fatal(err) - } - - assertAuthenticationEnabled(t, ac.Auth) - - assert.Contains(t, ac.Auth.AutoAuthMechanisms, "SCRAM-SHA-256") - - assert.Len(t, ac.Auth.DeploymentAuthMechanisms, 2) - assert.Len(t, ac.Auth.AutoAuthMechanisms, 1) - assert.Contains(t, ac.Auth.DeploymentAuthMechanisms, "SCRAM-SHA-256") - assert.Contains(t, ac.Auth.DeploymentAuthMechanisms, "MONGODB-X509") -} - -func TestDisableAuthentication(t *testing.T) { - dep := om.NewDeployment() - conn := om.NewMockedOmConnection(dep) - - // enable authentication - _ = conn.ReadUpdateAutomationConfig(func(ac *om.AutomationConfig) error { - ac.Auth.Enable() - return nil - }, zap.S()) - - if err := Disable(conn, Options{}, true, zap.S()); err != nil { - t.Fatal(err) - } - - ac, err := conn.ReadAutomationConfig() - if err != nil { - t.Fatal(err) - } - - assertAuthenticationDisabled(t, ac.Auth) -} - -func TestGetCorrectAuthMechanismFromVersion(t *testing.T) { - conn := om.NewMockedOmConnection(om.NewDeployment()) - ac, err := conn.ReadAutomationConfig() - require.NoError(t, err) - - mechanismList := convertToMechanismList([]string{"X509"}, ac) - - assert.Len(t, mechanismList, 1) - assert.Contains(t, mechanismList, mongoDBX509Mechanism) - - mechanismList = convertToMechanismList([]string{"SCRAM", "X509"}, ac) - - assert.Contains(t, mechanismList, scramSha256Mechanism) - assert.Contains(t, mechanismList, mongoDBX509Mechanism) - - // enable MONGODB-CR - ac.Auth.AutoAuthMechanism = "MONGODB-CR" - ac.Auth.Enable() - - mechanismList = convertToMechanismList([]string{"SCRAM", "X509"}, ac) - - assert.Contains(t, mechanismList, mongoDBCRMechanism) - assert.Contains(t, mechanismList, mongoDBX509Mechanism) -} - -func assertAuthenticationEnabled(t *testing.T, auth *om.Auth) { - assertAuthenticationEnabledWithUsers(t, auth, 0) -} - -func assertAuthenticationEnabledWithUsers(t *testing.T, auth *om.Auth, numUsers int) { - assert.True(t, auth.AuthoritativeSet) - assert.False(t, auth.Disabled) - assert.NotEmpty(t, auth.Key) - assert.NotEmpty(t, auth.KeyFileWindows) - assert.NotEmpty(t, auth.KeyFile) - assert.Len(t, auth.Users, numUsers) - assert.True(t, noneNil(auth.Users)) -} - -func assertAuthenticationDisabled(t *testing.T, auth *om.Auth) { - assert.True(t, auth.Disabled) - assert.Empty(t, auth.DeploymentAuthMechanisms) - assert.Empty(t, auth.AutoAuthMechanisms) - assert.Equal(t, auth.AutoUser, util.AutomationAgentName) - assert.NotEmpty(t, auth.Key) - assert.NotEmpty(t, auth.AutoPwd) - assert.True(t, len(auth.Users) == 0 || allNil(auth.Users)) -} - -func assertAuthenticationMechanism(t *testing.T, auth *om.Auth, mechanism string) { - assert.Len(t, auth.DeploymentAuthMechanisms, 1) - assert.Len(t, auth.AutoAuthMechanisms, 1) - assert.Len(t, auth.Users, 0) - assert.Contains(t, auth.DeploymentAuthMechanisms, mechanism) - assert.Contains(t, auth.AutoAuthMechanisms, mechanism) -} - -func assertDeploymentMechanismsConfigured(t *testing.T, authMechanism Mechanism, conn om.Connection, opts Options) { - err := authMechanism.EnableDeploymentAuthentication(conn, opts, zap.S()) - require.NoError(t, err) - - ac, err := conn.ReadAutomationConfig() - require.NoError(t, err) - assert.True(t, authMechanism.IsDeploymentAuthenticationConfigured(ac, opts)) -} - -func assertAgentAuthenticationDisabled(t *testing.T, authMechanism Mechanism, conn om.Connection, opts Options) { - err := authMechanism.EnableAgentAuthentication(conn, opts, zap.S()) - require.NoError(t, err) - - ac, err := conn.ReadAutomationConfig() - require.NoError(t, err) - assert.True(t, authMechanism.IsAgentAuthenticationConfigured(ac, opts)) - - err = authMechanism.DisableAgentAuthentication(conn, zap.S()) - require.NoError(t, err) - - ac, err = conn.ReadAutomationConfig() - require.NoError(t, err) - assert.False(t, authMechanism.IsAgentAuthenticationConfigured(ac, opts)) -} - -func noneNil(users []*om.MongoDBUser) bool { - for i := range users { - if users[i] == nil { - return false - } - } - return true -} - -func allNil(users []*om.MongoDBUser) bool { - for i := range users { - if users[i] != nil { - return false - } - } - return true -} diff --git a/controllers/operator/authentication/ldap.go b/controllers/operator/authentication/ldap.go index 1cd8a0b6c..debfcfa16 100644 --- a/controllers/operator/authentication/ldap.go +++ b/controllers/operator/authentication/ldap.go @@ -1,10 +1,14 @@ package authentication import ( + "context" + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/types" "github.com/mongodb/mongodb-kubernetes/controllers/om" "github.com/mongodb/mongodb-kubernetes/controllers/operator/ldap" + kubernetesClient "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/client" "github.com/mongodb/mongodb-kubernetes/pkg/util" "github.com/mongodb/mongodb-kubernetes/pkg/util/stringutil" ) @@ -15,10 +19,10 @@ func (l *ldapAuthMechanism) GetName() MechanismName { return LDAPPlain } -func (l *ldapAuthMechanism) EnableAgentAuthentication(conn om.Connection, opts Options, log *zap.SugaredLogger) error { +func (l *ldapAuthMechanism) EnableAgentAuthentication(client kubernetesClient.Client, ctx context.Context, namespacedName *types.NamespacedName, conn om.Connection, opts Options, log *zap.SugaredLogger) error { log.Info("Configuring LDAP authentication") err := conn.ReadUpdateAutomationConfig(func(ac *om.AutomationConfig) error { - if err := ac.EnsureKeyFileContents(); err != nil { + if err := ac.EnsureKeyFileContents(client, ctx, namespacedName); err != nil { return err } auth := ac.Auth diff --git a/controllers/operator/authentication/ldap_test.go b/controllers/operator/authentication/ldap_test.go deleted file mode 100644 index 0b619e3df..000000000 --- a/controllers/operator/authentication/ldap_test.go +++ /dev/null @@ -1,84 +0,0 @@ -package authentication - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - - "github.com/mongodb/mongodb-kubernetes/controllers/om" - "github.com/mongodb/mongodb-kubernetes/controllers/operator/ldap" -) - -var ldapPlainMechanism = getMechanismByName(LDAPPlain) - -func TestLdapDeploymentMechanism(t *testing.T) { - conn := om.NewMockedOmConnection(om.NewDeployment()) - - opts := Options{ - Ldap: &ldap.Ldap{ - BindMethod: "BindMethod", - BindQueryUser: "BindQueryUser", - Servers: "Servers", - }, - } - - err := ldapPlainMechanism.EnableDeploymentAuthentication(conn, opts, zap.S()) - require.NoError(t, err) - - ac, err := conn.ReadAutomationConfig() - require.NoError(t, err) - assert.Contains(t, ac.Auth.DeploymentAuthMechanisms, string(LDAPPlain)) - assert.Equal(t, "BindQueryUser", ac.Ldap.BindQueryUser) - assert.Equal(t, "Servers", ac.Ldap.Servers) - assert.Equal(t, "BindMethod", ac.Ldap.BindMethod) - - err = ldapPlainMechanism.DisableDeploymentAuthentication(conn, zap.S()) - require.NoError(t, err) - - ac, err = conn.ReadAutomationConfig() - require.NoError(t, err) - - assert.NotContains(t, ac.Auth.DeploymentAuthMechanisms, string(LDAPPlain)) - assert.Nil(t, ac.Ldap) -} - -func TestLdapEnableAgentAuthentication(t *testing.T) { - conn := om.NewMockedOmConnection(om.NewDeployment()) - opts := Options{ - AgentMechanism: "LDAP", - UserOptions: UserOptions{ - AutomationSubject: "mms-automation", - }, - AuthoritativeSet: true, - AutoPwd: "LDAPPassword.", - } - - err := ldapPlainMechanism.EnableAgentAuthentication(conn, opts, zap.S()) - require.NoError(t, err) - - ac, err := conn.ReadAutomationConfig() - require.NoError(t, err) - - assert.Equal(t, ac.Auth.AutoUser, opts.AutomationSubject) - assert.Len(t, ac.Auth.AutoAuthMechanisms, 1) - assert.Contains(t, ac.Auth.AutoAuthMechanisms, string(LDAPPlain)) - assert.Equal(t, "LDAPPassword.", ac.Auth.AutoPwd) - assert.False(t, ac.Auth.Disabled) - - assert.True(t, ac.Auth.AuthoritativeSet) -} - -func TestLDAP_DisableAgentAuthentication(t *testing.T) { - conn := om.NewMockedOmConnection(om.NewDeployment()) - - opts := Options{ - AutoPwd: "LDAPPassword.", - UserOptions: UserOptions{ - AutomationSubject: validSubject("automation"), - }, - } - - assertAgentAuthenticationDisabled(t, ldapPlainMechanism, conn, opts) -} diff --git a/controllers/operator/authentication/oidc.go b/controllers/operator/authentication/oidc.go index 7e9bf4c2c..10ee71f76 100644 --- a/controllers/operator/authentication/oidc.go +++ b/controllers/operator/authentication/oidc.go @@ -1,16 +1,19 @@ package authentication import ( + "context" "fmt" "slices" "strings" "go.uber.org/zap" "golang.org/x/xerrors" + "k8s.io/apimachinery/pkg/types" mdbv1 "github.com/mongodb/mongodb-kubernetes/api/v1/mdb" "github.com/mongodb/mongodb-kubernetes/controllers/om" "github.com/mongodb/mongodb-kubernetes/controllers/operator/oidc" + kubernetesClient "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/client" "github.com/mongodb/mongodb-kubernetes/pkg/util/stringutil" ) @@ -20,7 +23,7 @@ func (o *oidcAuthMechanism) GetName() MechanismName { return MongoDBOIDC } -func (o *oidcAuthMechanism) EnableAgentAuthentication(_ om.Connection, _ Options, _ *zap.SugaredLogger) error { +func (o *oidcAuthMechanism) EnableAgentAuthentication(client kubernetesClient.Client, ctx context.Context, namespacedName *types.NamespacedName, _ om.Connection, _ Options, _ *zap.SugaredLogger) error { return xerrors.Errorf("OIDC agent authentication is not supported") } diff --git a/controllers/operator/authentication/oidc_test.go b/controllers/operator/authentication/oidc_test.go deleted file mode 100644 index 6460db803..000000000 --- a/controllers/operator/authentication/oidc_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package authentication - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - "k8s.io/utils/ptr" - - "github.com/mongodb/mongodb-kubernetes/controllers/om" - "github.com/mongodb/mongodb-kubernetes/controllers/operator/oidc" -) - -var mongoDBOIDCMechanism = getMechanismByName(MongoDBOIDC) - -func TestOIDC_EnableDeploymentAuthentication(t *testing.T) { - conn := om.NewMockedOmConnection(om.NewDeployment()) - ac, err := conn.ReadAutomationConfig() - require.NoError(t, err) - assert.Empty(t, ac.OIDCProviderConfigs) - assert.Empty(t, ac.Auth.DeploymentAuthMechanisms) - - providerConfigs := []oidc.ProviderConfig{ - { - AuthNamePrefix: "okta", - Audience: "aud", - IssuerUri: "https://okta.mongodb.com", - ClientId: ptr.To("client1"), - RequestedScopes: []string{"openid", "profile"}, - UserClaim: "sub", - SupportsHumanFlows: true, - UseAuthorizationClaim: false, - }, - { - AuthNamePrefix: "congito", - Audience: "aud", - IssuerUri: "https://congito.mongodb.com", - ClientId: ptr.To("client2"), - UserClaim: "sub", - GroupsClaim: ptr.To("groups"), - SupportsHumanFlows: false, - UseAuthorizationClaim: true, - }, - } - - opts := Options{ - Mechanisms: []string{string(MongoDBOIDC)}, - OIDCProviderConfigs: providerConfigs, - } - - configured := mongoDBOIDCMechanism.IsDeploymentAuthenticationConfigured(ac, opts) - assert.False(t, configured) - - err = mongoDBOIDCMechanism.EnableDeploymentAuthentication(conn, opts, zap.S()) - require.NoError(t, err) - - ac, err = conn.ReadAutomationConfig() - require.NoError(t, err) - assert.Contains(t, ac.Auth.DeploymentAuthMechanisms, string(MongoDBOIDC)) - assert.Equal(t, providerConfigs, ac.OIDCProviderConfigs) - - configured = mongoDBOIDCMechanism.IsDeploymentAuthenticationConfigured(ac, opts) - assert.True(t, configured) - - err = mongoDBOIDCMechanism.DisableDeploymentAuthentication(conn, zap.S()) - require.NoError(t, err) - - ac, err = conn.ReadAutomationConfig() - require.NoError(t, err) - - configured = mongoDBOIDCMechanism.IsDeploymentAuthenticationConfigured(ac, opts) - assert.False(t, configured) - - assert.NotContains(t, ac.Auth.DeploymentAuthMechanisms, string(MongoDBOIDC)) - assert.Empty(t, ac.OIDCProviderConfigs) -} - -func TestOIDC_EnableAgentAuthentication(t *testing.T) { - conn := om.NewMockedOmConnection(om.NewDeployment()) - opts := Options{ - Mechanisms: []string{string(MongoDBOIDC)}, - } - - ac, err := conn.ReadAutomationConfig() - require.NoError(t, err) - - configured := mongoDBOIDCMechanism.IsAgentAuthenticationConfigured(ac, opts) - assert.False(t, configured) - - err = mongoDBOIDCMechanism.EnableAgentAuthentication(conn, opts, zap.S()) - require.Error(t, err) - - err = mongoDBOIDCMechanism.DisableAgentAuthentication(conn, zap.S()) - require.Error(t, err) -} diff --git a/controllers/operator/authentication/scramsha.go b/controllers/operator/authentication/scramsha.go index ee186b869..461d816d1 100644 --- a/controllers/operator/authentication/scramsha.go +++ b/controllers/operator/authentication/scramsha.go @@ -1,9 +1,13 @@ package authentication import ( + "context" + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/types" "github.com/mongodb/mongodb-kubernetes/controllers/om" + kubernetesClient "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/client" "github.com/mongodb/mongodb-kubernetes/pkg/util" "github.com/mongodb/mongodb-kubernetes/pkg/util/stringutil" ) @@ -18,12 +22,13 @@ func (s *automationConfigScramSha) GetName() MechanismName { return s.MechanismName } -func (s *automationConfigScramSha) EnableAgentAuthentication(conn om.Connection, opts Options, log *zap.SugaredLogger) error { +func (s *automationConfigScramSha) EnableAgentAuthentication(client kubernetesClient.Client, ctx context.Context, namespacedName *types.NamespacedName, conn om.Connection, opts Options, log *zap.SugaredLogger) error { return conn.ReadUpdateAutomationConfig(func(ac *om.AutomationConfig) error { - if err := configureScramAgentUsers(ac, opts); err != nil { + + if err := configureScramAgentUsers(client, ctx, namespacedName, ac, opts); err != nil { return err } - if err := ac.EnsureKeyFileContents(); err != nil { + if err := ac.EnsureKeyFileContents(client, ctx, namespacedName); err != nil { return err } @@ -91,8 +96,8 @@ func (s *automationConfigScramSha) IsDeploymentAuthenticationEnabled(ac *om.Auto } // configureScramAgentUsers makes sure that the given automation config always has the correct SCRAM-SHA users -func configureScramAgentUsers(ac *om.AutomationConfig, authOpts Options) error { - agentPassword, err := ac.EnsurePassword() +func configureScramAgentUsers(client kubernetesClient.Client, ctx context.Context, namespacedName *types.NamespacedName, ac *om.AutomationConfig, authOpts Options) error { + agentPassword, err := ac.EnsurePassword(client, ctx, namespacedName) if err != nil { return err } @@ -101,6 +106,5 @@ func configureScramAgentUsers(ac *om.AutomationConfig, authOpts Options) error { auth.AutoUser = authOpts.AutoUser } auth.AutoPwd = agentPassword - return nil } diff --git a/controllers/operator/authentication/scramsha_test.go b/controllers/operator/authentication/scramsha_test.go deleted file mode 100644 index 1c97e9943..000000000 --- a/controllers/operator/authentication/scramsha_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package authentication - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - - "github.com/mongodb/mongodb-kubernetes/controllers/om" - "github.com/mongodb/mongodb-kubernetes/pkg/util" -) - -var ( - mongoDBCRMechanism = getMechanismByName(MongoDBCR) - scramSha1Mechanism = getMechanismByName(ScramSha1) - scramSha256Mechanism = getMechanismByName(ScramSha256) -) - -func TestAgentsAuthentication(t *testing.T) { - type TestConfig struct { - mechanism Mechanism - } - tests := map[string]TestConfig{ - "SCRAM-SHA-1": { - mechanism: scramSha1Mechanism, - }, - "SCRAM-SHA-256": { - mechanism: scramSha256Mechanism, - }, - "CR": { - mechanism: mongoDBCRMechanism, - }, - } - for testName, testConfig := range tests { - t.Run(testName, func(t *testing.T) { - conn := om.NewMockedOmConnection(om.NewDeployment()) - - s := testConfig.mechanism - - opts := Options{ - AuthoritativeSet: true, - CAFilePath: util.CAFilePathInContainer, - } - - err := s.EnableAgentAuthentication(conn, opts, zap.S()) - require.NoError(t, err) - - err = s.EnableDeploymentAuthentication(conn, opts, zap.S()) - require.NoError(t, err) - - ac, err := conn.ReadAutomationConfig() - require.NoError(t, err) - - assertAuthenticationEnabled(t, ac.Auth) - assert.Equal(t, ac.Auth.AutoUser, util.AutomationAgentName) - assert.Len(t, ac.Auth.AutoAuthMechanisms, 1) - assert.Contains(t, ac.Auth.AutoAuthMechanisms, string(testConfig.mechanism.GetName())) - assert.NotEmpty(t, ac.Auth.AutoPwd) - assert.True(t, s.IsAgentAuthenticationConfigured(ac, opts)) - assert.True(t, s.IsDeploymentAuthenticationConfigured(ac, opts)) - }) - } -} - -func TestScramSha1_DisableAgentAuthentication(t *testing.T) { - conn := om.NewMockedOmConnection(om.NewDeployment()) - assertAgentAuthenticationDisabled(t, scramSha1Mechanism, conn, Options{}) -} - -func TestScramSha256_DisableAgentAuthentication(t *testing.T) { - conn := om.NewMockedOmConnection(om.NewDeployment()) - assertAgentAuthenticationDisabled(t, scramSha256Mechanism, conn, Options{}) -} diff --git a/controllers/operator/authentication/x509.go b/controllers/operator/authentication/x509.go index e863b7780..90b43b62b 100644 --- a/controllers/operator/authentication/x509.go +++ b/controllers/operator/authentication/x509.go @@ -1,11 +1,14 @@ package authentication import ( + "context" "regexp" "go.uber.org/zap" + "k8s.io/apimachinery/pkg/types" "github.com/mongodb/mongodb-kubernetes/controllers/om" + kubernetesClient "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/client" "github.com/mongodb/mongodb-kubernetes/pkg/util" "github.com/mongodb/mongodb-kubernetes/pkg/util/stringutil" ) @@ -16,10 +19,10 @@ func (x *connectionX509) GetName() MechanismName { return MongoDBX509 } -func (x *connectionX509) EnableAgentAuthentication(conn om.Connection, opts Options, log *zap.SugaredLogger) error { +func (x *connectionX509) EnableAgentAuthentication(client kubernetesClient.Client, ctx context.Context, namespacedName *types.NamespacedName, conn om.Connection, opts Options, log *zap.SugaredLogger) error { log.Info("Configuring x509 authentication") err := conn.ReadUpdateAutomationConfig(func(ac *om.AutomationConfig) error { - if err := ac.EnsureKeyFileContents(); err != nil { + if err := ac.EnsureKeyFileContents(client, ctx, namespacedName); err != nil { return err } auth := ac.Auth diff --git a/controllers/operator/authentication/x509_test.go b/controllers/operator/authentication/x509_test.go deleted file mode 100644 index f342b6702..000000000 --- a/controllers/operator/authentication/x509_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package authentication - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - - "github.com/mongodb/mongodb-kubernetes/controllers/om" - "github.com/mongodb/mongodb-kubernetes/pkg/util" -) - -var mongoDBX509Mechanism = getMechanismByName(MongoDBX509) - -func TestX509EnableAgentAuthentication(t *testing.T) { - conn := om.NewMockedOmConnection(om.NewDeployment()) - - options := Options{ - AgentMechanism: "X509", - ClientCertificates: util.RequireClientCertificates, - UserOptions: UserOptions{ - AutomationSubject: validSubject("automation"), - }, - AuthoritativeSet: true, - } - if err := mongoDBX509Mechanism.EnableAgentAuthentication(conn, options, zap.S()); err != nil { - t.Fatal(err) - } - - ac, err := conn.ReadAutomationConfig() - if err != nil { - t.Fatal(err) - } - - assert.Equal(t, ac.Auth.AutoUser, options.AutomationSubject) - assert.Len(t, ac.Auth.AutoAuthMechanisms, 1) - assert.Contains(t, ac.Auth.AutoAuthMechanisms, string(MongoDBX509)) - assert.Equal(t, ac.Auth.AutoPwd, util.MergoDelete) - assert.False(t, ac.Auth.Disabled) - assert.Len(t, ac.Auth.Users, 0) - - assert.True(t, ac.Auth.AuthoritativeSet) - assert.NotEmpty(t, ac.Auth.Key) - assert.NotEmpty(t, ac.Auth.KeyFileWindows) - assert.NotEmpty(t, ac.Auth.KeyFile) -} - -func TestX509_DisableAgentAuthentication(t *testing.T) { - conn := om.NewMockedOmConnection(om.NewDeployment()) - - opts := Options{ - UserOptions: UserOptions{ - AutomationSubject: validSubject("automation"), - }, - } - assertAgentAuthenticationDisabled(t, mongoDBX509Mechanism, conn, opts) -} - -func TestX509_DeploymentConfigured(t *testing.T) { - conn := om.NewMockedOmConnection(om.NewDeployment()) - opts := Options{AgentMechanism: "SCRAM", CAFilePath: util.CAFilePathInContainer} - - assertDeploymentMechanismsConfigured(t, mongoDBX509Mechanism, conn, opts) - - ac, err := conn.ReadAutomationConfig() - require.NoError(t, err) - assert.Equal(t, ac.AgentSSL.CAFilePath, util.CAFilePathInContainer) -} - -func validSubject(o string) string { - return fmt.Sprintf("CN=mms-automation-agent,OU=MongoDB Kubernetes Operator,O=%s,L=NY,ST=NY,C=US", o) -} diff --git a/controllers/operator/common_controller.go b/controllers/operator/common_controller.go index e76cf4e81..72babde36 100644 --- a/controllers/operator/common_controller.go +++ b/controllers/operator/common_controller.go @@ -514,7 +514,7 @@ func (r *ReconcileCommonController) updateOmAuthentication(ctx context.Context, authOpts.UserOptions = userOpts } - if err := authentication.Configure(conn, authOpts, isRecovering, log); err != nil { + if err := authentication.Configure(r.client, ctx, &types.NamespacedName{Namespace: ar.GetNamespace(), Name: ar.GetName()}, conn, authOpts, isRecovering, log); err != nil { return workflow.Failed(err), false } } else if wantToEnableAuthentication { @@ -533,7 +533,7 @@ func (r *ReconcileCommonController) updateOmAuthentication(ctx context.Context, } authOpts.UserOptions = userOpts - if err := authentication.Disable(conn, authOpts, false, log); err != nil { + if err := authentication.Disable(r.client, ctx, &types.NamespacedName{Namespace: ar.GetNamespace(), Name: ar.GetName()}, conn, authOpts, false, log); err != nil { return workflow.Failed(err), false } } @@ -597,7 +597,7 @@ func (r *ReconcileCommonController) clearProjectAuthenticationSettings(ctx conte UserOptions: userOpts, } - return authentication.Disable(conn, disableOpts, true, log) + return authentication.Disable(r.client, ctx, &types.NamespacedName{Namespace: mdb.Namespace, Name: mdb.Name}, conn, disableOpts, true, log) } // ensureX509SecretAndCheckTLSType checks if the secrets containing the certificates are present and whether the certificate are of kubernetes.io/tls type. diff --git a/controllers/operator/mongodbmultireplicaset_controller.go b/controllers/operator/mongodbmultireplicaset_controller.go index 5d963c00e..bddea035c 100644 --- a/controllers/operator/mongodbmultireplicaset_controller.go +++ b/controllers/operator/mongodbmultireplicaset_controller.go @@ -1236,7 +1236,7 @@ func (r *ReconcileMongoDbMultiReplicaSet) cleanOpsManagerState(ctx context.Conte ProcessNames: processNames, } - if err := authentication.Disable(conn, opts, true, log); err != nil { + if err := authentication.Disable(r.client, ctx, &types.NamespacedName{Namespace: mrs.GetNamespace(), Name: mrs.GetName()}, conn, opts, true, log); err != nil { return err } log.Infof("Removed deployment %s from Ops Manager at %s", mrs.Name, conn.BaseURL()) diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/sharded-cluster-scram-sha-256-switch-project-after.yaml b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/sharded-cluster-scram-sha-256-switch-project-after.yaml new file mode 100644 index 000000000..994ccc729 --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/sharded-cluster-scram-sha-256-switch-project-after.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: mongodb.com/v1 +kind: MongoDB +metadata: + name: sharded-cluster-scram-sha-256-switch-project +spec: + shardCount: 1 + mongodsPerShardCount: 1 + mongosCount: 1 + configServerCount: 1 + version: 4.4.0 + type: ShardedCluster + + opsManager: + configMapRef: + name: my-project-moved + credentials: my-credentials + + persistent: true + security: + authentication: + enabled: true + modes: ["SCRAM"] diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/sharded-cluster-scram-sha-256-switch-project-before.yaml b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/sharded-cluster-scram-sha-256-switch-project-before.yaml new file mode 100644 index 000000000..ff558d3a6 --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/fixtures/sharded-cluster-scram-sha-256-switch-project-before.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: mongodb.com/v1 +kind: MongoDB +metadata: + name: sharded-cluster-scram-sha-256-switch-project +spec: + shardCount: 1 + mongodsPerShardCount: 1 + mongosCount: 1 + configServerCount: 1 + version: 4.4.0 + type: ShardedCluster + + opsManager: + configMapRef: + name: my-project + credentials: my-credentials + + persistent: true + security: + authentication: + enabled: true + modes: ["SCRAM"] diff --git a/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_256_switch_project.py b/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_256_switch_project.py new file mode 100644 index 000000000..45cae30f7 --- /dev/null +++ b/docker/mongodb-kubernetes-tests/tests/authentication/sharded_cluster_scram_sha_256_switch_project.py @@ -0,0 +1,89 @@ +import pytest +from kubetester.automation_config_tester import AutomationConfigTester +from kubetester.kubetester import KubernetesTester +from kubetester.kubetester import fixture as load_fixture +from kubetester.mongodb import MongoDB +from kubetester.mongotester import ShardedClusterTester +from kubetester.phase import Phase + +from kubetester import ( + read_configmap, + random_k8s_name, + create_or_update_configmap, +) + +MDB_RESOURCE_NAME = "sharded-cluster-scram-sha-256-switch-project" +MDB_RESOURCE_BEFORE = "sharded-cluster-scram-sha-256-switch-project-before" +MDB_RESOURCE_AFTER = "sharded-cluster-scram-sha-256-switch-project-after" + +@pytest.fixture(scope="module") +def project_name_prefix(namespace: str) -> str: + """Generates a random Kubernetes project name prefix based on the namespace.""" + return random_k8s_name(f"{namespace}-project-") + + +@pytest.fixture(scope="module") +def second_project(namespace: str, project_name_prefix: str) -> str: + """Creates or updates a ConfigMap for the second project.""" + cm = read_configmap(namespace=namespace, name="my-project") + project_name = f"{project_name_prefix}-second" + return create_or_update_configmap( + namespace=namespace, + name=project_name, + data={ + "baseUrl": cm["baseUrl"], + "projectName": project_name, + "orgId": cm["orgId"], + }, + ) + +@pytest.fixture(scope="module") +def sharded_cluster(namespace: str) -> MongoDB: + """Fixture to initialize the MongoDB resource for the sharded cluster before the project switch.""" + resource = MongoDB.from_yaml(load_fixture(f"{MDB_RESOURCE_BEFORE}.yaml"), namespace=namespace) + return resource + + +@pytest.fixture(scope="module") +def second_sharded_cluster(namespace: str, second_project: str) -> MongoDB: + """Fixture to initialize the MongoDB resource for the sharded cluster after the project switch.""" + resource = MongoDB.from_yaml(load_fixture(f"{MDB_RESOURCE_AFTER}.yaml"), namespace=namespace) + resource["spec"]["opsManager"]["configMapRef"]["name"] = second_project + return resource + + +@pytest.mark.e2e_sharded_cluster_scram_sha_256_switch_project +class TestShardedClusterCreationAndProjectSwitch(KubernetesTester): + """ + description: | + Creates a Sharded Cluster which switches the project and checks everything is created as expected. + """ + + def test_create_sharded_cluster(self, custom_mdb_version: str, sharded_cluster: MongoDB): + sharded_cluster.set_version(custom_mdb_version) + sharded_cluster.update() + + sharded_cluster.assert_reaches_phase(Phase.Running, timeout=600) + + def test_sharded_cluster_connectivity(self): + ShardedClusterTester(MDB_RESOURCE_NAME, 1).assert_connectivity() + + def test_ops_manager_state_correctly_updated_in_original_one(self, sharded_cluster: MongoDB): + tester = sharded_cluster.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") + tester.assert_authentication_enabled() + + def test_move_sharded_cluster(self, custom_mdb_version: str, second_sharded_cluster: MongoDB): + second_sharded_cluster.set_version(custom_mdb_version) + second_sharded_cluster.update() + + second_sharded_cluster.assert_reaches_phase(Phase.Running, timeout=600) + + def test_moved_sharded_cluster_connectivity(self): + ShardedClusterTester(MDB_RESOURCE_NAME, 1).assert_connectivity() + + def test_ops_manager_state_correctly_updated_in_moved_one(self, second_sharded_cluster: MongoDB): + tester = second_sharded_cluster.get_automation_config_tester() + tester.assert_authentication_mechanism_enabled("SCRAM-SHA-256") + tester.assert_authentication_enabled() + diff --git a/main.go b/main.go index 5e5185825..4ac70bc12 100644 --- a/main.go +++ b/main.go @@ -26,11 +26,12 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/manager/signals" + localruntime "runtime" + sdktrace "go.opentelemetry.io/otel/sdk/trace" corev1 "k8s.io/api/core/v1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" - localruntime "runtime" ctrl "sigs.k8s.io/controller-runtime" runtime_cluster "sigs.k8s.io/controller-runtime/pkg/cluster" kubelog "sigs.k8s.io/controller-runtime/pkg/log" diff --git a/mongodb-community-operator/pkg/authentication/authentication_test.go b/mongodb-community-operator/pkg/authentication/authentication_test.go deleted file mode 100644 index c3e8fae40..000000000 --- a/mongodb-community-operator/pkg/authentication/authentication_test.go +++ /dev/null @@ -1,249 +0,0 @@ -package authentication - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "k8s.io/apimachinery/pkg/types" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - mdbv1 "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/api/v1" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/authentication/authtypes" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/authentication/mocks" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/authentication/x509" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/automationconfig" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/secret" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/util/constants" -) - -func TestEnable(t *testing.T) { - ctx := context.Background() - t.Run("SCRAM only", func(t *testing.T) { - auth := automationconfig.Auth{} - user := mocks.BuildScramMongoDBUser("my-user") - mdb := buildConfigurable("mdb", []string{constants.Sha256}, constants.Sha256, user) - passwordSecret := secret.Builder(). - SetName(user.PasswordSecretName). - SetNamespace(mdb.NamespacedName().Namespace). - SetField(user.PasswordSecretKey, "TDg_DESiScDrJV6"). - Build() - secrets := mocks.NewMockedSecretGetUpdateCreateDeleter(passwordSecret) - - err := Enable(ctx, &auth, secrets, mdb, mdb.AgentCertificateSecretNamespacedName()) - assert.NoError(t, err) - - assert.Equal(t, false, auth.Disabled) - assert.Equal(t, constants.Sha256, auth.AutoAuthMechanism) - assert.Equal(t, []string{constants.Sha256}, auth.DeploymentAuthMechanisms) - assert.Equal(t, []string{constants.Sha256}, auth.AutoAuthMechanisms) - assert.Len(t, auth.Users, 1) - assert.Equal(t, "my-user", auth.Users[0].Username) - assert.Equal(t, "mms-automation", auth.AutoUser) - }) - t.Run("SCRAM-SHA-256 and SCRAM-SHA-1", func(t *testing.T) { - auth := automationconfig.Auth{} - user := mocks.BuildScramMongoDBUser("my-user") - mdb := buildConfigurable("mdb", []string{constants.Sha256, constants.Sha1}, constants.Sha256, user) - passwordSecret := secret.Builder(). - SetName(user.PasswordSecretName). - SetNamespace(mdb.NamespacedName().Namespace). - SetField(user.PasswordSecretKey, "TDg_DESiScDrJV6"). - Build() - secrets := mocks.NewMockedSecretGetUpdateCreateDeleter(passwordSecret) - - err := Enable(ctx, &auth, secrets, mdb, mdb.AgentCertificateSecretNamespacedName()) - assert.NoError(t, err) - - assert.Equal(t, false, auth.Disabled) - assert.Equal(t, constants.Sha256, auth.AutoAuthMechanism) - assert.Equal(t, []string{constants.Sha256, constants.Sha1}, auth.DeploymentAuthMechanisms) - assert.Equal(t, []string{constants.Sha256, constants.Sha1}, auth.AutoAuthMechanisms) - assert.Len(t, auth.Users, 1) - assert.Equal(t, "my-user", auth.Users[0].Username) - assert.Equal(t, "mms-automation", auth.AutoUser) - }) - t.Run("X509 only", func(t *testing.T) { - auth := automationconfig.Auth{} - user := mocks.BuildX509MongoDBUser("my-user") - mdb := buildConfigurable("mdb", []string{constants.X509}, constants.X509, user) - agentSecret := x509.CreateAgentCertificateSecret("tls.crt", false, mdb.AgentCertificateSecretNamespacedName()) - secrets := mocks.NewMockedSecretGetUpdateCreateDeleter(agentSecret) - - err := Enable(ctx, &auth, secrets, mdb, mdb.AgentCertificateSecretNamespacedName()) - assert.NoError(t, err) - - assert.Equal(t, false, auth.Disabled) - assert.Equal(t, constants.X509, auth.AutoAuthMechanism) - assert.Equal(t, []string{constants.X509}, auth.DeploymentAuthMechanisms) - assert.Equal(t, []string{constants.X509}, auth.AutoAuthMechanisms) - assert.Len(t, auth.Users, 1) - assert.Equal(t, "CN=my-user,OU=organizationalunit,O=organization", auth.Users[0].Username) - assert.Equal(t, "CN=mms-automation-agent,OU=ENG,O=MongoDB,C=US", auth.AutoUser) - }) - t.Run("SCRAM and X509 with SCRAM agent", func(t *testing.T) { - auth := automationconfig.Auth{} - userScram := mocks.BuildScramMongoDBUser("my-user") - userX509 := mocks.BuildX509MongoDBUser("my-user") - mdb := buildConfigurable("mdb", []string{constants.Sha256, constants.X509}, constants.Sha256, userScram, userX509) - passwordSecret := secret.Builder(). - SetName(userScram.PasswordSecretName). - SetNamespace(mdb.NamespacedName().Namespace). - SetField(userScram.PasswordSecretKey, "TDg_DESiScDrJV6"). - Build() - secrets := mocks.NewMockedSecretGetUpdateCreateDeleter(passwordSecret) - - err := Enable(ctx, &auth, secrets, mdb, mdb.AgentCertificateSecretNamespacedName()) - assert.NoError(t, err) - - assert.Equal(t, false, auth.Disabled) - assert.Equal(t, constants.Sha256, auth.AutoAuthMechanism) - assert.Equal(t, []string{constants.Sha256, constants.X509}, auth.DeploymentAuthMechanisms) - assert.Equal(t, []string{constants.Sha256}, auth.AutoAuthMechanisms) - assert.Len(t, auth.Users, 2) - assert.Equal(t, "my-user", auth.Users[0].Username) - assert.Equal(t, "CN=my-user,OU=organizationalunit,O=organization", auth.Users[1].Username) - assert.Equal(t, "mms-automation", auth.AutoUser) - }) - t.Run("SCRAM and X509 with X509 agent", func(t *testing.T) { - auth := automationconfig.Auth{} - userScram := mocks.BuildScramMongoDBUser("my-user") - userX509 := mocks.BuildX509MongoDBUser("my-user") - mdb := buildConfigurable("mdb", []string{constants.Sha256, constants.X509}, constants.X509, userScram, userX509) - passwordSecret := secret.Builder(). - SetName(userScram.PasswordSecretName). - SetNamespace(mdb.NamespacedName().Namespace). - SetField(userScram.PasswordSecretKey, "TDg_DESiScDrJV6"). - Build() - agentSecret := x509.CreateAgentCertificateSecret("tls.crt", false, mdb.AgentCertificateSecretNamespacedName()) - secrets := mocks.NewMockedSecretGetUpdateCreateDeleter(passwordSecret, agentSecret) - - err := Enable(ctx, &auth, secrets, mdb, mdb.AgentCertificateSecretNamespacedName()) - assert.NoError(t, err) - - assert.Equal(t, false, auth.Disabled) - assert.Equal(t, constants.X509, auth.AutoAuthMechanism) - assert.Equal(t, []string{constants.Sha256, constants.X509}, auth.DeploymentAuthMechanisms) - assert.Equal(t, []string{constants.X509}, auth.AutoAuthMechanisms) - assert.Len(t, auth.Users, 2) - assert.Equal(t, "my-user", auth.Users[0].Username) - assert.Equal(t, "CN=my-user,OU=organizationalunit,O=organization", auth.Users[1].Username) - assert.Equal(t, "CN=mms-automation-agent,OU=ENG,O=MongoDB,C=US", auth.AutoUser) - }) -} - -func TestGetDeletedUsers(t *testing.T) { - lastAppliedSpec := mdbv1.MongoDBCommunitySpec{ - Members: 3, - Type: "ReplicaSet", - Version: "7.0.2", - Arbiters: 0, - Security: mdbv1.Security{ - Authentication: mdbv1.Authentication{ - Modes: []mdbv1.AuthMode{"SCRAM"}, - }, - }, - Users: []mdbv1.MongoDBUser{ - { - Name: "testUser", - PasswordSecretRef: mdbv1.SecretKeyReference{ - Name: "password-secret-name", - }, - ConnectionStringSecretName: "connection-string-secret", - DB: "admin", - }, - }, - } - - t.Run("no change same resource", func(t *testing.T) { - actual := getRemovedUsersFromSpec(lastAppliedSpec, &lastAppliedSpec) - - var expected []automationconfig.DeletedUser - assert.Equal(t, expected, actual) - }) - - t.Run("new user", func(t *testing.T) { - current := mdbv1.MongoDBCommunitySpec{ - Members: 3, - Type: "ReplicaSet", - Version: "7.0.2", - Arbiters: 0, - Security: mdbv1.Security{ - Authentication: mdbv1.Authentication{ - Modes: []mdbv1.AuthMode{"SCRAM"}, - }, - }, - Users: []mdbv1.MongoDBUser{ - { - Name: "testUser", - PasswordSecretRef: mdbv1.SecretKeyReference{ - Name: "password-secret-name", - }, - ConnectionStringSecretName: "connection-string-secret", - DB: "admin", - }, - { - Name: "newUser", - PasswordSecretRef: mdbv1.SecretKeyReference{ - Name: "new-password-secret-name", - }, - ConnectionStringSecretName: "new-connection-string-secret", - DB: "admin", - }, - }, - } - - var expected []automationconfig.DeletedUser - actual := getRemovedUsersFromSpec(current, &lastAppliedSpec) - - assert.Equal(t, expected, actual) - }) - - t.Run("removed one user", func(t *testing.T) { - current := mdbv1.MongoDBCommunitySpec{ - Members: 3, - Type: "ReplicaSet", - Version: "7.0.2", - Arbiters: 0, - Security: mdbv1.Security{ - Authentication: mdbv1.Authentication{ - Modes: []mdbv1.AuthMode{"SCRAM"}, - }, - }, - Users: []mdbv1.MongoDBUser{}, - } - - expected := []automationconfig.DeletedUser{ - { - User: "testUser", - Dbs: []string{"admin"}, - }, - } - actual := getRemovedUsersFromSpec(current, &lastAppliedSpec) - - assert.Equal(t, expected, actual) - }) -} - -func buildConfigurable(name string, auth []string, agent string, users ...authtypes.User) mocks.MockConfigurable { - return mocks.NewMockConfigurable( - authtypes.Options{ - AuthoritativeSet: false, - KeyFile: "/path/to/keyfile", - AuthMechanisms: auth, - AgentName: constants.AgentName, - AutoAuthMechanism: agent, - }, - users, - types.NamespacedName{ - Name: name, - Namespace: "default", - }, - []metav1.OwnerReference{{ - APIVersion: "v1", - Kind: "mdbc", - Name: "my-ref", - }}, - ) -} diff --git a/mongodb-community-operator/pkg/authentication/scram/scram_enabler_test.go b/mongodb-community-operator/pkg/authentication/scram/scram_enabler_test.go deleted file mode 100644 index 0599b8bd1..000000000 --- a/mongodb-community-operator/pkg/authentication/scram/scram_enabler_test.go +++ /dev/null @@ -1,110 +0,0 @@ -package scram - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/authentication/authtypes" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/automationconfig" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/util/constants" -) - -func TestScramAutomationConfig(t *testing.T) { - // Case 1: Both SHA-256 and SHA-1 - auth := automationconfig.Auth{} - opts := authtypes.Options{ - AuthoritativeSet: false, - KeyFile: constants.AutomationAgentKeyFilePathInContainer, - AuthMechanisms: []string{constants.Sha256, constants.Sha1}, - AgentName: "mms-automation", - AutoAuthMechanism: constants.Sha256, - } - err := configureInAutomationConfig(&auth, "password", "keyfilecontents", []automationconfig.MongoDBUser{}, opts) - assert.NoError(t, err) - - t.Run("Authentication is correctly configured", func(t *testing.T) { - assert.Equal(t, constants.AgentName, auth.AutoUser) - assert.Equal(t, "keyfilecontents", auth.Key) - assert.Equal(t, "password", auth.AutoPwd) - assert.Equal(t, constants.Sha256, auth.AutoAuthMechanism) - assert.Len(t, auth.DeploymentAuthMechanisms, 2) - assert.Len(t, auth.AutoAuthMechanisms, 2) - assert.Equal(t, []string{constants.Sha256, constants.Sha1}, auth.DeploymentAuthMechanisms) - assert.Equal(t, []string{constants.Sha256, constants.Sha1}, auth.AutoAuthMechanisms) - assert.Equal(t, constants.AutomationAgentKeyFilePathInContainer, auth.KeyFile) - assert.Equal(t, constants.AutomationAgentWindowsKeyFilePath, auth.KeyFileWindows) - }) - t.Run("Subsequent configuration doesn't add to deployment auth mechanisms", func(t *testing.T) { - err := configureInAutomationConfig(&auth, "password", "keyfilecontents", []automationconfig.MongoDBUser{}, opts) - assert.NoError(t, err) - assert.Equal(t, []string{constants.Sha256, constants.Sha1}, auth.DeploymentAuthMechanisms) - }) - - // Case 2: only SHA-256 - auth = automationconfig.Auth{} - opts = authtypes.Options{ - AuthoritativeSet: false, - KeyFile: constants.AutomationAgentKeyFilePathInContainer, - AuthMechanisms: []string{constants.Sha256}, - AgentName: "mms-automation", - AutoAuthMechanism: constants.Sha256, - } - err = configureInAutomationConfig(&auth, "password", "keyfilecontents", []automationconfig.MongoDBUser{}, opts) - assert.NoError(t, err) - - t.Run("Authentication is correctly configured", func(t *testing.T) { - assert.Equal(t, constants.Sha256, auth.AutoAuthMechanism) - assert.Len(t, auth.DeploymentAuthMechanisms, 1) - assert.Len(t, auth.AutoAuthMechanisms, 1) - assert.Equal(t, []string{constants.Sha256}, auth.DeploymentAuthMechanisms) - assert.Equal(t, []string{constants.Sha256}, auth.AutoAuthMechanisms) - assert.Equal(t, constants.AutomationAgentKeyFilePathInContainer, auth.KeyFile) - assert.Equal(t, constants.AutomationAgentWindowsKeyFilePath, auth.KeyFileWindows) - }) - t.Run("Subsequent configuration doesn't add to deployment auth mechanisms", func(t *testing.T) { - err := configureInAutomationConfig(&auth, "password", "keyfilecontents", []automationconfig.MongoDBUser{}, opts) - assert.NoError(t, err) - assert.Equal(t, []string{constants.Sha256}, auth.DeploymentAuthMechanisms) - }) - - // Case 1: only SHA-1 - auth = automationconfig.Auth{} - opts = authtypes.Options{ - AuthoritativeSet: false, - KeyFile: constants.AutomationAgentKeyFilePathInContainer, - AuthMechanisms: []string{constants.Sha1}, - AgentName: "mms-automation", - AutoAuthMechanism: constants.Sha1, - } - err = configureInAutomationConfig(&auth, "password", "keyfilecontents", []automationconfig.MongoDBUser{}, opts) - assert.NoError(t, err) - - t.Run("Authentication is correctly configured", func(t *testing.T) { - assert.Equal(t, constants.Sha1, auth.AutoAuthMechanism) - assert.Len(t, auth.DeploymentAuthMechanisms, 1) - assert.Len(t, auth.AutoAuthMechanisms, 1) - assert.Equal(t, []string{constants.Sha1}, auth.DeploymentAuthMechanisms) - assert.Equal(t, []string{constants.Sha1}, auth.AutoAuthMechanisms) - assert.Equal(t, constants.AutomationAgentKeyFilePathInContainer, auth.KeyFile) - assert.Equal(t, constants.AutomationAgentWindowsKeyFilePath, auth.KeyFileWindows) - }) - t.Run("Subsequent configuration doesn't add to deployment auth mechanisms", func(t *testing.T) { - err := configureInAutomationConfig(&auth, "password", "keyfilecontents", []automationconfig.MongoDBUser{}, opts) - assert.NoError(t, err) - assert.Equal(t, []string{constants.Sha1}, auth.DeploymentAuthMechanisms) - }) -} - -// configureInAutomationConfig updates the provided auth struct and fully configures Scram authentication. -func configureInAutomationConfig(auth *automationconfig.Auth, agentPassword, agentKeyFile string, users []automationconfig.MongoDBUser, opts authtypes.Options) error { - err := enableAgentAuthentication(auth, agentPassword, agentKeyFile, opts) - if err != nil { - return err - } - err = enableClientAuthentication(auth, opts, users) - if err != nil { - return err - } - return nil -} diff --git a/mongodb-community-operator/pkg/authentication/scram/scram_test.go b/mongodb-community-operator/pkg/authentication/scram/scram_test.go deleted file mode 100644 index b888cc39d..000000000 --- a/mongodb-community-operator/pkg/authentication/scram/scram_test.go +++ /dev/null @@ -1,319 +0,0 @@ -package scram - -import ( - "context" - "os" - "reflect" - "testing" - - "github.com/stretchr/testify/assert" - "go.uber.org/zap" - "k8s.io/apimachinery/pkg/types" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/authentication/authtypes" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/authentication/mocks" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/authentication/scramcredentials" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/automationconfig" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/secret" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/util/constants" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/util/generate" -) - -func init() { - logger, err := zap.NewDevelopment() - if err != nil { - os.Exit(1) - } - zap.ReplaceGlobals(logger) -} - -const ( - testSha1Salt = "zEt5uDSnr/l9paFPsQzhAA==" - testSha1ServerKey = "LEm/fv4gM0Y/XizbUoz/hULRnX0=" - testSha1StoredKey = "0HzXK7NtK40HXVn6zOqrNKVl+MY=" - - testSha256Salt = "qRr+7VgicfVcFjwZhu8u5JSE5ZeVBUP1A+lM4A==" - testSha256ServerKey = "C9FIUhP6mqwe/2SJIheGBpOIqlxuq9Nh3fs+t+R/3zk=" - testSha256StoredKey = "7M7dUSY0sHTOXdNnoPSVbXg9Flon1b3t8MINGI8Tst0=" -) - -func TestReadExistingCredentials(t *testing.T) { - ctx := context.Background() - mdbObjectKey := types.NamespacedName{Name: "mdb-0", Namespace: "default"} - user := mocks.BuildScramMongoDBUser("mdbuser-0") - t.Run("credentials are successfully generated when all fields are present", func(t *testing.T) { - scramCredsSecret := validScramCredentialsSecret(mdbObjectKey, user.ScramCredentialsSecretName) - scram1Creds, scram256Creds, err := readExistingCredentials(ctx, mocks.NewMockedSecretGetUpdateCreateDeleter(scramCredsSecret), mdbObjectKey, user.ScramCredentialsSecretName) - assert.NoError(t, err) - assertScramCredsCredentialsValidity(t, scram1Creds, scram256Creds) - }) - t.Run("credentials are not generated if a field is missing", func(t *testing.T) { - scramCredsSecret := invalidSecret(mdbObjectKey, user.ScramCredentialsSecretName) - _, _, err := readExistingCredentials(ctx, mocks.NewMockedSecretGetUpdateCreateDeleter(scramCredsSecret), mdbObjectKey, user.ScramCredentialsSecretName) - assert.Error(t, err) - }) - - t.Run("credentials are not generated if the secret does not exist", func(t *testing.T) { - scramCredsSecret := validScramCredentialsSecret(mdbObjectKey, user.ScramCredentialsSecretName) - _, _, err := readExistingCredentials(ctx, mocks.NewMockedSecretGetUpdateCreateDeleter(scramCredsSecret), mdbObjectKey, "different-username") - assert.Error(t, err) - }) -} - -func TestComputeScramCredentials_ComputesSameStoredAndServerKey_WithSameSalt(t *testing.T) { - sha1Salt, sha256SaltKey, err := generate.Salts() - assert.NoError(t, err) - - username := "user-1" - password := "X6oSVAfD1la8fJwhfN" // nolint - - for i := 0; i < 10; i++ { - sha1Creds0, sha256Creds0, err := computeScramShaCredentials(username, password, sha1Salt, sha256SaltKey) - assert.NoError(t, err) - sha1Creds1, sha256Creds1, err := computeScramShaCredentials(username, password, sha1Salt, sha256SaltKey) - assert.NoError(t, err) - - assert.True(t, reflect.DeepEqual(sha1Creds0, sha1Creds1)) - assert.True(t, reflect.DeepEqual(sha256Creds0, sha256Creds1)) - } -} - -func TestEnsureScramCredentials(t *testing.T) { - ctx := context.Background() - mdb, user := buildConfigurableAndUser("mdb-0") - t.Run("Fails when there is no password secret, and no credentials secret", func(t *testing.T) { - _, _, err := ensureScramCredentials(ctx, mocks.NewMockedSecretGetUpdateCreateDeleter(), user, mdb.NamespacedName(), nil) - assert.Error(t, err) - }) - t.Run("Existing credentials are used when password does not exist, but credentials secret has been created", func(t *testing.T) { - scramCredentialsSecret := validScramCredentialsSecret(mdb.NamespacedName(), user.ScramCredentialsSecretName) - scram1Creds, scram256Creds, err := ensureScramCredentials(ctx, mocks.NewMockedSecretGetUpdateCreateDeleter(scramCredentialsSecret), user, mdb.NamespacedName(), nil) - assert.NoError(t, err) - assertScramCredsCredentialsValidity(t, scram1Creds, scram256Creds) - }) - t.Run("Changing password results in different credentials being returned", func(t *testing.T) { - newPassword, err := generate.RandomFixedLengthStringOfSize(20) - assert.NoError(t, err) - - differentPasswordSecret := secret.Builder(). - SetName(user.PasswordSecretName). - SetNamespace(mdb.NamespacedName().Namespace). - SetField(user.PasswordSecretKey, newPassword). - Build() - - scramCredentialsSecret := validScramCredentialsSecret(mdb.NamespacedName(), user.ScramCredentialsSecretName) - scram1Creds, scram256Creds, err := ensureScramCredentials(ctx, mocks.NewMockedSecretGetUpdateCreateDeleter(scramCredentialsSecret, differentPasswordSecret), user, mdb.NamespacedName(), nil) - assert.NoError(t, err) - assert.NotEqual(t, testSha1Salt, scram1Creds.Salt) - assert.NotEmpty(t, scram1Creds.Salt) - assert.NotEqual(t, testSha1StoredKey, scram1Creds.StoredKey) - assert.NotEmpty(t, scram1Creds.StoredKey) - assert.NotEqual(t, testSha1StoredKey, scram1Creds.ServerKey) - assert.NotEmpty(t, scram1Creds.ServerKey) - assert.Equal(t, 10000, scram1Creds.IterationCount) - - assert.NotEqual(t, testSha256Salt, scram256Creds.Salt) - assert.NotEmpty(t, scram256Creds.Salt) - assert.NotEqual(t, testSha256StoredKey, scram256Creds.StoredKey) - assert.NotEmpty(t, scram256Creds.StoredKey) - assert.NotEqual(t, testSha256ServerKey, scram256Creds.ServerKey) - assert.NotEmpty(t, scram256Creds.ServerKey) - assert.Equal(t, 15000, scram256Creds.IterationCount) - }) -} - -func TestConvertMongoDBUserToAutomationConfigUser(t *testing.T) { - ctx := context.Background() - mdb, user := buildConfigurableAndUser("mdb-0") - - t.Run("When password exists, the user is created in the automation config", func(t *testing.T) { - passwordSecret := secret.Builder(). - SetName(user.PasswordSecretName). - SetNamespace(mdb.NamespacedName().Namespace). - SetField(user.PasswordSecretKey, "TDg_DESiScDrJV6"). - Build() - - acUser, err := convertMongoDBUserToAutomationConfigUser(ctx, mocks.NewMockedSecretGetUpdateCreateDeleter(passwordSecret), mdb.NamespacedName(), nil, user) - - assert.NoError(t, err) - assert.Equal(t, user.Username, acUser.Username) - assert.Equal(t, user.Database, "admin") - assert.Equal(t, len(user.Roles), len(acUser.Roles)) - assert.NotNil(t, acUser.ScramSha1Creds) - assert.NotNil(t, acUser.ScramSha256Creds) - for i, acRole := range acUser.Roles { - assert.Equal(t, user.Roles[i].Name, acRole.Role) - assert.Equal(t, user.Roles[i].Database, acRole.Database) - } - }) - - t.Run("If there is no password secret, the creation fails", func(t *testing.T) { - _, err := convertMongoDBUserToAutomationConfigUser(ctx, mocks.NewMockedSecretGetUpdateCreateDeleter(), mdb.NamespacedName(), nil, user) - assert.Error(t, err) - }) -} - -func TestConfigureScram(t *testing.T) { - ctx := context.Background() - t.Run("Should fail if there is no password present for the user", func(t *testing.T) { - mdb, _ := buildConfigurableAndUser("mdb-0") - s := mocks.NewMockedSecretGetUpdateCreateDeleter() - - auth := automationconfig.Auth{} - err := Enable(ctx, &auth, s, mdb) - assert.Error(t, err) - }) - - t.Run("Agent Credentials Secret should be created if there are no users", func(t *testing.T) { - mdb := buildConfigurable("mdb-0") - s := mocks.NewMockedSecretGetUpdateCreateDeleter() - auth := automationconfig.Auth{} - err := Enable(ctx, &auth, s, mdb) - assert.NoError(t, err) - - passwordSecret, err := s.GetSecret(ctx, mdb.GetAgentPasswordSecretNamespacedName()) - assert.NoError(t, err) - assert.True(t, secret.HasAllKeys(passwordSecret, constants.AgentPasswordKey)) - assert.NotEmpty(t, passwordSecret.Data[constants.AgentPasswordKey]) - - keyfileSecret, err := s.GetSecret(ctx, mdb.GetAgentKeyfileSecretNamespacedName()) - assert.NoError(t, err) - assert.True(t, secret.HasAllKeys(keyfileSecret, constants.AgentKeyfileKey)) - assert.NotEmpty(t, keyfileSecret.Data[constants.AgentKeyfileKey]) - }) - - t.Run("Agent Credentials Secret should contain owner reference", func(t *testing.T) { - mdb := buildConfigurable("mdb-0") - s := mocks.NewMockedSecretGetUpdateCreateDeleter() - auth := automationconfig.Auth{} - err := Enable(ctx, &auth, s, mdb) - assert.NoError(t, err) - - passwordSecret, err := s.GetSecret(ctx, mdb.GetAgentPasswordSecretNamespacedName()) - assert.NoError(t, err) - - actualRef := passwordSecret.GetOwnerReferences() - expectedRef := []metav1.OwnerReference{{ - APIVersion: "v1", - Kind: "mdbc", - Name: "my-ref", - }} - assert.Equal(t, expectedRef, actualRef) - }) - - t.Run("Agent Password Secret is used if it exists", func(t *testing.T) { - mdb := buildConfigurable("mdb-0") - - agentPasswordSecret := secret.Builder(). - SetName(mdb.GetAgentPasswordSecretNamespacedName().Name). - SetNamespace(mdb.GetAgentPasswordSecretNamespacedName().Namespace). - SetField(constants.AgentPasswordKey, "A21Zv5agv3EKXFfM"). - Build() - - s := mocks.NewMockedSecretGetUpdateCreateDeleter(agentPasswordSecret) - auth := automationconfig.Auth{} - err := Enable(ctx, &auth, s, mdb) - assert.NoError(t, err) - - ps, err := s.GetSecret(ctx, mdb.GetAgentPasswordSecretNamespacedName()) - assert.NoError(t, err) - assert.True(t, secret.HasAllKeys(ps, constants.AgentPasswordKey)) - assert.NotEmpty(t, ps.Data[constants.AgentPasswordKey]) - assert.Equal(t, "A21Zv5agv3EKXFfM", string(ps.Data[constants.AgentPasswordKey])) - }) - - t.Run("Agent Keyfile Secret is used if present", func(t *testing.T) { - mdb := buildConfigurable("mdb-0") - - keyfileSecret := secret.Builder(). - SetName(mdb.GetAgentKeyfileSecretNamespacedName().Name). - SetNamespace(mdb.GetAgentKeyfileSecretNamespacedName().Namespace). - SetField(constants.AgentKeyfileKey, "RuPeMaIe2g0SNTTa"). - Build() - - s := mocks.NewMockedSecretGetUpdateCreateDeleter(keyfileSecret) - auth := automationconfig.Auth{} - err := Enable(ctx, &auth, s, mdb) - assert.NoError(t, err) - - ks, err := s.GetSecret(ctx, mdb.GetAgentKeyfileSecretNamespacedName()) - assert.NoError(t, err) - assert.True(t, secret.HasAllKeys(ks, constants.AgentKeyfileKey)) - assert.Equal(t, "RuPeMaIe2g0SNTTa", string(ks.Data[constants.AgentKeyfileKey])) - }) - - t.Run("Agent Credentials Secret should be created", func(t *testing.T) { - mdb := buildConfigurable("mdb-0") - s := mocks.NewMockedSecretGetUpdateCreateDeleter() - auth := automationconfig.Auth{} - err := Enable(ctx, &auth, s, mdb) - assert.NoError(t, err) - }) -} - -func buildConfigurable(name string, users ...authtypes.User) authtypes.Configurable { - return mocks.NewMockConfigurable( - authtypes.Options{ - AuthoritativeSet: false, - KeyFile: "/path/to/keyfile", - AuthMechanisms: []string{constants.Sha256}, - AgentName: constants.AgentName, - AutoAuthMechanism: constants.Sha256, - }, - users, - types.NamespacedName{ - Name: name, - Namespace: "default", - }, - []metav1.OwnerReference{{ - APIVersion: "v1", - Kind: "mdbc", - Name: "my-ref", - }}, - ) -} - -func buildConfigurableAndUser(name string) (authtypes.Configurable, authtypes.User) { - mdb := buildConfigurable(name, mocks.BuildScramMongoDBUser(name)) - return mdb, mdb.GetAuthUsers()[0] -} - -func assertScramCredsCredentialsValidity(t *testing.T, scram1Creds, scram256Creds scramcredentials.ScramCreds) { - assert.Equal(t, testSha1Salt, scram1Creds.Salt) - assert.Equal(t, testSha1StoredKey, scram1Creds.StoredKey) - assert.Equal(t, testSha1ServerKey, scram1Creds.ServerKey) - assert.Equal(t, 10000, scram1Creds.IterationCount) - - assert.Equal(t, testSha256Salt, scram256Creds.Salt) - assert.Equal(t, testSha256StoredKey, scram256Creds.StoredKey) - assert.Equal(t, testSha256ServerKey, scram256Creds.ServerKey) - assert.Equal(t, 15000, scram256Creds.IterationCount) -} - -// validScramCredentialsSecret returns a secret that has all valid scram credentials -func validScramCredentialsSecret(objectKey types.NamespacedName, scramCredentialsSecretName string) corev1.Secret { - return secret.Builder(). - SetName(scramCredentialsSecretName). - SetNamespace(objectKey.Namespace). - SetField(sha1SaltKey, testSha1Salt). - SetField(sha1StoredKeyKey, testSha1StoredKey). - SetField(sha1ServerKeyKey, testSha1ServerKey). - SetField(sha256SaltKey, testSha256Salt). - SetField(sha256StoredKeyKey, testSha256StoredKey). - SetField(sha256ServerKeyKey, testSha256ServerKey). - Build() -} - -// invalidSecret returns a secret that is incomplete -func invalidSecret(objectKey types.NamespacedName, scramCredentialsSecretName string) corev1.Secret { - return secret.Builder(). - SetName(scramCredentialsSecretName). - SetNamespace(objectKey.Namespace). - SetField(sha1SaltKey, "nxBSYyZZIBZxStyt"). - SetField(sha1StoredKeyKey, "Bs4sePK0cdMy6n"). - SetField(sha1ServerKeyKey, "eP6_p76ql_h8iiH"). - Build() -} diff --git a/mongodb-community-operator/pkg/authentication/x509/x509.go b/mongodb-community-operator/pkg/authentication/x509/x509.go index 719096492..839f83c2b 100644 --- a/mongodb-community-operator/pkg/authentication/x509/x509.go +++ b/mongodb-community-operator/pkg/authentication/x509/x509.go @@ -15,7 +15,7 @@ import ( "regexp" "time" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/authentication/authtypes" diff --git a/mongodb-community-operator/pkg/authentication/x509/x509_enabler_test.go b/mongodb-community-operator/pkg/authentication/x509/x509_enabler_test.go deleted file mode 100644 index 0be4bc4f3..000000000 --- a/mongodb-community-operator/pkg/authentication/x509/x509_enabler_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package x509 - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/authentication/authtypes" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/automationconfig" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/util/constants" -) - -func TestX509AutomationConfig(t *testing.T) { - t.Run("Only X509", func(t *testing.T) { - auth := automationconfig.Auth{} - opts := authtypes.Options{ - AuthoritativeSet: false, - KeyFile: constants.AutomationAgentKeyFilePathInContainer, - AuthMechanisms: []string{constants.X509}, - AutoAuthMechanism: constants.X509, - } - err := configureInAutomationConfig(&auth, "keyfilecontents", "CN=my-agent,O=MongoDB", []automationconfig.MongoDBUser{}, opts) - assert.NoError(t, err) - - t.Run("Authentication is correctly configured", func(t *testing.T) { - assert.Equal(t, "CN=my-agent,O=MongoDB", auth.AutoUser) - assert.Equal(t, "keyfilecontents", auth.Key) - assert.Equal(t, "", auth.AutoPwd) - assert.Equal(t, constants.X509, auth.AutoAuthMechanism) - assert.Len(t, auth.DeploymentAuthMechanisms, 1) - assert.Len(t, auth.AutoAuthMechanisms, 1) - assert.Equal(t, []string{constants.X509}, auth.DeploymentAuthMechanisms) - assert.Equal(t, []string{constants.X509}, auth.AutoAuthMechanisms) - assert.Equal(t, constants.AutomationAgentKeyFilePathInContainer, auth.KeyFile) - assert.Equal(t, constants.AutomationAgentWindowsKeyFilePath, auth.KeyFileWindows) - }) - t.Run("Subsequent configuration doesn't add to deployment auth mechanisms", func(t *testing.T) { - err := configureInAutomationConfig(&auth, "keyfilecontents", "CN=my-agent,O=MongoDB", []automationconfig.MongoDBUser{}, opts) - assert.NoError(t, err) - assert.Equal(t, []string{constants.X509}, auth.DeploymentAuthMechanisms) - }) - }) - - t.Run("X509 and SHA-256", func(t *testing.T) { - auth := automationconfig.Auth{} - opts := authtypes.Options{ - AuthoritativeSet: false, - KeyFile: constants.AutomationAgentKeyFilePathInContainer, - AuthMechanisms: []string{constants.X509, constants.Sha256}, - AutoAuthMechanism: constants.X509, - } - err := configureInAutomationConfig(&auth, "keyfilecontents", "CN=my-agent,O=MongoDB", []automationconfig.MongoDBUser{}, opts) - assert.NoError(t, err) - - t.Run("Authentication is correctly configured", func(t *testing.T) { - assert.Equal(t, "CN=my-agent,O=MongoDB", auth.AutoUser) - assert.Equal(t, "keyfilecontents", auth.Key) - assert.Equal(t, "", auth.AutoPwd) - assert.Equal(t, constants.X509, auth.AutoAuthMechanism) - assert.Len(t, auth.DeploymentAuthMechanisms, 1) - assert.Len(t, auth.AutoAuthMechanisms, 1) - assert.Equal(t, []string{constants.X509}, auth.DeploymentAuthMechanisms) - assert.Equal(t, []string{constants.X509}, auth.AutoAuthMechanisms) - assert.Equal(t, constants.AutomationAgentKeyFilePathInContainer, auth.KeyFile) - assert.Equal(t, constants.AutomationAgentWindowsKeyFilePath, auth.KeyFileWindows) - }) - t.Run("Subsequent configuration doesn't add to deployment auth mechanisms", func(t *testing.T) { - err := configureInAutomationConfig(&auth, "keyfilecontents", "CN=my-agent,O=MongoDB", []automationconfig.MongoDBUser{}, opts) - assert.NoError(t, err) - assert.Equal(t, []string{constants.X509}, auth.DeploymentAuthMechanisms) - }) - }) - - t.Run("Fail validation", func(t *testing.T) { - auth := automationconfig.Auth{} - opts := authtypes.Options{ - AuthoritativeSet: false, - KeyFile: constants.AutomationAgentKeyFilePathInContainer, - AuthMechanisms: []string{}, - AutoAuthMechanism: constants.X509, - } - err := configureInAutomationConfig(&auth, "keyfilecontents", "CN=my-agent,O=MongoDB", []automationconfig.MongoDBUser{}, opts) - assert.Error(t, err) - - auth = automationconfig.Auth{} - opts = authtypes.Options{ - AuthoritativeSet: false, - KeyFile: constants.AutomationAgentKeyFilePathInContainer, - AuthMechanisms: []string{constants.X509}, - AutoAuthMechanism: "", - } - err = configureInAutomationConfig(&auth, "keyfilecontents", "CN=my-agent,O=MongoDB", []automationconfig.MongoDBUser{}, opts) - assert.Error(t, err) - }) -} - -// configureInAutomationConfig updates the provided auth struct and fully configures Scram authentication. -func configureInAutomationConfig(auth *automationconfig.Auth, agentKeyFile, agentName string, users []automationconfig.MongoDBUser, opts authtypes.Options) error { - err := enableAgentAuthentication(auth, agentKeyFile, agentName, opts) - if err != nil { - return err - } - err = enableClientAuthentication(auth, opts, users) - if err != nil { - return err - } - return nil -} diff --git a/mongodb-community-operator/pkg/authentication/x509/x509_test.go b/mongodb-community-operator/pkg/authentication/x509/x509_test.go deleted file mode 100644 index 390b47ae7..000000000 --- a/mongodb-community-operator/pkg/authentication/x509/x509_test.go +++ /dev/null @@ -1,273 +0,0 @@ -package x509 - -import ( - "context" - "reflect" - "testing" - - "github.com/stretchr/testify/assert" - "k8s.io/apimachinery/pkg/types" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/authentication/authtypes" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/authentication/mocks" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/automationconfig" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/secret" - "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/util/constants" -) - -func TestEnable(t *testing.T) { - ctx := context.Background() - t.Run("X509 agent", func(t *testing.T) { - auth := automationconfig.Auth{} - mdb := buildX509Configurable("mdb", mocks.BuildX509MongoDBUser("my-user"), mocks.BuildScramMongoDBUser("my-scram-user")) - - agentSecret := CreateAgentCertificateSecret("tls.crt", false, mdb.AgentCertificateSecretNamespacedName()) - keyfileSecret := secret.Builder(). - SetName(mdb.GetAgentKeyfileSecretNamespacedName().Name). - SetNamespace(mdb.GetAgentKeyfileSecretNamespacedName().Namespace). - SetField(constants.AgentKeyfileKey, "RuPeMaIe2g0SNTTa"). - Build() - secrets := mocks.NewMockedSecretGetUpdateCreateDeleter(agentSecret, keyfileSecret) - - err := Enable(ctx, &auth, secrets, mdb, mdb.AgentCertificateSecretNamespacedName()) - assert.NoError(t, err) - - expected := automationconfig.Auth{ - Users: []automationconfig.MongoDBUser{ - { - Mechanisms: []string{}, - Roles: []automationconfig.Role{ - { - Role: "readWrite", - Database: "admin", - }, - { - Role: "clusterAdmin", - Database: "admin", - }, - }, - Username: "CN=my-user,OU=organizationalunit,O=organization", - Database: "$external", - AuthenticationRestrictions: []string{}, - }, - }, - Disabled: false, - AuthoritativeSet: false, - AutoAuthMechanisms: []string{constants.X509}, - AutoAuthMechanism: constants.X509, - DeploymentAuthMechanisms: []string{constants.X509}, - AutoUser: "CN=mms-automation-agent,OU=ENG,O=MongoDB,C=US", - Key: "RuPeMaIe2g0SNTTa", - KeyFile: "/path/to/keyfile", - KeyFileWindows: constants.AutomationAgentWindowsKeyFilePath, - AutoPwd: "", - } - - assert.Equal(t, expected, auth) - }) - t.Run("SCRAM agent", func(t *testing.T) { - auth := automationconfig.Auth{} - mdb := buildScramConfigurable("mdb", mocks.BuildX509MongoDBUser("my-user"), mocks.BuildScramMongoDBUser("my-scram-user")) - - secrets := mocks.NewMockedSecretGetUpdateCreateDeleter() - - err := Enable(ctx, &auth, secrets, mdb, mdb.AgentCertificateSecretNamespacedName()) - assert.NoError(t, err) - - expected := automationconfig.Auth{ - Users: []automationconfig.MongoDBUser{{ - Mechanisms: []string{}, - Roles: []automationconfig.Role{ - { - Role: "readWrite", - Database: "admin", - }, - { - Role: "clusterAdmin", - Database: "admin", - }, - }, - Username: "CN=my-user,OU=organizationalunit,O=organization", - Database: "$external", - AuthenticationRestrictions: []string{}, - }}, - Disabled: false, - AuthoritativeSet: false, - DeploymentAuthMechanisms: []string{constants.X509}, - } - - assert.Equal(t, expected, auth) - }) -} - -func Test_ensureAgent(t *testing.T) { - ctx := context.Background() - auth := automationconfig.Auth{} - mdb := buildX509Configurable("mdb") - secrets := mocks.NewMockedSecretGetUpdateCreateDeleter() - - err := ensureAgent(ctx, &auth, secrets, mdb, mdb.AgentCertificateSecretNamespacedName()) - assert.Error(t, err) - - auth = automationconfig.Auth{} - agentSecret := CreateAgentCertificateSecret("tls.pem", false, mdb.AgentCertificateSecretNamespacedName()) - secrets = mocks.NewMockedSecretGetUpdateCreateDeleter(agentSecret) - - err = ensureAgent(ctx, &auth, secrets, mdb, mdb.AgentCertificateSecretNamespacedName()) - assert.Error(t, err) - assert.ErrorContains(t, err, "key \"tls.crt\" not present in the Secret") - - auth = automationconfig.Auth{} - agentSecret = CreateAgentCertificateSecret("tls.crt", true, mdb.AgentCertificateSecretNamespacedName()) - secrets = mocks.NewMockedSecretGetUpdateCreateDeleter(agentSecret) - - err = ensureAgent(ctx, &auth, secrets, mdb, mdb.AgentCertificateSecretNamespacedName()) - assert.Error(t, err) - assert.ErrorContains(t, err, "x509: malformed certificate") - - auth = automationconfig.Auth{} - agentSecret = CreateAgentCertificateSecret("tls.crt", false, mdb.AgentCertificateSecretNamespacedName()) - secrets = mocks.NewMockedSecretGetUpdateCreateDeleter(agentSecret) - - err = ensureAgent(ctx, &auth, secrets, mdb, mdb.AgentCertificateSecretNamespacedName()) - assert.NoError(t, err) -} - -func Test_convertMongoDBResourceUsersToAutomationConfigUsers(t *testing.T) { - type args struct { - mdb authtypes.Configurable - } - tests := []struct { - name string - args args - want []automationconfig.MongoDBUser - }{ - { - name: "Only x.509 users", - args: args{mdb: buildX509Configurable("mongodb", mocks.BuildX509MongoDBUser("my-user-1"), mocks.BuildX509MongoDBUser("my-user-2"))}, - want: []automationconfig.MongoDBUser{ - { - Mechanisms: []string{}, - Roles: []automationconfig.Role{ - { - Role: "readWrite", - Database: "admin", - }, - { - Role: "clusterAdmin", - Database: "admin", - }, - }, - Username: "CN=my-user-1,OU=organizationalunit,O=organization", - Database: "$external", - AuthenticationRestrictions: []string{}, - }, - { - Mechanisms: []string{}, - Roles: []automationconfig.Role{ - { - Role: "readWrite", - Database: "admin", - }, - { - Role: "clusterAdmin", - Database: "admin", - }, - }, - Username: "CN=my-user-2,OU=organizationalunit,O=organization", - Database: "$external", - AuthenticationRestrictions: []string{}, - }, - }, - }, - { - name: "Only SCRAM users", - args: args{mdb: buildX509Configurable("mongodb", mocks.BuildScramMongoDBUser("my-user-1"), mocks.BuildScramMongoDBUser("my-user-2"))}, - want: nil, - }, - { - name: "X.509 and SCRAM users", - args: args{mdb: buildX509Configurable("mongodb", mocks.BuildX509MongoDBUser("my-user-1"), mocks.BuildScramMongoDBUser("my-user-2"))}, - want: []automationconfig.MongoDBUser{ - { - Mechanisms: []string{}, - Roles: []automationconfig.Role{ - { - Role: "readWrite", - Database: "admin", - }, - { - Role: "clusterAdmin", - Database: "admin", - }, - }, - Username: "CN=my-user-1,OU=organizationalunit,O=organization", - Database: "$external", - AuthenticationRestrictions: []string{}, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := convertMongoDBResourceUsersToAutomationConfigUsers(tt.args.mdb) - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("convertMongoDBResourceUsersToAutomationConfigUsers() got = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_readAgentSubjectsFromCert(t *testing.T) { - agentCert, _, _ := CreateAgentCertificate() - - subjectName, err := readAgentSubjectsFromCert(agentCert) - assert.NoError(t, err) - - assert.Equal(t, "CN=mms-automation-agent,OU=ENG,O=MongoDB,C=US", subjectName) -} - -func buildX509Configurable(name string, users ...authtypes.User) mocks.MockConfigurable { - return mocks.NewMockConfigurable( - authtypes.Options{ - AuthoritativeSet: false, - KeyFile: "/path/to/keyfile", - AuthMechanisms: []string{constants.X509}, - AutoAuthMechanism: constants.X509, - }, - users, - types.NamespacedName{ - Name: name, - Namespace: "default", - }, - []metav1.OwnerReference{{ - APIVersion: "v1", - Kind: "mdbc", - Name: "my-ref", - }}, - ) -} - -func buildScramConfigurable(name string, users ...authtypes.User) mocks.MockConfigurable { - return mocks.NewMockConfigurable( - authtypes.Options{ - AuthoritativeSet: false, - KeyFile: "/path/to/keyfile", - AuthMechanisms: []string{constants.Sha256, constants.X509}, - AgentName: constants.AgentName, - AutoAuthMechanism: constants.Sha256, - }, - users, - types.NamespacedName{ - Name: name, - Namespace: "default", - }, - []metav1.OwnerReference{{ - APIVersion: "v1", - Kind: "mdbc", - Name: "my-ref", - }}, - ) -}