1365 lines · 38.2 KB · Go
RawDownload
// 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t