// Copyright (c) 2015-2021 MinIO, Inc.//// This file is part of MinIO Object Storage stack//// This program is free software: you can redistribute it and/or modify// it under the terms of the GNU Affero General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// This program is distributed in the hope that it will be useful// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU Affero General Public License for more details.//// You should have received a copy of the GNU Affero General Public License// along with this program. If not, see <http://www.gnu.org/licenses/>. package cmd import ( "bytes" "context" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "os" "runtime" "strings" "testing" "time" "github.com/minio/madmin-go" minio "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" cr "github.com/minio/minio-go/v7/pkg/credentials" "github.com/minio/minio-go/v7/pkg/s3utils" "github.com/minio/minio-go/v7/pkg/set" "github.com/minio/minio-go/v7/pkg/signer" "github.com/minio/minio/internal/auth") const ( testDefaultTimeout = 30 * time.Second) // API suite container for IAMtype TestSuiteIAM struct { TestSuiteCommon ServerTypeDescription string // Flag to turn on tests for etcd backend IAM withEtcdBackend bool endpoint string adm *madmin.AdminClient client *minio.Client} func newTestSuiteIAM(c TestSuiteCommon, withEtcdBackend bool) *TestSuiteIAM { etcdStr := "" if withEtcdBackend { etcdStr = " (with etcd backend)" } return &TestSuiteIAM{ TestSuiteCommon: c, ServerTypeDescription: fmt.Sprintf("%s%s", c.serverType, etcdStr), withEtcdBackend: withEtcdBackend, }} func (s *TestSuiteIAM) iamSetup(c *check) { var err error // strip url scheme from endpoint s.endpoint = strings.TrimPrefix(s.endPoint, "http://") if s.secure { s.endpoint = strings.TrimPrefix(s.endPoint, "https://") } s.adm, err = madmin.New(s.endpoint, s.accessKey, s.secretKey, s.secure) if err != nil { c.Fatalf("error creating admin client: %v", err) } // Set transport, so that TLS is handled correctly. s.adm.SetCustomTransport(s.TestSuiteCommon.client.Transport) s.client, err = minio.New(s.endpoint, &minio.Options{ Creds: credentials.NewStaticV4(s.accessKey, s.secretKey, ""), Secure: s.secure, Transport: s.TestSuiteCommon.client.Transport, }) if err != nil { c.Fatalf("error creating minio client: %v", err) }} // List of all IAM test suites (i.e. test server configuration combinations)// common to tests.var iamTestSuites = func() []*TestSuiteIAM { baseTestCases := []TestSuiteCommon{ // Init and run test on ErasureSD backend with signature v4. {serverType: "ErasureSD", signer: signerV4}, // Init and run test on ErasureSD backend, with tls enabled. {serverType: "ErasureSD", signer: signerV4, secure: true}, // Init and run test on Erasure backend. {serverType: "Erasure", signer: signerV4}, // Init and run test on ErasureSet backend. {serverType: "ErasureSet", signer: signerV4}, } testCases := []*TestSuiteIAM{} for _, bt := range baseTestCases { testCases = append(testCases, newTestSuiteIAM(bt, false), newTestSuiteIAM(bt, true), ) } return testCases}() const ( EnvTestEtcdBackend = "ETCD_SERVER") func (s *TestSuiteIAM) setUpEtcd(c *check, etcdServer string) { ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) defer cancel() configCmds := []string{ "etcd", "endpoints=" + etcdServer, "path_prefix=" + mustGetUUID(), } _, err := s.adm.SetConfigKV(ctx, strings.Join(configCmds, " ")) if err != nil { c.Fatalf("unable to setup Etcd for tests: %v", err) } s.RestartIAMSuite(c)} func (s *TestSuiteIAM) SetUpSuite(c *check) { // If etcd backend is specified and etcd server is not present, the test // is skipped. etcdServer := os.Getenv(EnvTestEtcdBackend) if s.withEtcdBackend && etcdServer == "" { c.Skip("Skipping etcd backend IAM test as no etcd server is configured.") } s.TestSuiteCommon.SetUpSuite(c) s.iamSetup(c) if s.withEtcdBackend { s.setUpEtcd(c, etcdServer) }} func (s *TestSuiteIAM) RestartIAMSuite(c *check) { s.TestSuiteCommon.RestartTestServer(c) s.iamSetup(c)} func (s *TestSuiteIAM) getAdminClient(c *check, accessKey, secretKey, sessionToken string) *madmin.AdminClient { madmClnt, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{ Creds: credentials.NewStaticV4(accessKey, secretKey, sessionToken), Secure: s.secure, }) if err != nil { c.Fatalf("error creating user admin client: %s", err) } madmClnt.SetCustomTransport(s.TestSuiteCommon.client.Transport) return madmClnt} func (s *TestSuiteIAM) getUserClient(c *check, accessKey, secretKey, sessionToken string) *minio.Client { client, err := minio.New(s.endpoint, &minio.Options{ Creds: credentials.NewStaticV4(accessKey, secretKey, sessionToken), Secure: s.secure, Transport: s.TestSuiteCommon.client.Transport, }) if err != nil { c.Fatalf("error creating user minio client: %s", err) } return client} func TestIAMInternalIDPServerSuite(t *testing.T) { if runtime.GOOS == globalWindowsOSName { t.Skip("windows is clunky disable these tests") } for i, testCase := range iamTestSuites { t.Run( fmt.Sprintf("Test: %d, ServerType: %s", i+1, testCase.ServerTypeDescription), func(t *testing.T) { suite := testCase c := &check{t, testCase.serverType} suite.SetUpSuite(c) suite.TestUserCreate(c) suite.TestUserPolicyEscalationBug(c) suite.TestPolicyCreate(c) suite.TestCannedPolicies(c) suite.TestGroupAddRemove(c) suite.TestServiceAccountOpsByAdmin(c) suite.TestServiceAccountOpsByUser(c) suite.TestAddServiceAccountPerms(c) suite.TearDownSuite(c) }, ) }} func (s *TestSuiteIAM) TestUserCreate(c *check) { ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) defer cancel() // 1. Create a user. accessKey, secretKey := mustGenerateCredentials(c) err := s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled) if err != nil { c.Fatalf("Unable to set user: %v", err) } // 2. Check new user appears in listing usersMap, err := s.adm.ListUsers(ctx) if err != nil { c.Fatalf("error listing: %v", err) } v, ok := usersMap[accessKey] if !ok { c.Fatalf("user not listed: %s", accessKey) } c.Assert(v.Status, madmin.AccountEnabled) // 3. Associate policy and check that user can access err = s.adm.SetPolicy(ctx, "readwrite", accessKey, false) if err != nil { c.Fatalf("unable to set policy: %v", err) } client := s.getUserClient(c, accessKey, secretKey, "") err = client.MakeBucket(ctx, getRandomBucketName(), minio.MakeBucketOptions{}) if err != nil { c.Fatalf("user could not create bucket: %v", err) } // 3.10. Check that user's password can be updated. _, newSecretKey := mustGenerateCredentials(c) err = s.adm.SetUser(ctx, accessKey, newSecretKey, madmin.AccountEnabled) if err != nil { c.Fatalf("Unable to update user's secret key: %v", err) } // 3.10.1 Check that old password no longer works. err = client.MakeBucket(ctx, getRandomBucketName(), minio.MakeBucketOptions{}) if err == nil { c.Fatalf("user was unexpectedly able to create bucket with bad password!") } // 3.10.2 Check that new password works. client = s.getUserClient(c, accessKey, newSecretKey, "") err = client.MakeBucket(ctx, getRandomBucketName(), minio.MakeBucketOptions{}) if err != nil { c.Fatalf("user could not create bucket: %v", err) } // 4. Check that user can be disabled and verify it. err = s.adm.SetUserStatus(ctx, accessKey, madmin.AccountDisabled) if err != nil { c.Fatalf("could not set user account to disabled") } usersMap, err = s.adm.ListUsers(ctx) if err != nil { c.Fatalf("error listing: %v", err) } v, ok = usersMap[accessKey] if !ok { c.Fatalf("user was not listed after disabling: %s", accessKey) } c.Assert(v.Status, madmin.AccountDisabled) err = client.MakeBucket(ctx, getRandomBucketName(), minio.MakeBucketOptions{}) if err == nil { c.Fatalf("user account was not disabled!") } // 5. Check that user can be deleted and verify it. err = s.adm.RemoveUser(ctx, accessKey) if err != nil { c.Fatalf("user could not be deleted: %v", err) } usersMap, err = s.adm.ListUsers(ctx) if err != nil { c.Fatalf("error listing: %v", err) } _, ok = usersMap[accessKey] if ok { c.Fatalf("user not deleted: %s", accessKey) } err = client.MakeBucket(ctx, getRandomBucketName(), minio.MakeBucketOptions{}) if err == nil { c.Fatalf("user account was not deleted!") }} func (s *TestSuiteIAM) TestUserPolicyEscalationBug(c *check) { ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) defer cancel() bucket := getRandomBucketName() err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) if err != nil { c.Fatalf("bucket creat error: %v", err) } // 2. Create a user, associate policy and verify access accessKey, secretKey := mustGenerateCredentials(c) err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled) if err != nil { c.Fatalf("Unable to set user: %v", err) } // 2.1 check that user does not have any access to the bucket uClient := s.getUserClient(c, accessKey, secretKey, "") c.mustNotListObjects(ctx, uClient, bucket) // 2.2 create and associate policy to user policy := "mypolicy-test-user-update" policyBytes := []byte(fmt.Sprintf(`{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::%s/*" ] } ]}`, bucket)) err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) if err != nil { c.Fatalf("policy add error: %v", err) } err = s.adm.SetPolicy(ctx, policy, accessKey, false) if err != nil { c.Fatalf("Unable to set policy: %v", err) } // 2.3 check user has access to bucket c.mustListObjects(ctx, uClient, bucket) // 2.3 check that user cannot delete the bucket err = uClient.RemoveBucket(ctx, bucket) if err == nil || err.Error() != "Access Denied." { c.Fatalf("bucket was deleted unexpectedly or got unexpected err: %v", err) } // 3. Craft a request to update the user's permissions ep := s.adm.GetEndpointURL() urlValue := url.Values{} urlValue.Add("accessKey", accessKey) u, err := url.Parse(fmt.Sprintf("%s://%s/minio/admin/v3/add-user?%s", ep.Scheme, ep.Host, s3utils.QueryEncode(urlValue))) if err != nil { c.Fatalf("unexpected url parse err: %v", err) } req, err := http.NewRequestWithContext(ctx, http.MethodPut, u.String(), nil) if err != nil { c.Fatalf("unexpected new request err: %v", err) } reqBodyArg := madmin.UserInfo{ SecretKey: secretKey, PolicyName: "consoleAdmin", Status: madmin.AccountEnabled, } buf, err := json.Marshal(reqBodyArg) if err != nil { c.Fatalf("unexpected json encode err: %v", err) } buf, err = madmin.EncryptData(secretKey, buf) if err != nil { c.Fatalf("unexpected encryption err: %v", err) } req.ContentLength = int64(len(buf)) sum := sha256.Sum256(buf) req.Header.Set("X-Amz-Content-Sha256", hex.EncodeToString(sum[:])) req.Body = ioutil.NopCloser(bytes.NewReader(buf)) req = signer.SignV4(*req, accessKey, secretKey, "", "") // 3.1 Execute the request. resp, err := s.TestSuiteCommon.client.Do(req) if err != nil { c.Fatalf("unexpected request err: %v", err) } if resp.StatusCode != 200 { c.Fatalf("got unexpected response: %#v\n", resp) } // 3.2 check that user cannot delete the bucket err = uClient.RemoveBucket(ctx, bucket) if err == nil || err.Error() != "Access Denied." { c.Fatalf("User was able to escalate privileges (Err=%v)!", err) }} func (s *TestSuiteIAM) TestAddServiceAccountPerms(c *check) { ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) defer cancel() // 1. Create a policy policy1 := "deny-svc" policy2 := "allow-svc" policyBytes := []byte(`{ "Version": "2012-10-17", "Statement": [ { "Effect": "Deny", "Action": [ "admin:CreateServiceAccount" ] } ]}`) newPolicyBytes := []byte(`{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::testbucket/*" ] } ]}`) err := s.adm.AddCannedPolicy(ctx, policy1, policyBytes) if err != nil { c.Fatalf("policy add error: %v", err) } err = s.adm.AddCannedPolicy(ctx, policy2, newPolicyBytes) if err != nil { c.Fatalf("policy add error: %v", err) } // 2. Verify that policy json is validated by server invalidPolicyBytes := policyBytes[:len(policyBytes)-1] err = s.adm.AddCannedPolicy(ctx, policy1+"invalid", invalidPolicyBytes) if err == nil { c.Fatalf("invalid policy creation success") } // 3. Create a user, associate policy and verify access accessKey, secretKey := mustGenerateCredentials(c) err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled) if err != nil { c.Fatalf("Unable to set user: %v", err) } // 3.1 check that user does not have any access to the bucket uClient := s.getUserClient(c, accessKey, secretKey, "") c.mustNotListObjects(ctx, uClient, "testbucket") // 3.2 associate policy to user err = s.adm.SetPolicy(ctx, policy1, accessKey, false) if err != nil { c.Fatalf("Unable to set policy: %v", err) } admClnt := s.getAdminClient(c, accessKey, secretKey, "") // 3.3 check user does not have explicit permissions to create service account. c.mustNotCreateSvcAccount(ctx, accessKey, admClnt) // 4. Verify the policy appears in listing ps, err := s.adm.ListCannedPolicies(ctx) if err != nil { c.Fatalf("policy list err: %v", err) } _, ok := ps[policy1] if !ok { c.Fatalf("policy was missing!") } // 3.2 associate policy to user err = s.adm.SetPolicy(ctx, policy2, accessKey, false) if err != nil { c.Fatalf("Unable to set policy: %v", err) } // 3.3 check user can create service account implicitly. c.mustCreateSvcAccount(ctx, accessKey, admClnt) Lines 1–500 of 1365. Load more
// Copyright (c) 2015-2021 MinIO, Inc. // // This file is part of MinIO Object Storage stack // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. package cmd import ( "bytes" "context" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "os" "runtime" "strings" "testing" "time" "github.com/minio/madmin-go" minio "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" cr "github.com/minio/minio-go/v7/pkg/credentials" "github.com/minio/minio-go/v7/pkg/s3utils" "github.com/minio/minio-go/v7/pkg/set" "github.com/minio/minio-go/v7/pkg/signer" "github.com/minio/minio/internal/auth" ) const ( testDefaultTimeout = 30 * time.Second ) // API suite container for IAM type TestSuiteIAM struct { TestSuiteCommon ServerTypeDescription string // Flag to turn on tests for etcd backend IAM withEtcdBackend bool endpoint string adm *madmin.AdminClient client *minio.Client } func newTestSuiteIAM(c TestSuiteCommon, withEtcdBackend bool) *TestSuiteIAM { etcdStr := "" if withEtcdBackend { etcdStr = " (with etcd backend)" } return &TestSuiteIAM{ TestSuiteCommon: c, ServerTypeDescription: fmt.Sprintf("%s%s", c.serverType, etcdStr), withEtcdBackend: withEtcdBackend, } } func (s *TestSuiteIAM) iamSetup(c *check) { var err error // strip url scheme from endpoint s.endpoint = strings.TrimPrefix(s.endPoint, "http://") if s.secure { s.endpoint = strings.TrimPrefix(s.endPoint, "https://") } s.adm, err = madmin.New(s.endpoint, s.accessKey, s.secretKey, s.secure) if err != nil { c.Fatalf("error creating admin client: %v", err) } // Set transport, so that TLS is handled correctly. s.adm.SetCustomTransport(s.TestSuiteCommon.client.Transport) s.client, err = minio.New(s.endpoint, &minio.Options{ Creds: credentials.NewStaticV4(s.accessKey, s.secretKey, ""), Secure: s.secure, Transport: s.TestSuiteCommon.client.Transport, }) if err != nil { c.Fatalf("error creating minio client: %v", err) } } // List of all IAM test suites (i.e. test server configuration combinations) // common to tests. var iamTestSuites = func() []*TestSuiteIAM { baseTestCases := []TestSuiteCommon{ // Init and run test on ErasureSD backend with signature v4. {serverType: "ErasureSD", signer: signerV4}, // Init and run test on ErasureSD backend, with tls enabled. {serverType: "ErasureSD", signer: signerV4, secure: true}, // Init and run test on Erasure backend. {serverType: "Erasure", signer: signerV4}, // Init and run test on ErasureSet backend. {serverType: "ErasureSet", signer: signerV4}, } testCases := []*TestSuiteIAM{} for _, bt := range baseTestCases { testCases = append(testCases, newTestSuiteIAM(bt, false), newTestSuiteIAM(bt, true), ) } return testCases }() const ( EnvTestEtcdBackend = "ETCD_SERVER" ) func (s *TestSuiteIAM) setUpEtcd(c *check, etcdServer string) { ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) defer cancel() configCmds := []string{ "etcd", "endpoints=" + etcdServer, "path_prefix=" + mustGetUUID(), } _, err := s.adm.SetConfigKV(ctx, strings.Join(configCmds, " ")) if err != nil { c.Fatalf("unable to setup Etcd for tests: %v", err) } s.RestartIAMSuite(c) } func (s *TestSuiteIAM) SetUpSuite(c *check) { // If etcd backend is specified and etcd server is not present, the test // is skipped. etcdServer := os.Getenv(EnvTestEtcdBackend) if s.withEtcdBackend && etcdServer == "" { c.Skip("Skipping etcd backend IAM test as no etcd server is configured.") } s.TestSuiteCommon.SetUpSuite(c) s.iamSetup(c) if s.withEtcdBackend { s.setUpEtcd(c, etcdServer) } } func (s *TestSuiteIAM) RestartIAMSuite(c *check) { s.TestSuiteCommon.RestartTestServer(c) s.iamSetup(c) } func (s *TestSuiteIAM) getAdminClient(c *check, accessKey, secretKey, sessionToken string) *madmin.AdminClient { madmClnt, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{ Creds: credentials.NewStaticV4(accessKey, secretKey, sessionToken), Secure: s.secure, }) if err != nil { c.Fatalf("error creating user admin client: %s", err) } madmClnt.SetCustomTransport(s.TestSuiteCommon.client.Transport) return madmClnt } func (s *TestSuiteIAM) getUserClient(c *check, accessKey, secretKey, sessionToken string) *minio.Client { client, err := minio.New(s.endpoint, &minio.Options{ Creds: credentials.NewStaticV4(accessKey, secretKey, sessionToken), Secure: s.secure, Transport: s.TestSuiteCommon.client.Transport, }) if err != nil { c.Fatalf("error creating user minio client: %s", err) } return client } func TestIAMInternalIDPServerSuite(t *testing.T) { if runtime.GOOS == globalWindowsOSName { t.Skip("windows is clunky disable these tests") } for i, testCase := range iamTestSuites { t.Run( fmt.Sprintf("Test: %d, ServerType: %s", i+1, testCase.ServerTypeDescription), func(t *testing.T) { suite := testCase c := &check{t, testCase.serverType} suite.SetUpSuite(c) suite.TestUserCreate(c) suite.TestUserPolicyEscalationBug(c) suite.TestPolicyCreate(c) suite.TestCannedPolicies(c) suite.TestGroupAddRemove(c) suite.TestServiceAccountOpsByAdmin(c) suite.TestServiceAccountOpsByUser(c) suite.TestAddServiceAccountPerms(c) suite.TearDownSuite(c) }, ) } } func (s *TestSuiteIAM) TestUserCreate(c *check) { ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) defer cancel() // 1. Create a user. accessKey, secretKey := mustGenerateCredentials(c) err := s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled) if err != nil { c.Fatalf("Unable to set user: %v", err) } // 2. Check new user appears in listing usersMap, err := s.adm.ListUsers(ctx) if err != nil { c.Fatalf("error listing: %v", err) } v, ok := usersMap[accessKey] if !ok { c.Fatalf("user not listed: %s", accessKey) } c.Assert(v.Status, madmin.AccountEnabled) // 3. Associate policy and check that user can access err = s.adm.SetPolicy(ctx, "readwrite", accessKey, false) if err != nil { c.Fatalf("unable to set policy: %v", err) } client := s.getUserClient(c, accessKey, secretKey, "") err = client.MakeBucket(ctx, getRandomBucketName(), minio.MakeBucketOptions{}) if err != nil { c.Fatalf("user could not create bucket: %v", err) } // 3.10. Check that user's password can be updated. _, newSecretKey := mustGenerateCredentials(c) err = s.adm.SetUser(ctx, accessKey, newSecretKey, madmin.AccountEnabled) if err != nil { c.Fatalf("Unable to update user's secret key: %v", err) } // 3.10.1 Check that old password no longer works. err = client.MakeBucket(ctx, getRandomBucketName(), minio.MakeBucketOptions{}) if err == nil { c.Fatalf("user was unexpectedly able to create bucket with bad password!") } // 3.10.2 Check that new password works. client = s.getUserClient(c, accessKey, newSecretKey, "") err = client.MakeBucket(ctx, getRandomBucketName(), minio.MakeBucketOptions{}) if err != nil { c.Fatalf("user could not create bucket: %v", err) } // 4. Check that user can be disabled and verify it. err = s.adm.SetUserStatus(ctx, accessKey, madmin.AccountDisabled) if err != nil { c.Fatalf("could not set user account to disabled") } usersMap, err = s.adm.ListUsers(ctx) if err != nil { c.Fatalf("error listing: %v", err) } v, ok = usersMap[accessKey] if !ok { c.Fatalf("user was not listed after disabling: %s", accessKey) } c.Assert(v.Status, madmin.AccountDisabled) err = client.MakeBucket(ctx, getRandomBucketName(), minio.MakeBucketOptions{}) if err == nil { c.Fatalf("user account was not disabled!") } // 5. Check that user can be deleted and verify it. err = s.adm.RemoveUser(ctx, accessKey) if err != nil { c.Fatalf("user could not be deleted: %v", err) } usersMap, err = s.adm.ListUsers(ctx) if err != nil { c.Fatalf("error listing: %v", err) } _, ok = usersMap[accessKey] if ok { c.Fatalf("user not deleted: %s", accessKey) } err = client.MakeBucket(ctx, getRandomBucketName(), minio.MakeBucketOptions{}) if err == nil { c.Fatalf("user account was not deleted!") } } func (s *TestSuiteIAM) TestUserPolicyEscalationBug(c *check) { ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) defer cancel() bucket := getRandomBucketName() err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) if err != nil { c.Fatalf("bucket creat error: %v", err) } // 2. Create a user, associate policy and verify access accessKey, secretKey := mustGenerateCredentials(c) err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled) if err != nil { c.Fatalf("Unable to set user: %v", err) } // 2.1 check that user does not have any access to the bucket uClient := s.getUserClient(c, accessKey, secretKey, "") c.mustNotListObjects(ctx, uClient, bucket) // 2.2 create and associate policy to user policy := "mypolicy-test-user-update" policyBytes := []byte(fmt.Sprintf(`{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::%s/*" ] } ] }`, bucket)) err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) if err != nil { c.Fatalf("policy add error: %v", err) } err = s.adm.SetPolicy(ctx, policy, accessKey, false) if err != nil { c.Fatalf("Unable to set policy: %v", err) } // 2.3 check user has access to bucket c.mustListObjects(ctx, uClient, bucket) // 2.3 check that user cannot delete the bucket err = uClient.RemoveBucket(ctx, bucket) if err == nil || err.Error() != "Access Denied." { c.Fatalf("bucket was deleted unexpectedly or got unexpected err: %v", err) } // 3. Craft a request to update the user's permissions ep := s.adm.GetEndpointURL() urlValue := url.Values{} urlValue.Add("accessKey", accessKey) u, err := url.Parse(fmt.Sprintf("%s://%s/minio/admin/v3/add-user?%s", ep.Scheme, ep.Host, s3utils.QueryEncode(urlValue))) if err != nil { c.Fatalf("unexpected url parse err: %v", err) } req, err := http.NewRequestWithContext(ctx, http.MethodPut, u.String(), nil) if err != nil { c.Fatalf("unexpected new request err: %v", err) } reqBodyArg := madmin.UserInfo{ SecretKey: secretKey, PolicyName: "consoleAdmin", Status: madmin.AccountEnabled, } buf, err := json.Marshal(reqBodyArg) if err != nil { c.Fatalf("unexpected json encode err: %v", err) } buf, err = madmin.EncryptData(secretKey, buf) if err != nil { c.Fatalf("unexpected encryption err: %v", err) } req.ContentLength = int64(len(buf)) sum := sha256.Sum256(buf) req.Header.Set("X-Amz-Content-Sha256", hex.EncodeToString(sum[:])) req.Body = ioutil.NopCloser(bytes.NewReader(buf)) req = signer.SignV4(*req, accessKey, secretKey, "", "") // 3.1 Execute the request. resp, err := s.TestSuiteCommon.client.Do(req) if err != nil { c.Fatalf("unexpected request err: %v", err) } if resp.StatusCode != 200 { c.Fatalf("got unexpected response: %#v\n", resp) } // 3.2 check that user cannot delete the bucket err = uClient.RemoveBucket(ctx, bucket) if err == nil || err.Error() != "Access Denied." { c.Fatalf("User was able to escalate privileges (Err=%v)!", err) } } func (s *TestSuiteIAM) TestAddServiceAccountPerms(c *check) { ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) defer cancel() // 1. Create a policy policy1 := "deny-svc" policy2 := "allow-svc" policyBytes := []byte(`{ "Version": "2012-10-17", "Statement": [ { "Effect": "Deny", "Action": [ "admin:CreateServiceAccount" ] } ] }`) newPolicyBytes := []byte(`{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::testbucket/*" ] } ] }`) err := s.adm.AddCannedPolicy(ctx, policy1, policyBytes) if err != nil { c.Fatalf("policy add error: %v", err) } err = s.adm.AddCannedPolicy(ctx, policy2, newPolicyBytes) if err != nil { c.Fatalf("policy add error: %v", err) } // 2. Verify that policy json is validated by server invalidPolicyBytes := policyBytes[:len(policyBytes)-1] err = s.adm.AddCannedPolicy(ctx, policy1+"invalid", invalidPolicyBytes) if err == nil { c.Fatalf("invalid policy creation success") } // 3. Create a user, associate policy and verify access accessKey, secretKey := mustGenerateCredentials(c) err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled) if err != nil { c.Fatalf("Unable to set user: %v", err) } // 3.1 check that user does not have any access to the bucket uClient := s.getUserClient(c, accessKey, secretKey, "") c.mustNotListObjects(ctx, uClient, "testbucket") // 3.2 associate policy to user err = s.adm.SetPolicy(ctx, policy1, accessKey, false) if err != nil { c.Fatalf("Unable to set policy: %v", err) } admClnt := s.getAdminClient(c, accessKey, secretKey, "") // 3.3 check user does not have explicit permissions to create service account. c.mustNotCreateSvcAccount(ctx, accessKey, admClnt) // 4. Verify the policy appears in listing ps, err := s.adm.ListCannedPolicies(ctx) if err != nil { c.Fatalf("policy list err: %v", err) } _, ok := ps[policy1] if !ok { c.Fatalf("policy was missing!") } // 3.2 associate policy to user err = s.adm.SetPolicy(ctx, policy2, accessKey, false) if err != nil { c.Fatalf("Unable to set policy: %v", err) } // 3.3 check user can create service account implicitly. c.mustCreateSvcAccount(ctx, accessKey, admClnt)