diff --git a/e2e-tests/disabled-auth/compare/find-after-insert-auth.json b/e2e-tests/disabled-auth/compare/find-after-insert-auth.json new file mode 100644 index 0000000000..b9f196193c --- /dev/null +++ b/e2e-tests/disabled-auth/compare/find-after-insert-auth.json @@ -0,0 +1,5 @@ +switched to db myApp +{ "_id" : , "x" : 100500 } +{ "_id" : , "x" : 200500 } +{ "_id" : , "x" : 200501 } +bye diff --git a/e2e-tests/disabled-auth/compare/find-after-insert.json b/e2e-tests/disabled-auth/compare/find-after-insert.json new file mode 100644 index 0000000000..d0a0868e32 --- /dev/null +++ b/e2e-tests/disabled-auth/compare/find-after-insert.json @@ -0,0 +1,4 @@ +switched to db myApp +{ "_id" : , "x" : 100500 } +{ "_id" : , "x" : 100501 } +bye diff --git a/e2e-tests/disabled-auth/compare/find-after-restore-auth.json b/e2e-tests/disabled-auth/compare/find-after-restore-auth.json new file mode 100644 index 0000000000..051f527204 --- /dev/null +++ b/e2e-tests/disabled-auth/compare/find-after-restore-auth.json @@ -0,0 +1,4 @@ +switched to db myApp +{ "_id" : , "x" : 100500 } +{ "_id" : , "x" : 200500 } +bye diff --git a/e2e-tests/disabled-auth/compare/find-after-restore.json b/e2e-tests/disabled-auth/compare/find-after-restore.json new file mode 100644 index 0000000000..74495091bf --- /dev/null +++ b/e2e-tests/disabled-auth/compare/find-after-restore.json @@ -0,0 +1,3 @@ +switched to db myApp +{ "_id" : , "x" : 100500 } +bye diff --git a/e2e-tests/disabled-auth/compare/find-with-auth.json b/e2e-tests/disabled-auth/compare/find-with-auth.json new file mode 100644 index 0000000000..74495091bf --- /dev/null +++ b/e2e-tests/disabled-auth/compare/find-with-auth.json @@ -0,0 +1,3 @@ +switched to db myApp +{ "_id" : , "x" : 100500 } +bye diff --git a/e2e-tests/disabled-auth/conf/backup-minio.yml b/e2e-tests/disabled-auth/conf/backup-minio.yml new file mode 100644 index 0000000000..42dfbf7ae0 --- /dev/null +++ b/e2e-tests/disabled-auth/conf/backup-minio.yml @@ -0,0 +1,9 @@ +apiVersion: psmdb.percona.com/v1 +kind: PerconaServerMongoDBBackup +metadata: + finalizers: + - percona.com/delete-backup + name: backup-minio +spec: + clusterName: some-name + storageName: minio diff --git a/e2e-tests/disabled-auth/conf/cluster-no-auth.yml b/e2e-tests/disabled-auth/conf/cluster-no-auth.yml new file mode 100644 index 0000000000..6c2450ef41 --- /dev/null +++ b/e2e-tests/disabled-auth/conf/cluster-no-auth.yml @@ -0,0 +1,129 @@ +apiVersion: psmdb.percona.com/v1 +kind: PerconaServerMongoDB +metadata: + name: some-name +spec: + image: + imagePullPolicy: Always + updateStrategy: SmartUpdate + secrets: + users: some-users + tls: + mode: disabled + unsafeFlags: + tls: true + backup: + enabled: true + image: perconalab/percona-server-mongodb-operator:1.1.0-backup + storages: + minio: + type: s3 + s3: + credentialsSecret: minio-secret + region: us-east-1 + bucket: operator-testing + endpointUrl: http://minio-service:9000/ + insecureSkipTLSVerify: false + sharding: + enabled: true + configsvrReplSet: + size: 3 + affinity: + antiAffinityTopologyKey: none + configuration: | + net: + port: 27018 + security: + authorization: disabled + volumeSpec: + persistentVolumeClaim: + resources: + requests: + storage: 3Gi + expose: + enabled: true + type: ClusterIP + + mongos: + size: 3 + affinity: + antiAffinityTopologyKey: none + configuration: | + net: + port: 27019 + expose: + type: ClusterIP + + replsets: + - name: rs0 + affinity: + antiAffinityTopologyKey: none + expose: + enabled: true + type: ClusterIP + resources: + limits: + cpu: 500m + memory: 1G + requests: + cpu: 100m + memory: 0.1G + volumeSpec: + persistentVolumeClaim: + resources: + requests: + storage: 1Gi + size: 3 + configuration: | + net: + port: 27016 + security: + authorization: disabled + - name: rs1 + affinity: + antiAffinityTopologyKey: none + expose: + enabled: true + type: ClusterIP + resources: + limits: + cpu: 500m + memory: 1G + requests: + cpu: 100m + memory: 0.1G + volumeSpec: + persistentVolumeClaim: + resources: + requests: + storage: 1Gi + size: 3 + configuration: | + net: + port: 27016 + security: + authorization: disabled + - name: rs2 + affinity: + antiAffinityTopologyKey: none + expose: + enabled: true + type: ClusterIP + resources: + limits: + cpu: 500m + memory: 1G + requests: + cpu: 100m + memory: 0.1G + volumeSpec: + persistentVolumeClaim: + resources: + requests: + storage: 1Gi + size: 3 + configuration: | + net: + port: 27016 + security: + authorization: disabled diff --git a/e2e-tests/disabled-auth/conf/cluster-with-auth.yml b/e2e-tests/disabled-auth/conf/cluster-with-auth.yml new file mode 100644 index 0000000000..ed958806e8 --- /dev/null +++ b/e2e-tests/disabled-auth/conf/cluster-with-auth.yml @@ -0,0 +1,120 @@ +apiVersion: psmdb.percona.com/v1 +kind: PerconaServerMongoDB +metadata: + name: some-name +spec: + image: + imagePullPolicy: Always + updateStrategy: SmartUpdate + secrets: + users: some-users + tls: + mode: preferTLS + backup: + enabled: true + image: perconalab/percona-server-mongodb-operator:1.1.0-backup + storages: + minio: + type: s3 + s3: + credentialsSecret: minio-secret + region: us-east-1 + bucket: operator-testing + endpointUrl: http://minio-service:9000/ + insecureSkipTLSVerify: false + sharding: + enabled: true + + configsvrReplSet: + size: 3 + affinity: + antiAffinityTopologyKey: none + configuration: | + net: + port: 27018 + volumeSpec: + persistentVolumeClaim: + resources: + requests: + storage: 3Gi + expose: + enabled: true + type: ClusterIP + + mongos: + size: 3 + affinity: + antiAffinityTopologyKey: none + configuration: | + net: + port: 27019 + expose: + type: ClusterIP + + replsets: + - name: rs0 + affinity: + antiAffinityTopologyKey: none + expose: + enabled: true + type: ClusterIP + resources: + limits: + cpu: 500m + memory: 1G + requests: + cpu: 100m + memory: 0.1G + volumeSpec: + persistentVolumeClaim: + resources: + requests: + storage: 1Gi + size: 3 + configuration: | + net: + port: 27016 + - name: rs1 + affinity: + antiAffinityTopologyKey: none + expose: + enabled: true + type: ClusterIP + resources: + limits: + cpu: 500m + memory: 1G + requests: + cpu: 100m + memory: 0.1G + volumeSpec: + persistentVolumeClaim: + resources: + requests: + storage: 1Gi + size: 3 + configuration: | + net: + port: 27016 + - name: rs2 + affinity: + antiAffinityTopologyKey: none + expose: + enabled: true + type: ClusterIP + resources: + limits: + cpu: 500m + memory: 1G + requests: + cpu: 100m + memory: 0.1G + volumeSpec: + persistentVolumeClaim: + resources: + requests: + storage: 1Gi + size: 3 + configuration: | + net: + port: 27016 diff --git a/e2e-tests/disabled-auth/conf/restore.yml b/e2e-tests/disabled-auth/conf/restore.yml new file mode 100644 index 0000000000..32ab3c4b9a --- /dev/null +++ b/e2e-tests/disabled-auth/conf/restore.yml @@ -0,0 +1,7 @@ +apiVersion: psmdb.percona.com/v1 +kind: PerconaServerMongoDBRestore +metadata: + name: +spec: + clusterName: some-name + backupName: diff --git a/e2e-tests/disabled-auth/run b/e2e-tests/disabled-auth/run new file mode 100755 index 0000000000..0a511abf95 --- /dev/null +++ b/e2e-tests/disabled-auth/run @@ -0,0 +1,123 @@ +#!/bin/bash + +set -o errexit +set -o xtrace + +test_dir=$(realpath "$(dirname "$0")") +. "${test_dir}/../functions" +set_debug + +custom_port='27019' + +create_infra "$namespace" + +deploy_minio + +desc "create PSMDB sharded cluster without auth" +cluster="some-name" +kubectl_bin apply -f "$conf_dir/secrets.yml" +kubectl_bin apply -f "$conf_dir/client.yml" + +apply_s3_storage_secrets + +apply_cluster "$test_dir/conf/cluster-no-auth.yml" + +desc 'wait for all pods to start' +wait_for_running $cluster-rs0 3 +wait_for_running $cluster-rs1 3 +wait_for_running $cluster-rs2 3 +wait_for_running $cluster-cfg 3 "false" +wait_for_running $cluster-mongos 3 + +desc 'write data without auth' +run_mongos \ + 'use myApp\n db.createCollection("test")' \ + "@$cluster-mongos.$namespace" "" "" "" "$custom_port" +run_mongos \ + 'use myApp\n db.test.insert({x: 100500})' \ + "@$cluster-mongos.$namespace" "" "" "" "$custom_port" + +desc 'verify data was written' +run_mongos \ + 'use myApp\n db.test.find()' \ + "@$cluster-mongos.$namespace" "" "" "" "$custom_port" + +desc 'wait for backup agents' +wait_backup_agent $cluster-rs0-0 +wait_backup_agent $cluster-rs1-0 +wait_backup_agent $cluster-rs2-0 + +backup_name_no_auth="backup-no-auth" + +desc 'run backup without auth' +run_backup minio ${backup_name_no_auth} +wait_backup "$backup_name_no_auth" + +desc 'insert new data without auth' +run_mongos \ + 'use myApp\n db.test.insert({x: 100501})' \ + "@$cluster-mongos.$namespace" "" "" "" "$custom_port" + +desc 'verify new data exists' +compare_mongos_cmd "find" "@$cluster-mongos.$namespace" "-after-insert" "" "myApp" "test" "$custom_port" + +desc 'restore from backup (no auth)' +run_restore "$backup_name_no_auth" +wait_restore "$backup_name_no_auth" "$cluster" + +desc 'verify data was restored to original state' +compare_mongos_cmd "find" "@$cluster-mongos.$namespace" "-after-restore" "" "myApp" "test" "$custom_port" + +desc 'enable authentication' +pause_cluster "$cluster" +wait_for_cluster_state "${cluster}" "paused" + +apply_cluster "$test_dir/conf/cluster-with-auth.yml" + +unpause_cluster "$cluster" +wait_for_cluster_state "${cluster}" "ready" + +desc 'create users with auth enabled' +run_mongos \ + 'db.createUser({user:"myApp",pwd:"myPass",roles:[{db:"myApp",role:"readWrite"}]})' \ + "userAdmin:userAdmin123456@$cluster-mongos.$namespace" "" "" "" "$custom_port" + +desc 'verify existing data is still accessible with auth' +compare_mongos_cmd "find" "myApp:myPass@$cluster-mongos.$namespace" "-with-auth" "" "myApp" "test" "$custom_port" + +desc 'insert new data with auth' +run_mongos \ + 'use myApp\n db.test.insert({x: 200500})' \ + "myApp:myPass@$cluster-mongos.$namespace" "" "" "" "$custom_port" + +desc 'wait for backup agents after auth change' +wait_backup_agent $cluster-rs0-0 +wait_backup_agent $cluster-rs1-0 +wait_backup_agent $cluster-rs2-0 + +backup_name_with_auth="backup-with-auth" + +desc 'run backup with auth enabled' +run_backup minio ${backup_name_with_auth} +wait_backup "$backup_name_with_auth" + +desc 'insert more data with auth' +run_mongos \ + 'use myApp\n db.test.insert({x: 200501})' \ + "myApp:myPass@$cluster-mongos.$namespace" "" "" "" "$custom_port" + +desc 'verify new data exists' +compare_mongos_cmd "find" "myApp:myPass@$cluster-mongos.$namespace" "-after-insert-auth" "" "myApp" "test" "$custom_port" + +desc 'restore from backup (with auth)' +run_restore "$backup_name_with_auth" +wait_restore "$backup_name_with_auth" "$cluster" + +desc 'verify data was restored to state before last insert' +compare_mongos_cmd "find" "myApp:myPass@$cluster-mongos.$namespace" "-after-restore-auth" "" "myApp" "test" "$custom_port" + +desc 'cleanup backups' +kubectl_bin delete psmdb-backup --all + +destroy "$namespace" +desc 'test passed' diff --git a/e2e-tests/run-pr.csv b/e2e-tests/run-pr.csv index 8e58ef853d..426f97fed6 100644 --- a/e2e-tests/run-pr.csv +++ b/e2e-tests/run-pr.csv @@ -27,6 +27,7 @@ demand-backup-physical-sharded-gcp-native demand-backup-physical-sharded-minio demand-backup-physical-sharded-minio-native demand-backup-sharded +disabled-auth expose-sharded finalizer ignore-labels-annotations diff --git a/e2e-tests/run-release.csv b/e2e-tests/run-release.csv index 50b5c8b931..fc7ecc8513 100644 --- a/e2e-tests/run-release.csv +++ b/e2e-tests/run-release.csv @@ -28,6 +28,7 @@ demand-backup-physical-sharded-gcp-native demand-backup-physical-sharded-minio demand-backup-physical-sharded-minio-native demand-backup-sharded +disabled-auth expose-sharded finalizer ignore-labels-annotations diff --git a/pkg/apis/psmdb/v1/psmdb_types.go b/pkg/apis/psmdb/v1/psmdb_types.go index cfa365cea1..47ef7be799 100644 --- a/pkg/apis/psmdb/v1/psmdb_types.go +++ b/pkg/apis/psmdb/v1/psmdb_types.go @@ -659,6 +659,25 @@ func (conf MongoConfiguration) QuietEnabled() bool { return b } +// IsAuthorizationEnabled returns whether mongo config has `authorization` enabled under `security` section. +// https://www.mongodb.com/docs/manual/reference/configuration-options/#mongodb-setting-security.authorization +func (conf MongoConfiguration) IsAuthorizationEnabled() bool { + m, err := conf.GetOptions("security") + if err != nil || m == nil { + return true + } + v, ok := m["authorization"] + if !ok { + return true + } + + if str, ok := v.(string); ok { + return str != "disabled" + } + + return true +} + // GetPort returns the net.port of the mongo configuration. // https://www.mongodb.com/docs/manual/reference/configuration-options/#mongodb-setting-net.port func (conf MongoConfiguration) GetPort() (int32, error) { diff --git a/pkg/apis/psmdb/v1/psmdb_types_test.go b/pkg/apis/psmdb/v1/psmdb_types_test.go index 76ca08e3b6..94f7fada6b 100644 --- a/pkg/apis/psmdb/v1/psmdb_types_test.go +++ b/pkg/apis/psmdb/v1/psmdb_types_test.go @@ -234,6 +234,69 @@ func TestReplsetSpec_GetHorizons(t *testing.T) { }) } +func TestMongoConfiguration_IsAuthorizationEnabled(t *testing.T) { + tests := map[string]struct { + conf MongoConfiguration + expected bool + }{ + "no security section": { + conf: `systemLog: + verbosity: 1`, + expected: true, + }, + "empty config": { + conf: MongoConfiguration(""), + expected: true, + }, + "security section without authorization": { + conf: `security: + keyFile: /etc/mongodb-keyfile`, + expected: true, + }, + "authorization explicitly enabled": { + conf: `security: + authorization: enabled`, + expected: true, + }, + "authorization explicitly disabled": { + conf: `security: + authorization: disabled`, + expected: false, + }, + "authorization with other string value": { + conf: `security: + authorization: someOtherValue`, + expected: true, + }, + "authorization with empty string": { + conf: `security: + authorization: ""`, + expected: true, + }, + "complete security config with authorization enabled": { + conf: `security: + keyFile: /etc/mongodb-keyfile + authorization: enabled + clusterAuthMode: keyFile`, + expected: true, + }, + "complete security config with authorization disabled": { + conf: `security: + keyFile: /etc/mongodb-keyfile + authorization: disabled + clusterAuthMode: keyFile`, + expected: false, + }, + } + + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + result := tt.conf.IsAuthorizationEnabled() + assert.Equal(t, tt.expected, result) + }) + } +} + func TestBackupSpec_MainStorage(t *testing.T) { tests := map[string]struct { spec BackupSpec diff --git a/pkg/controller/perconaservermongodb/mgo.go b/pkg/controller/perconaservermongodb/mgo.go index b7d69bf7a4..21a8be9247 100644 --- a/pkg/controller/perconaservermongodb/mgo.go +++ b/pkg/controller/perconaservermongodb/mgo.go @@ -641,7 +641,7 @@ func (r *ReconcilePerconaServerMongoDB) handleRsAddToShard(ctx context.Context, } // handleReplsetInit initializes the replset within the first running pod's mongod container. -// This must be ran from within the running container to utilize the MongoDB Localhost Exception. +// This must be run from within the running container to utilize the MongoDB Localhost Exception. // // See: https://www.mongodb.com/docs/manual/core/localhost-exception/ func (r *ReconcilePerconaServerMongoDB) handleReplsetInit(ctx context.Context, cr *api.PerconaServerMongoDB, replset *api.ReplsetSpec, pods []corev1.Pod) (*corev1.Pod, *api.ReplsetMemberStatus, error) { @@ -1027,10 +1027,7 @@ func (r *ReconcilePerconaServerMongoDB) createOrUpdateSystemUsers(ctx context.Co return errors.Wrap(err, "create or update system role") } - users := []api.SystemUserRole{api.RoleClusterAdmin, api.RoleClusterMonitor, api.RoleBackup} - if cr.CompareVersion("1.13.0") >= 0 { - users = append(users, api.RoleDatabaseAdmin) - } + users := []api.SystemUserRole{api.RoleClusterAdmin, api.RoleClusterMonitor, api.RoleBackup, api.RoleDatabaseAdmin} for _, role := range users { creds, err := getInternalCredentials(ctx, r.client, cr, role) diff --git a/pkg/psmdb/container.go b/pkg/psmdb/container.go index 03c12bcb8a..138cba628e 100644 --- a/pkg/psmdb/container.go +++ b/pkg/psmdb/container.go @@ -218,30 +218,40 @@ func containerArgs(ctx context.Context, cr *api.PerconaServerMongoDB, replset *a // TODO(andrew): in the safe mode `sslAllowInvalidCertificates` should be set only with the external services args := []string{ "--bind_ip_all", - "--auth", - "--dbpath=" + config.MongodContainerDataDir, - "--port=" + strconv.Itoa(int(replset.GetPort())), - "--replSet=" + replset.Name, - "--storageEngine=" + string(replset.Storage.Engine), - "--relaxPermChecks", } - name, err := replset.CustomReplsetName() - if err == nil { - args[4] = "--replSet=" + name + if cr.CompareVersion("1.22.0") < 0 || replset.Configuration.IsAuthorizationEnabled() { + args = append(args, "--auth") } + replSetName := replset.Name + if name, err := replset.CustomReplsetName(); err == nil { + replSetName = name + } + + args = append(args, + "--dbpath="+config.MongodContainerDataDir, + "--port="+strconv.Itoa(int(replset.GetPort())), + "--replSet="+replSetName, + "--storageEngine="+string(replset.Storage.Engine), + "--relaxPermChecks", + ) + if *cr.Spec.TLS.AllowInvalidCertificates || cr.CompareVersion("1.16.0") < 0 { args = append(args, "--sslAllowInvalidCertificates") } - if cr.Spec.Secrets.InternalKey != "" || (cr.TLSEnabled() && cr.Spec.TLS.Mode == api.TLSModeAllow) || (!cr.TLSEnabled() && cr.UnsafeTLSDisabled()) { - args = append(args, - "--clusterAuthMode=keyFile", - "--keyFile="+config.MongodSecretsDir+"/mongodb-key", - ) - } else if cr.TLSEnabled() { - args = append(args, "--clusterAuthMode=x509") + // If auth is disabled, we consider that TLS should be also disabled + // and for that reason clusterAuthMode should not be even configured. + if replset.Configuration.IsAuthorizationEnabled() { + if cr.Spec.Secrets.InternalKey != "" || (cr.TLSEnabled() && cr.Spec.TLS.Mode == api.TLSModeAllow) || (!cr.TLSEnabled() && cr.UnsafeTLSDisabled()) { + args = append(args, + "--clusterAuthMode=keyFile", + "--keyFile="+config.MongodSecretsDir+"/mongodb-key", + ) + } else if cr.TLSEnabled() { + args = append(args, "--clusterAuthMode=x509") + } } if cr.CompareVersion("1.16.0") >= 0 { diff --git a/pkg/psmdb/mongos.go b/pkg/psmdb/mongos.go index ec0c058e34..ed6d0afa2b 100644 --- a/pkg/psmdb/mongos.go +++ b/pkg/psmdb/mongos.go @@ -260,15 +260,19 @@ func mongosContainerArgs(cr *api.PerconaServerMongoDB, useConfigFile bool, cfgIn "--relaxPermChecks", }...) - if cr.Spec.Secrets.InternalKey != "" || (cr.TLSEnabled() && cr.Spec.TLS.Mode == api.TLSModeAllow) || (!cr.TLSEnabled() && cr.UnsafeTLSDisabled()) { - args = append(args, - "--clusterAuthMode=keyFile", - "--keyFile="+config.MongodSecretsDir+"/mongodb-key", - ) - } else if cr.TLSEnabled() { - args = append(args, - "--clusterAuthMode=x509", - ) + // If auth is disabled, we consider that TLS should be also disabled + // and for that reason clusterAuthMode should not be even configured. + if cfgRs.Configuration.IsAuthorizationEnabled() { + if cr.Spec.Secrets.InternalKey != "" || (cr.TLSEnabled() && cr.Spec.TLS.Mode == api.TLSModeAllow) || (!cr.TLSEnabled() && cr.UnsafeTLSDisabled()) { + args = append(args, + "--clusterAuthMode=keyFile", + "--keyFile="+config.MongodSecretsDir+"/mongodb-key", + ) + } else if cr.TLSEnabled() { + args = append(args, + "--clusterAuthMode=x509", + ) + } } if cr.CompareVersion("1.16.0") >= 0 {