github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/sts-handlers_test.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "context" 22 "fmt" 23 "os" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/minio/madmin-go/v3" 29 minio "github.com/minio/minio-go/v7" 30 cr "github.com/minio/minio-go/v7/pkg/credentials" 31 "github.com/minio/minio-go/v7/pkg/set" 32 ldap "github.com/minio/pkg/v2/ldap" 33 "golang.org/x/exp/slices" 34 ) 35 36 func runAllIAMSTSTests(suite *TestSuiteIAM, c *check) { 37 suite.SetUpSuite(c) 38 // The STS for root test needs to be the first one after setup. 39 suite.TestSTSForRoot(c) 40 suite.TestSTS(c) 41 suite.TestSTSWithDenyDeleteVersion(c) 42 suite.TestSTSWithTags(c) 43 suite.TestSTSServiceAccountsWithUsername(c) 44 suite.TestSTSWithGroupPolicy(c) 45 suite.TearDownSuite(c) 46 } 47 48 func TestIAMInternalIDPSTSServerSuite(t *testing.T) { 49 baseTestCases := []TestSuiteCommon{ 50 // Init and run test on ErasureSD backend with signature v4. 51 {serverType: "ErasureSD", signer: signerV4}, 52 // Init and run test on ErasureSD backend, with tls enabled. 53 {serverType: "ErasureSD", signer: signerV4, secure: true}, 54 // Init and run test on Erasure backend. 55 {serverType: "Erasure", signer: signerV4}, 56 // Init and run test on ErasureSet backend. 57 {serverType: "ErasureSet", signer: signerV4}, 58 } 59 testCases := []*TestSuiteIAM{} 60 for _, bt := range baseTestCases { 61 testCases = append(testCases, 62 newTestSuiteIAM(bt, false), 63 newTestSuiteIAM(bt, true), 64 ) 65 } 66 for i, testCase := range testCases { 67 etcdStr := "" 68 if testCase.withEtcdBackend { 69 etcdStr = " (with etcd backend)" 70 } 71 t.Run( 72 fmt.Sprintf("Test: %d, ServerType: %s%s", i+1, testCase.serverType, etcdStr), 73 func(t *testing.T) { 74 runAllIAMSTSTests(testCase, &check{t, testCase.serverType}) 75 }, 76 ) 77 } 78 } 79 80 func (s *TestSuiteIAM) TestSTSServiceAccountsWithUsername(c *check) { 81 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 82 defer cancel() 83 84 bucket := "dillon-bucket" 85 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 86 if err != nil { 87 c.Fatalf("bucket create error: %v", err) 88 } 89 90 // Create policy 91 policy := "mypolicy-username" 92 policyBytes := []byte(`{ 93 "Version": "2012-10-17", 94 "Statement": [ 95 { 96 "Effect": "Allow", 97 "Action": [ 98 "s3:*" 99 ], 100 "Resource": [ 101 "arn:aws:s3:::${aws:username}-*" 102 ] 103 } 104 ] 105 }`) 106 err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) 107 if err != nil { 108 c.Fatalf("policy add error: %v", err) 109 } 110 111 if err = s.adm.AddUser(ctx, "dillon", "dillon-123"); err != nil { 112 c.Fatalf("policy add error: %v", err) 113 } 114 115 err = s.adm.SetPolicy(ctx, policy, "dillon", false) 116 if err != nil { 117 c.Fatalf("Unable to set policy: %v", err) 118 } 119 120 assumeRole := cr.STSAssumeRole{ 121 Client: s.TestSuiteCommon.client, 122 STSEndpoint: s.endPoint, 123 Options: cr.STSAssumeRoleOptions{ 124 AccessKey: "dillon", 125 SecretKey: "dillon-123", 126 Location: "", 127 }, 128 } 129 130 value, err := assumeRole.Retrieve() 131 if err != nil { 132 c.Fatalf("Expected to generate STS creds, got err: %#v", err) 133 } 134 135 // Check that the LDAP sts cred is actually working. 136 minioClient, err := minio.New(s.endpoint, &minio.Options{ 137 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 138 Secure: s.secure, 139 Transport: s.TestSuiteCommon.client.Transport, 140 }) 141 if err != nil { 142 c.Fatalf("Error initializing client: %v", err) 143 } 144 145 // Validate that the client from sts creds can access the bucket. 146 c.mustListObjects(ctx, minioClient, bucket) 147 148 // Create an madmin client with user creds 149 userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{ 150 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 151 Secure: s.secure, 152 }) 153 if err != nil { 154 c.Fatalf("Err creating user admin client: %v", err) 155 } 156 userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport) 157 158 // Create svc acc 159 cr := c.mustCreateSvcAccount(ctx, value.AccessKeyID, userAdmClient) 160 161 svcClient := s.getUserClient(c, cr.AccessKey, cr.SecretKey, "") 162 163 // 1. Check S3 access for service account ListObjects() 164 c.mustListObjects(ctx, svcClient, bucket) 165 166 // 2. Check S3 access for upload 167 c.mustUpload(ctx, svcClient, bucket) 168 169 // 3. Check S3 access for download 170 c.mustDownload(ctx, svcClient, bucket) 171 } 172 173 func (s *TestSuiteIAM) TestSTSWithDenyDeleteVersion(c *check) { 174 ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) 175 defer cancel() 176 177 bucket := getRandomBucketName() 178 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{ObjectLocking: true}) 179 if err != nil { 180 c.Fatalf("bucket creat error: %v", err) 181 } 182 183 // Create policy, user and associate policy 184 policy := "mypolicy" 185 policyBytes := []byte(fmt.Sprintf(`{ 186 "Version": "2012-10-17", 187 "Statement": [ 188 { 189 "Sid": "ObjectActionsRW", 190 "Effect": "Allow", 191 "Action": [ 192 "s3:PutObject", 193 "s3:PutObjectTagging", 194 "s3:AbortMultipartUpload", 195 "s3:DeleteObject", 196 "s3:GetObject", 197 "s3:GetObjectTagging", 198 "s3:GetObjectVersion", 199 "s3:ListMultipartUploadParts" 200 ], 201 "Resource": [ 202 "arn:aws:s3:::%s/*" 203 ] 204 }, 205 { 206 "Sid": "DenyDeleteVersionAction", 207 "Effect": "Deny", 208 "Action": [ 209 "s3:DeleteObjectVersion" 210 ], 211 "Resource": [ 212 "arn:aws:s3:::%s/*" 213 ] 214 } 215 ] 216 } 217 `, bucket, bucket)) 218 219 err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) 220 if err != nil { 221 c.Fatalf("policy add error: %v", err) 222 } 223 224 accessKey, secretKey := mustGenerateCredentials(c) 225 err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled) 226 if err != nil { 227 c.Fatalf("Unable to set user: %v", err) 228 } 229 230 err = s.adm.SetPolicy(ctx, policy, accessKey, false) 231 if err != nil { 232 c.Fatalf("Unable to set policy: %v", err) 233 } 234 235 // confirm that the user is able to access the bucket 236 uClient := s.getUserClient(c, accessKey, secretKey, "") 237 versions := c.mustUploadReturnVersions(ctx, uClient, bucket) 238 c.mustNotDelete(ctx, uClient, bucket, versions[0]) 239 240 assumeRole := cr.STSAssumeRole{ 241 Client: s.TestSuiteCommon.client, 242 STSEndpoint: s.endPoint, 243 Options: cr.STSAssumeRoleOptions{ 244 AccessKey: accessKey, 245 SecretKey: secretKey, 246 Location: "", 247 }, 248 } 249 250 value, err := assumeRole.Retrieve() 251 if err != nil { 252 c.Fatalf("err calling assumeRole: %v", err) 253 } 254 255 minioClient, err := minio.New(s.endpoint, &minio.Options{ 256 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 257 Secure: s.secure, 258 Transport: s.TestSuiteCommon.client.Transport, 259 }) 260 if err != nil { 261 c.Fatalf("Error initializing client: %v", err) 262 } 263 264 versions = c.mustUploadReturnVersions(ctx, minioClient, bucket) 265 c.mustNotDelete(ctx, minioClient, bucket, versions[0]) 266 } 267 268 func (s *TestSuiteIAM) TestSTSWithTags(c *check) { 269 ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) 270 defer cancel() 271 272 bucket := getRandomBucketName() 273 object := getRandomObjectName() 274 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 275 if err != nil { 276 c.Fatalf("bucket creat error: %v", err) 277 } 278 279 // Create policy, user and associate policy 280 policy := "mypolicy" 281 policyBytes := []byte(fmt.Sprintf(`{ 282 "Version": "2012-10-17", 283 "Statement": [ 284 { 285 "Effect": "Allow", 286 "Action": "s3:GetObject", 287 "Resource": "arn:aws:s3:::%s/*", 288 "Condition": { "StringEquals": {"s3:ExistingObjectTag/security": "public" } } 289 }, 290 { 291 "Effect": "Allow", 292 "Action": "s3:DeleteObjectTagging", 293 "Resource": "arn:aws:s3:::%s/*", 294 "Condition": { "StringEquals": {"s3:ExistingObjectTag/security": "public" } } 295 }, 296 { 297 "Effect": "Allow", 298 "Action": "s3:DeleteObject", 299 "Resource": "arn:aws:s3:::%s/*" 300 }, 301 { 302 "Effect": "Allow", 303 "Action": [ 304 "s3:PutObject" 305 ], 306 "Resource": [ 307 "arn:aws:s3:::%s/*" 308 ], 309 "Condition": { 310 "ForAllValues:StringLike": { 311 "s3:RequestObjectTagKeys": [ 312 "security", 313 "virus" 314 ] 315 } 316 } 317 } 318 ] 319 }`, bucket, bucket, bucket, bucket)) 320 err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) 321 if err != nil { 322 c.Fatalf("policy add error: %v", err) 323 } 324 325 accessKey, secretKey := mustGenerateCredentials(c) 326 err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled) 327 if err != nil { 328 c.Fatalf("Unable to set user: %v", err) 329 } 330 331 err = s.adm.SetPolicy(ctx, policy, accessKey, false) 332 if err != nil { 333 c.Fatalf("Unable to set policy: %v", err) 334 } 335 336 // confirm that the user is able to access the bucket 337 uClient := s.getUserClient(c, accessKey, secretKey, "") 338 c.mustPutObjectWithTags(ctx, uClient, bucket, object) 339 c.mustGetObject(ctx, uClient, bucket, object) 340 341 assumeRole := cr.STSAssumeRole{ 342 Client: s.TestSuiteCommon.client, 343 STSEndpoint: s.endPoint, 344 Options: cr.STSAssumeRoleOptions{ 345 AccessKey: accessKey, 346 SecretKey: secretKey, 347 Location: "", 348 }, 349 } 350 351 value, err := assumeRole.Retrieve() 352 if err != nil { 353 c.Fatalf("err calling assumeRole: %v", err) 354 } 355 356 minioClient, err := minio.New(s.endpoint, &minio.Options{ 357 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 358 Secure: s.secure, 359 Transport: s.TestSuiteCommon.client.Transport, 360 }) 361 if err != nil { 362 c.Fatalf("Error initializing client: %v", err) 363 } 364 365 // Validate sts creds can access the object 366 c.mustPutObjectWithTags(ctx, minioClient, bucket, object) 367 c.mustGetObject(ctx, minioClient, bucket, object) 368 c.mustHeadObject(ctx, minioClient, bucket, object, 2) 369 370 // Validate that the client can remove objects 371 if err = minioClient.RemoveObjectTagging(ctx, bucket, object, minio.RemoveObjectTaggingOptions{}); err != nil { 372 c.Fatalf("user is unable to delete the object tags: %v", err) 373 } 374 375 if err = minioClient.RemoveObject(ctx, bucket, object, minio.RemoveObjectOptions{}); err != nil { 376 c.Fatalf("user is unable to delete the object: %v", err) 377 } 378 } 379 380 func (s *TestSuiteIAM) TestSTS(c *check) { 381 ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) 382 defer cancel() 383 384 bucket := getRandomBucketName() 385 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 386 if err != nil { 387 c.Fatalf("bucket creat error: %v", err) 388 } 389 390 // Create policy, user and associate policy 391 policy := "mypolicy" 392 policyBytes := []byte(fmt.Sprintf(`{ 393 "Version": "2012-10-17", 394 "Statement": [ 395 { 396 "Effect": "Allow", 397 "Action": [ 398 "s3:PutObject", 399 "s3:GetObject", 400 "s3:ListBucket" 401 ], 402 "Resource": [ 403 "arn:aws:s3:::%s/*" 404 ] 405 } 406 ] 407 }`, bucket)) 408 err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) 409 if err != nil { 410 c.Fatalf("policy add error: %v", err) 411 } 412 413 accessKey, secretKey := mustGenerateCredentials(c) 414 err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled) 415 if err != nil { 416 c.Fatalf("Unable to set user: %v", err) 417 } 418 419 err = s.adm.SetPolicy(ctx, policy, accessKey, false) 420 if err != nil { 421 c.Fatalf("Unable to set policy: %v", err) 422 } 423 424 // confirm that the user is able to access the bucket 425 uClient := s.getUserClient(c, accessKey, secretKey, "") 426 c.mustListObjects(ctx, uClient, bucket) 427 428 assumeRole := cr.STSAssumeRole{ 429 Client: s.TestSuiteCommon.client, 430 STSEndpoint: s.endPoint, 431 Options: cr.STSAssumeRoleOptions{ 432 AccessKey: accessKey, 433 SecretKey: secretKey, 434 Location: "", 435 }, 436 } 437 438 value, err := assumeRole.Retrieve() 439 if err != nil { 440 c.Fatalf("err calling assumeRole: %v", err) 441 } 442 443 minioClient, err := minio.New(s.endpoint, &minio.Options{ 444 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 445 Secure: s.secure, 446 Transport: s.TestSuiteCommon.client.Transport, 447 }) 448 if err != nil { 449 c.Fatalf("Error initializing client: %v", err) 450 } 451 452 // Validate that the client from sts creds can access the bucket. 453 c.mustListObjects(ctx, minioClient, bucket) 454 455 // Validate that the client cannot remove any objects 456 err = minioClient.RemoveObject(ctx, bucket, "someobject", minio.RemoveObjectOptions{}) 457 if err.Error() != "Access Denied." { 458 c.Fatalf("unexpected non-access-denied err: %v", err) 459 } 460 } 461 462 func (s *TestSuiteIAM) TestSTSWithGroupPolicy(c *check) { 463 ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) 464 defer cancel() 465 466 bucket := getRandomBucketName() 467 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 468 if err != nil { 469 c.Fatalf("bucket creat error: %v", err) 470 } 471 472 // Create policy, user and associate policy 473 policy := "mypolicy" 474 policyBytes := []byte(fmt.Sprintf(`{ 475 "Version": "2012-10-17", 476 "Statement": [ 477 { 478 "Effect": "Allow", 479 "Action": [ 480 "s3:PutObject", 481 "s3:GetObject", 482 "s3:ListBucket" 483 ], 484 "Resource": [ 485 "arn:aws:s3:::%s/*" 486 ] 487 } 488 ] 489 }`, bucket)) 490 err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) 491 if err != nil { 492 c.Fatalf("policy add error: %v", err) 493 } 494 495 accessKey, secretKey := mustGenerateCredentials(c) 496 err = s.adm.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled) 497 if err != nil { 498 c.Fatalf("Unable to set user: %v", err) 499 } 500 501 // confirm that the user is unable to access the bucket - we have not 502 // yet set any policy 503 uClient := s.getUserClient(c, accessKey, secretKey, "") 504 c.mustNotListObjects(ctx, uClient, bucket) 505 506 err = s.adm.UpdateGroupMembers(ctx, madmin.GroupAddRemove{ 507 Group: "test-group", 508 Members: []string{accessKey}, 509 }) 510 if err != nil { 511 c.Fatalf("unable to add user to group: %v", err) 512 } 513 514 err = s.adm.SetPolicy(ctx, policy, "test-group", true) 515 if err != nil { 516 c.Fatalf("Unable to set policy: %v", err) 517 } 518 519 // confirm that the user is able to access the bucket - permission comes 520 // from group. 521 c.mustListObjects(ctx, uClient, bucket) 522 523 // Create STS user. 524 assumeRole := cr.STSAssumeRole{ 525 Client: s.TestSuiteCommon.client, 526 STSEndpoint: s.endPoint, 527 Options: cr.STSAssumeRoleOptions{ 528 AccessKey: accessKey, 529 SecretKey: secretKey, 530 Location: "", 531 }, 532 } 533 value, err := assumeRole.Retrieve() 534 if err != nil { 535 c.Fatalf("err calling assumeRole: %v", err) 536 } 537 538 // Check that STS user client has access coming from parent user's 539 // group. 540 minioClient, err := minio.New(s.endpoint, &minio.Options{ 541 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 542 Secure: s.secure, 543 Transport: s.TestSuiteCommon.client.Transport, 544 }) 545 if err != nil { 546 c.Fatalf("Error initializing client: %v", err) 547 } 548 549 // Validate that the client from sts creds can access the bucket. 550 c.mustListObjects(ctx, minioClient, bucket) 551 552 // Validate that the client cannot remove any objects 553 err = minioClient.RemoveObject(ctx, bucket, "someobject", minio.RemoveObjectOptions{}) 554 if err.Error() != "Access Denied." { 555 c.Fatalf("unexpected non-access-denied err: %v", err) 556 } 557 } 558 559 // TestSTSForRoot - needs to be the first test after server setup due to the 560 // buckets list check. 561 func (s *TestSuiteIAM) TestSTSForRoot(c *check) { 562 ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) 563 defer cancel() 564 565 bucket := getRandomBucketName() 566 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 567 if err != nil { 568 c.Fatalf("bucket create error: %v", err) 569 } 570 571 assumeRole := cr.STSAssumeRole{ 572 Client: s.TestSuiteCommon.client, 573 STSEndpoint: s.endPoint, 574 Options: cr.STSAssumeRoleOptions{ 575 AccessKey: globalActiveCred.AccessKey, 576 SecretKey: globalActiveCred.SecretKey, 577 Location: "", 578 }, 579 } 580 581 value, err := assumeRole.Retrieve() 582 if err != nil { 583 c.Fatalf("err calling assumeRole: %v", err) 584 } 585 586 minioClient, err := minio.New(s.endpoint, &minio.Options{ 587 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 588 Secure: s.secure, 589 Transport: s.TestSuiteCommon.client.Transport, 590 }) 591 if err != nil { 592 c.Fatalf("Error initializing client: %v", err) 593 } 594 595 // Validate that the client from sts creds can access the bucket. 596 c.mustListObjects(ctx, minioClient, bucket) 597 598 // Validate that a bucket can be created 599 bucket2 := getRandomBucketName() 600 err = minioClient.MakeBucket(ctx, bucket2, minio.MakeBucketOptions{}) 601 if err != nil { 602 c.Fatalf("bucket creat error: %v", err) 603 } 604 605 // Validate that admin APIs can be called - create an madmin client with 606 // user creds 607 userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{ 608 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 609 Secure: s.secure, 610 }) 611 if err != nil { 612 c.Fatalf("Err creating user admin client: %v", err) 613 } 614 userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport) 615 616 time.Sleep(2 * time.Second) // wait for listbuckets cache to be invalidated 617 618 accInfo, err := userAdmClient.AccountInfo(ctx, madmin.AccountOpts{}) 619 if err != nil { 620 c.Fatalf("root user STS should be able to get account info: %v", err) 621 } 622 623 gotBuckets := set.NewStringSet() 624 for _, b := range accInfo.Buckets { 625 gotBuckets.Add(b.Name) 626 if !(b.Access.Read && b.Access.Write) { 627 c.Fatalf("root user should have read and write access to bucket: %v", b.Name) 628 } 629 } 630 shouldHaveBuckets := set.CreateStringSet(bucket2, bucket) 631 if !gotBuckets.Equals(shouldHaveBuckets) { 632 c.Fatalf("root user should have access to all buckets") 633 } 634 635 // This must fail. 636 if err := userAdmClient.AddUser(ctx, globalActiveCred.AccessKey, globalActiveCred.SecretKey); err == nil { 637 c.Fatal("AddUser() for root credential must fail via root STS creds") 638 } 639 } 640 641 // SetUpLDAP - expects to setup an LDAP test server using the test LDAP 642 // container and canned data from https://github.com/minio/minio-ldap-testing 643 func (s *TestSuiteIAM) SetUpLDAP(c *check, serverAddr string) { 644 ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) 645 defer cancel() 646 647 configCmds := []string{ 648 "identity_ldap", 649 fmt.Sprintf("server_addr=%s", serverAddr), 650 "server_insecure=on", 651 "lookup_bind_dn=cn=admin,dc=min,dc=io", 652 "lookup_bind_password=admin", 653 "user_dn_search_base_dn=dc=min,dc=io", 654 "user_dn_search_filter=(uid=%s)", 655 "group_search_base_dn=ou=swengg,dc=min,dc=io", 656 "group_search_filter=(&(objectclass=groupofnames)(member=%d))", 657 } 658 _, err := s.adm.SetConfigKV(ctx, strings.Join(configCmds, " ")) 659 if err != nil { 660 c.Fatalf("unable to setup LDAP for tests: %v", err) 661 } 662 663 s.RestartIAMSuite(c) 664 } 665 666 const ( 667 EnvTestLDAPServer = "_MINIO_LDAP_TEST_SERVER" 668 ) 669 670 func TestIAMWithLDAPServerSuite(t *testing.T) { 671 for i, testCase := range iamTestSuites { 672 t.Run( 673 fmt.Sprintf("Test: %d, ServerType: %s", i+1, testCase.ServerTypeDescription), 674 func(t *testing.T) { 675 c := &check{t, testCase.serverType} 676 suite := testCase 677 678 ldapServer := os.Getenv(EnvTestLDAPServer) 679 if ldapServer == "" { 680 c.Skip("Skipping LDAP test as no LDAP server is provided.") 681 } 682 683 suite.SetUpSuite(c) 684 suite.SetUpLDAP(c, ldapServer) 685 suite.TestLDAPSTS(c) 686 suite.TestLDAPUnicodeVariations(c) 687 suite.TestLDAPSTSServiceAccounts(c) 688 suite.TestLDAPSTSServiceAccountsWithUsername(c) 689 suite.TestLDAPSTSServiceAccountsWithGroups(c) 690 suite.TearDownSuite(c) 691 }, 692 ) 693 } 694 } 695 696 func (s *TestSuiteIAM) TestLDAPSTS(c *check) { 697 ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) 698 defer cancel() 699 700 bucket := getRandomBucketName() 701 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 702 if err != nil { 703 c.Fatalf("bucket create error: %v", err) 704 } 705 706 // Create policy 707 policy := "mypolicy" 708 policyBytes := []byte(fmt.Sprintf(`{ 709 "Version": "2012-10-17", 710 "Statement": [ 711 { 712 "Effect": "Allow", 713 "Action": [ 714 "s3:PutObject", 715 "s3:GetObject", 716 "s3:ListBucket" 717 ], 718 "Resource": [ 719 "arn:aws:s3:::%s/*" 720 ] 721 } 722 ] 723 }`, bucket)) 724 err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) 725 if err != nil { 726 c.Fatalf("policy add error: %v", err) 727 } 728 729 ldapID := cr.LDAPIdentity{ 730 Client: s.TestSuiteCommon.client, 731 STSEndpoint: s.endPoint, 732 LDAPUsername: "dillon", 733 LDAPPassword: "dillon", 734 } 735 736 _, err = ldapID.Retrieve() 737 if err == nil { 738 c.Fatalf("Expected to fail to create STS cred with no associated policy!") 739 } 740 741 // Attempting to set a non-existent policy should fail. 742 userDN := "uid=dillon,ou=people,ou=swengg,dc=min,dc=io" 743 err = s.adm.SetPolicy(ctx, policy+"x", userDN, false) 744 if err == nil { 745 c.Fatalf("should not be able to set non-existent policy") 746 } 747 748 err = s.adm.SetPolicy(ctx, policy, userDN, false) 749 if err != nil { 750 c.Fatalf("Unable to set policy: %v", err) 751 } 752 753 value, err := ldapID.Retrieve() 754 if err != nil { 755 c.Fatalf("Expected to generate STS creds, got err: %#v", err) 756 } 757 758 minioClient, err := minio.New(s.endpoint, &minio.Options{ 759 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 760 Secure: s.secure, 761 Transport: s.TestSuiteCommon.client.Transport, 762 }) 763 if err != nil { 764 c.Fatalf("Error initializing client: %v", err) 765 } 766 767 // Validate that user listing does not return any entries 768 usersList, err := s.adm.ListUsers(ctx) 769 if err != nil { 770 c.Fatalf("list users should not fail: %v", err) 771 } 772 if len(usersList) != 1 { 773 c.Fatalf("expected user listing output: %v", usersList) 774 } 775 uinfo := usersList[userDN] 776 if uinfo.PolicyName != policy || uinfo.Status != madmin.AccountEnabled { 777 c.Fatalf("expected user listing content: %v", uinfo) 778 } 779 780 // Validate that the client from sts creds can access the bucket. 781 c.mustListObjects(ctx, minioClient, bucket) 782 783 // Validate that the client cannot remove any objects 784 err = minioClient.RemoveObject(ctx, bucket, "someobject", minio.RemoveObjectOptions{}) 785 if err.Error() != "Access Denied." { 786 c.Fatalf("unexpected non-access-denied err: %v", err) 787 } 788 789 // Remove the policy assignment on the user DN: 790 err = s.adm.SetPolicy(ctx, "", userDN, false) 791 if err != nil { 792 c.Fatalf("Unable to remove policy setting: %v", err) 793 } 794 795 _, err = ldapID.Retrieve() 796 if err == nil { 797 c.Fatalf("Expected to fail to create a user with no associated policy!") 798 } 799 800 // Set policy via group and validate policy assignment. 801 groupDN := "cn=projectb,ou=groups,ou=swengg,dc=min,dc=io" 802 err = s.adm.SetPolicy(ctx, policy, groupDN, true) 803 if err != nil { 804 c.Fatalf("Unable to set group policy: %v", err) 805 } 806 807 value, err = ldapID.Retrieve() 808 if err != nil { 809 c.Fatalf("Expected to generate STS creds, got err: %#v", err) 810 } 811 812 minioClient, err = minio.New(s.endpoint, &minio.Options{ 813 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 814 Secure: s.secure, 815 Transport: s.TestSuiteCommon.client.Transport, 816 }) 817 if err != nil { 818 c.Fatalf("Error initializing client: %v", err) 819 } 820 821 // Validate that the client from sts creds can access the bucket. 822 c.mustListObjects(ctx, minioClient, bucket) 823 824 // Validate that the client cannot remove any objects 825 err = minioClient.RemoveObject(ctx, bucket, "someobject", minio.RemoveObjectOptions{}) 826 c.Assert(err.Error(), "Access Denied.") 827 } 828 829 func (s *TestSuiteIAM) TestLDAPUnicodeVariations(c *check) { 830 ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) 831 defer cancel() 832 833 bucket := getRandomBucketName() 834 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 835 if err != nil { 836 c.Fatalf("bucket create error: %v", err) 837 } 838 839 // Create policy 840 policy := "mypolicy" 841 policyBytes := []byte(fmt.Sprintf(`{ 842 "Version": "2012-10-17", 843 "Statement": [ 844 { 845 "Effect": "Allow", 846 "Action": [ 847 "s3:PutObject", 848 "s3:GetObject", 849 "s3:ListBucket" 850 ], 851 "Resource": [ 852 "arn:aws:s3:::%s/*" 853 ] 854 } 855 ] 856 }`, bucket)) 857 err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) 858 if err != nil { 859 c.Fatalf("policy add error: %v", err) 860 } 861 862 ldapID := cr.LDAPIdentity{ 863 Client: s.TestSuiteCommon.client, 864 STSEndpoint: s.endPoint, 865 LDAPUsername: "svc.algorithm", 866 LDAPPassword: "example", 867 } 868 869 _, err = ldapID.Retrieve() 870 if err == nil { 871 c.Fatalf("Expected to fail to create STS cred with no associated policy!") 872 } 873 874 mustNormalizeDN := func(dn string) string { 875 normalizedDN, err := ldap.NormalizeDN(dn) 876 if err != nil { 877 c.Fatalf("normalize err: %v", err) 878 } 879 return normalizedDN 880 } 881 882 actualUserDN := mustNormalizeDN("uid=svc.algorithm,OU=swengg,DC=min,DC=io") 883 884 // \uFE52 is the unicode dot SMALL FULL STOP used below: 885 userDNWithUnicodeDot := "uid=svc﹒algorithm,OU=swengg,DC=min,DC=io" 886 887 _, err = s.adm.AttachPolicyLDAP(ctx, madmin.PolicyAssociationReq{ 888 Policies: []string{policy}, 889 User: userDNWithUnicodeDot, 890 }) 891 if err != nil { 892 c.Fatalf("Unable to set policy: %v", err) 893 } 894 895 value, err := ldapID.Retrieve() 896 if err != nil { 897 c.Fatalf("Expected to generate STS creds, got err: %#v", err) 898 } 899 900 usersList, err := s.adm.ListUsers(ctx) 901 if err != nil { 902 c.Fatalf("list users should not fail: %v", err) 903 } 904 if len(usersList) != 1 { 905 c.Fatalf("expected user listing output: %#v", usersList) 906 } 907 uinfo := usersList[actualUserDN] 908 if uinfo.PolicyName != policy || uinfo.Status != madmin.AccountEnabled { 909 c.Fatalf("expected user listing content: %v", uinfo) 910 } 911 912 minioClient, err := minio.New(s.endpoint, &minio.Options{ 913 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 914 Secure: s.secure, 915 Transport: s.TestSuiteCommon.client.Transport, 916 }) 917 if err != nil { 918 c.Fatalf("Error initializing client: %v", err) 919 } 920 921 // Validate that the client from sts creds can access the bucket. 922 c.mustListObjects(ctx, minioClient, bucket) 923 924 // Validate that the client cannot remove any objects 925 err = minioClient.RemoveObject(ctx, bucket, "someobject", minio.RemoveObjectOptions{}) 926 if err.Error() != "Access Denied." { 927 c.Fatalf("unexpected non-access-denied err: %v", err) 928 } 929 930 // Remove the policy assignment on the user DN: 931 _, err = s.adm.DetachPolicyLDAP(ctx, madmin.PolicyAssociationReq{ 932 Policies: []string{policy}, 933 User: userDNWithUnicodeDot, 934 }) 935 if err != nil { 936 c.Fatalf("Unable to remove policy setting: %v", err) 937 } 938 939 _, err = ldapID.Retrieve() 940 if err == nil { 941 c.Fatalf("Expected to fail to create a user with no associated policy!") 942 } 943 944 // Set policy via group and validate policy assignment. 945 actualGroupDN := mustNormalizeDN("cn=project.c,ou=groups,ou=swengg,dc=min,dc=io") 946 groupDNWithUnicodeDot := "cn=project﹒c,ou=groups,ou=swengg,dc=min,dc=io" 947 _, err = s.adm.AttachPolicyLDAP(ctx, madmin.PolicyAssociationReq{ 948 Policies: []string{policy}, 949 Group: groupDNWithUnicodeDot, 950 }) 951 if err != nil { 952 c.Fatalf("Unable to attach group policy: %v", err) 953 } 954 955 value, err = ldapID.Retrieve() 956 if err != nil { 957 c.Fatalf("Expected to generate STS creds, got err: %#v", err) 958 } 959 960 policyResult, err := s.adm.GetLDAPPolicyEntities(ctx, madmin.PolicyEntitiesQuery{ 961 Policy: []string{policy}, 962 }) 963 if err != nil { 964 c.Fatalf("GetLDAPPolicyEntities should not fail: %v", err) 965 } 966 { 967 // Check that the mapping we created exists. 968 idx := slices.IndexFunc(policyResult.PolicyMappings, func(e madmin.PolicyEntities) bool { 969 return e.Policy == policy && slices.Contains(e.Groups, actualGroupDN) 970 }) 971 if !(idx >= 0) { 972 c.Fatalf("expected groupDN (%s) to be present in mapping list: %#v", actualGroupDN, policyResult) 973 } 974 } 975 976 minioClient, err = minio.New(s.endpoint, &minio.Options{ 977 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 978 Secure: s.secure, 979 Transport: s.TestSuiteCommon.client.Transport, 980 }) 981 if err != nil { 982 c.Fatalf("Error initializing client: %v", err) 983 } 984 985 // Validate that the client from sts creds can access the bucket. 986 c.mustListObjects(ctx, minioClient, bucket) 987 988 // Validate that the client cannot remove any objects 989 err = minioClient.RemoveObject(ctx, bucket, "someobject", minio.RemoveObjectOptions{}) 990 c.Assert(err.Error(), "Access Denied.") 991 } 992 993 func (s *TestSuiteIAM) TestLDAPSTSServiceAccounts(c *check) { 994 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 995 defer cancel() 996 997 bucket := getRandomBucketName() 998 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 999 if err != nil { 1000 c.Fatalf("bucket create error: %v", err) 1001 } 1002 1003 // Create policy 1004 policy := "mypolicy" 1005 policyBytes := []byte(fmt.Sprintf(`{ 1006 "Version": "2012-10-17", 1007 "Statement": [ 1008 { 1009 "Effect": "Allow", 1010 "Action": [ 1011 "s3:PutObject", 1012 "s3:GetObject", 1013 "s3:ListBucket" 1014 ], 1015 "Resource": [ 1016 "arn:aws:s3:::%s/*" 1017 ] 1018 } 1019 ] 1020 }`, bucket)) 1021 err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) 1022 if err != nil { 1023 c.Fatalf("policy add error: %v", err) 1024 } 1025 1026 userDN := "uid=dillon,ou=people,ou=swengg,dc=min,dc=io" 1027 err = s.adm.SetPolicy(ctx, policy, userDN, false) 1028 if err != nil { 1029 c.Fatalf("Unable to set policy: %v", err) 1030 } 1031 1032 ldapID := cr.LDAPIdentity{ 1033 Client: s.TestSuiteCommon.client, 1034 STSEndpoint: s.endPoint, 1035 LDAPUsername: "dillon", 1036 LDAPPassword: "dillon", 1037 } 1038 1039 value, err := ldapID.Retrieve() 1040 if err != nil { 1041 c.Fatalf("Expected to generate STS creds, got err: %#v", err) 1042 } 1043 1044 // Check that the LDAP sts cred is actually working. 1045 minioClient, err := minio.New(s.endpoint, &minio.Options{ 1046 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 1047 Secure: s.secure, 1048 Transport: s.TestSuiteCommon.client.Transport, 1049 }) 1050 if err != nil { 1051 c.Fatalf("Error initializing client: %v", err) 1052 } 1053 1054 // Validate that the client from sts creds can access the bucket. 1055 c.mustListObjects(ctx, minioClient, bucket) 1056 1057 // Create an madmin client with user creds 1058 userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{ 1059 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 1060 Secure: s.secure, 1061 }) 1062 if err != nil { 1063 c.Fatalf("Err creating user admin client: %v", err) 1064 } 1065 userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport) 1066 1067 // Create svc acc 1068 cr := c.mustCreateSvcAccount(ctx, value.AccessKeyID, userAdmClient) 1069 1070 // 1. Check that svc account appears in listing 1071 c.assertSvcAccAppearsInListing(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey) 1072 1073 // 2. Check that svc account info can be queried 1074 c.assertSvcAccInfoQueryable(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey, true) 1075 1076 // 3. Check S3 access 1077 c.assertSvcAccS3Access(ctx, s, cr, bucket) 1078 1079 // 5. Check that service account can be deleted. 1080 c.assertSvcAccDeletion(ctx, s, userAdmClient, value.AccessKeyID, bucket) 1081 1082 // 6. Check that service account cannot be created for some other user. 1083 c.mustNotCreateSvcAccount(ctx, globalActiveCred.AccessKey, userAdmClient) 1084 } 1085 1086 func (s *TestSuiteIAM) TestLDAPSTSServiceAccountsWithUsername(c *check) { 1087 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 1088 defer cancel() 1089 1090 bucket := "dillon" 1091 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 1092 if err != nil { 1093 c.Fatalf("bucket create error: %v", err) 1094 } 1095 1096 // Create policy 1097 policy := "mypolicy-username" 1098 policyBytes := []byte(`{ 1099 "Version": "2012-10-17", 1100 "Statement": [ 1101 { 1102 "Effect": "Allow", 1103 "Action": [ 1104 "s3:PutObject", 1105 "s3:GetObject", 1106 "s3:ListBucket" 1107 ], 1108 "Resource": [ 1109 "arn:aws:s3:::${ldap:username}/*" 1110 ] 1111 } 1112 ] 1113 }`) 1114 err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) 1115 if err != nil { 1116 c.Fatalf("policy add error: %v", err) 1117 } 1118 1119 userDN := "uid=dillon,ou=people,ou=swengg,dc=min,dc=io" 1120 err = s.adm.SetPolicy(ctx, policy, userDN, false) 1121 if err != nil { 1122 c.Fatalf("Unable to set policy: %v", err) 1123 } 1124 1125 ldapID := cr.LDAPIdentity{ 1126 Client: s.TestSuiteCommon.client, 1127 STSEndpoint: s.endPoint, 1128 LDAPUsername: "dillon", 1129 LDAPPassword: "dillon", 1130 } 1131 1132 value, err := ldapID.Retrieve() 1133 if err != nil { 1134 c.Fatalf("Expected to generate STS creds, got err: %#v", err) 1135 } 1136 1137 // Check that the LDAP sts cred is actually working. 1138 minioClient, err := minio.New(s.endpoint, &minio.Options{ 1139 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 1140 Secure: s.secure, 1141 Transport: s.TestSuiteCommon.client.Transport, 1142 }) 1143 if err != nil { 1144 c.Fatalf("Error initializing client: %v", err) 1145 } 1146 1147 // Validate that the client from sts creds can access the bucket. 1148 c.mustListObjects(ctx, minioClient, bucket) 1149 1150 // Create an madmin client with user creds 1151 userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{ 1152 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 1153 Secure: s.secure, 1154 }) 1155 if err != nil { 1156 c.Fatalf("Err creating user admin client: %v", err) 1157 } 1158 userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport) 1159 1160 // Create svc acc 1161 cr := c.mustCreateSvcAccount(ctx, value.AccessKeyID, userAdmClient) 1162 1163 svcClient := s.getUserClient(c, cr.AccessKey, cr.SecretKey, "") 1164 1165 // 1. Check S3 access for service account ListObjects() 1166 c.mustListObjects(ctx, svcClient, bucket) 1167 1168 // 2. Check S3 access for upload 1169 c.mustUpload(ctx, svcClient, bucket) 1170 1171 // 3. Check S3 access for download 1172 c.mustDownload(ctx, svcClient, bucket) 1173 } 1174 1175 // In this test, the parent users gets their permissions from a group, rather 1176 // than having a policy set directly on them. 1177 func (s *TestSuiteIAM) TestLDAPSTSServiceAccountsWithGroups(c *check) { 1178 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 1179 defer cancel() 1180 1181 bucket := getRandomBucketName() 1182 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 1183 if err != nil { 1184 c.Fatalf("bucket create error: %v", err) 1185 } 1186 1187 // Create policy 1188 policy := "mypolicy" 1189 policyBytes := []byte(fmt.Sprintf(`{ 1190 "Version": "2012-10-17", 1191 "Statement": [ 1192 { 1193 "Effect": "Allow", 1194 "Action": [ 1195 "s3:PutObject", 1196 "s3:GetObject", 1197 "s3:ListBucket" 1198 ], 1199 "Resource": [ 1200 "arn:aws:s3:::%s/*" 1201 ] 1202 } 1203 ] 1204 }`, bucket)) 1205 err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) 1206 if err != nil { 1207 c.Fatalf("policy add error: %v", err) 1208 } 1209 1210 groupDN := "cn=projecta,ou=groups,ou=swengg,dc=min,dc=io" 1211 err = s.adm.SetPolicy(ctx, policy, groupDN, true) 1212 if err != nil { 1213 c.Fatalf("Unable to set policy: %v", err) 1214 } 1215 1216 ldapID := cr.LDAPIdentity{ 1217 Client: s.TestSuiteCommon.client, 1218 STSEndpoint: s.endPoint, 1219 LDAPUsername: "dillon", 1220 LDAPPassword: "dillon", 1221 } 1222 1223 value, err := ldapID.Retrieve() 1224 if err != nil { 1225 c.Fatalf("Expected to generate STS creds, got err: %#v", err) 1226 } 1227 1228 // Check that the LDAP sts cred is actually working. 1229 minioClient, err := minio.New(s.endpoint, &minio.Options{ 1230 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 1231 Secure: s.secure, 1232 Transport: s.TestSuiteCommon.client.Transport, 1233 }) 1234 if err != nil { 1235 c.Fatalf("Error initializing client: %v", err) 1236 } 1237 1238 // Validate that the client from sts creds can access the bucket. 1239 c.mustListObjects(ctx, minioClient, bucket) 1240 1241 // Create an madmin client with user creds 1242 userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{ 1243 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 1244 Secure: s.secure, 1245 }) 1246 if err != nil { 1247 c.Fatalf("Err creating user admin client: %v", err) 1248 } 1249 userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport) 1250 1251 // Create svc acc 1252 cr := c.mustCreateSvcAccount(ctx, value.AccessKeyID, userAdmClient) 1253 1254 // 1. Check that svc account appears in listing 1255 c.assertSvcAccAppearsInListing(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey) 1256 1257 // 2. Check that svc account info can be queried 1258 c.assertSvcAccInfoQueryable(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey, true) 1259 1260 // 3. Check S3 access 1261 c.assertSvcAccS3Access(ctx, s, cr, bucket) 1262 1263 // 5. Check that service account can be deleted. 1264 c.assertSvcAccDeletion(ctx, s, userAdmClient, value.AccessKeyID, bucket) 1265 1266 // 6. Check that service account cannot be created for some other user. 1267 c.mustNotCreateSvcAccount(ctx, globalActiveCred.AccessKey, userAdmClient) 1268 } 1269 1270 func (s *TestSuiteIAM) TestOpenIDSTS(c *check) { 1271 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 1272 defer cancel() 1273 1274 bucket := getRandomBucketName() 1275 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 1276 if err != nil { 1277 c.Fatalf("bucket create error: %v", err) 1278 } 1279 1280 // Generate web identity STS token by interacting with OpenID IDP. 1281 token, err := MockOpenIDTestUserInteraction(ctx, testAppParams, "dillon@example.io", "dillon") 1282 if err != nil { 1283 c.Fatalf("mock user err: %v", err) 1284 } 1285 // fmt.Printf("TOKEN: %s\n", token) 1286 1287 webID := cr.STSWebIdentity{ 1288 Client: s.TestSuiteCommon.client, 1289 STSEndpoint: s.endPoint, 1290 GetWebIDTokenExpiry: func() (*cr.WebIdentityToken, error) { 1291 return &cr.WebIdentityToken{ 1292 Token: token, 1293 }, nil 1294 }, 1295 } 1296 1297 // Create policy - with name as one of the groups in OpenID the user is 1298 // a member of. 1299 policy := "projecta" 1300 policyBytes := []byte(fmt.Sprintf(`{ 1301 "Version": "2012-10-17", 1302 "Statement": [ 1303 { 1304 "Effect": "Allow", 1305 "Action": [ 1306 "s3:PutObject", 1307 "s3:GetObject", 1308 "s3:ListBucket" 1309 ], 1310 "Resource": [ 1311 "arn:aws:s3:::%s/*" 1312 ] 1313 } 1314 ] 1315 }`, bucket)) 1316 err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) 1317 if err != nil { 1318 c.Fatalf("policy add error: %v", err) 1319 } 1320 1321 value, err := webID.Retrieve() 1322 if err != nil { 1323 c.Fatalf("Expected to generate STS creds, got err: %#v", err) 1324 } 1325 1326 minioClient, err := minio.New(s.endpoint, &minio.Options{ 1327 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 1328 Secure: s.secure, 1329 Transport: s.TestSuiteCommon.client.Transport, 1330 }) 1331 if err != nil { 1332 c.Fatalf("Error initializing client: %v", err) 1333 } 1334 1335 // Validate that the client from sts creds can access the bucket. 1336 c.mustListObjects(ctx, minioClient, bucket) 1337 1338 // Validate that the client cannot remove any objects 1339 err = minioClient.RemoveObject(ctx, bucket, "someobject", minio.RemoveObjectOptions{}) 1340 if err.Error() != "Access Denied." { 1341 c.Fatalf("unexpected non-access-denied err: %v", err) 1342 } 1343 } 1344 1345 func (s *TestSuiteIAM) TestOpenIDSTSDurationSeconds(c *check) { 1346 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 1347 defer cancel() 1348 1349 bucket := getRandomBucketName() 1350 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 1351 if err != nil { 1352 c.Fatalf("bucket create error: %v", err) 1353 } 1354 1355 // Generate web identity STS token by interacting with OpenID IDP. 1356 token, err := MockOpenIDTestUserInteraction(ctx, testAppParams, "dillon@example.io", "dillon") 1357 if err != nil { 1358 c.Fatalf("mock user err: %v", err) 1359 } 1360 // fmt.Printf("TOKEN: %s\n", token) 1361 1362 webID := cr.STSWebIdentity{ 1363 Client: s.TestSuiteCommon.client, 1364 STSEndpoint: s.endPoint, 1365 GetWebIDTokenExpiry: func() (*cr.WebIdentityToken, error) { 1366 return &cr.WebIdentityToken{ 1367 Token: token, 1368 Expiry: 900, 1369 }, nil 1370 }, 1371 } 1372 1373 // Create policy - with name as one of the groups in OpenID the user is 1374 // a member of. 1375 policy := "projecta" 1376 policyTmpl := `{ 1377 "Version": "2012-10-17", 1378 "Statement": [ 1379 { 1380 "Effect": "Deny", 1381 "Action": ["sts:AssumeRoleWithWebIdentity"], 1382 "Condition": {"NumericGreaterThan": {"sts:DurationSeconds": "%d"}} 1383 }, 1384 { 1385 "Effect": "Allow", 1386 "Action": [ 1387 "s3:PutObject", 1388 "s3:GetObject", 1389 "s3:ListBucket" 1390 ], 1391 "Resource": [ 1392 "arn:aws:s3:::%s/*" 1393 ] 1394 } 1395 ] 1396 }` 1397 1398 for i, testCase := range []struct { 1399 durSecs int 1400 expectedErr bool 1401 }{ 1402 {60, true}, 1403 {1800, false}, 1404 } { 1405 policyBytes := []byte(fmt.Sprintf(policyTmpl, testCase.durSecs, bucket)) 1406 err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) 1407 if err != nil { 1408 c.Fatalf("Test %d: policy add error: %v", i+1, err) 1409 } 1410 1411 value, err := webID.Retrieve() 1412 if err != nil && !testCase.expectedErr { 1413 c.Fatalf("Test %d: Expected to generate STS creds, got err: %#v", i+1, err) 1414 } 1415 if err == nil && testCase.expectedErr { 1416 c.Fatalf("Test %d: An error is unexpected to generate STS creds, got err: %#v", i+1, err) 1417 } 1418 1419 if err != nil && testCase.expectedErr { 1420 continue 1421 } 1422 1423 minioClient, err := minio.New(s.endpoint, &minio.Options{ 1424 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 1425 Secure: s.secure, 1426 Transport: s.TestSuiteCommon.client.Transport, 1427 }) 1428 if err != nil { 1429 c.Fatalf("Test %d: Error initializing client: %v", i+1, err) 1430 } 1431 1432 c.mustListObjects(ctx, minioClient, bucket) 1433 } 1434 } 1435 1436 func (s *TestSuiteIAM) TestOpenIDSTSAddUser(c *check) { 1437 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 1438 defer cancel() 1439 1440 bucket := getRandomBucketName() 1441 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 1442 if err != nil { 1443 c.Fatalf("bucket create error: %v", err) 1444 } 1445 1446 // Generate web identity STS token by interacting with OpenID IDP. 1447 token, err := MockOpenIDTestUserInteraction(ctx, testAppParams, "dillon@example.io", "dillon") 1448 if err != nil { 1449 c.Fatalf("mock user err: %v", err) 1450 } 1451 1452 webID := cr.STSWebIdentity{ 1453 Client: s.TestSuiteCommon.client, 1454 STSEndpoint: s.endPoint, 1455 GetWebIDTokenExpiry: func() (*cr.WebIdentityToken, error) { 1456 return &cr.WebIdentityToken{ 1457 Token: token, 1458 }, nil 1459 }, 1460 } 1461 1462 // Create policy - with name as one of the groups in OpenID the user is 1463 // a member of. 1464 policy := "projecta" 1465 policyBytes := []byte(fmt.Sprintf(`{ 1466 "Version": "2012-10-17", 1467 "Statement": [ 1468 { 1469 "Effect": "Allow", 1470 "Action": [ 1471 "s3:PutObject", 1472 "s3:GetObject", 1473 "s3:ListBucket" 1474 ], 1475 "Resource": [ 1476 "arn:aws:s3:::%s/*" 1477 ] 1478 } 1479 ] 1480 }`, bucket)) 1481 err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) 1482 if err != nil { 1483 c.Fatalf("policy add error: %v", err) 1484 } 1485 1486 value, err := webID.Retrieve() 1487 if err != nil { 1488 c.Fatalf("Expected to generate STS creds, got err: %#v", err) 1489 } 1490 1491 // Create an madmin client with user creds 1492 userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{ 1493 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 1494 Secure: s.secure, 1495 }) 1496 if err != nil { 1497 c.Fatalf("Err creating user admin client: %v", err) 1498 } 1499 userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport) 1500 1501 c.mustNotCreateIAMUser(ctx, userAdmClient) 1502 1503 // Create admin user policy. 1504 policyBytes = []byte(`{ 1505 "Version": "2012-10-17", 1506 "Statement": [ 1507 { 1508 "Effect": "Allow", 1509 "Action": [ 1510 "admin:*" 1511 ] 1512 } 1513 ] 1514 }`) 1515 err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) 1516 if err != nil { 1517 c.Fatalf("policy add error: %v", err) 1518 } 1519 1520 cr := c.mustCreateIAMUser(ctx, userAdmClient) 1521 1522 userInfo := c.mustGetIAMUserInfo(ctx, userAdmClient, cr.AccessKey) 1523 c.Assert(userInfo.Status, madmin.AccountEnabled) 1524 } 1525 1526 func (s *TestSuiteIAM) TestOpenIDServiceAcc(c *check) { 1527 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 1528 defer cancel() 1529 1530 bucket := getRandomBucketName() 1531 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 1532 if err != nil { 1533 c.Fatalf("bucket create error: %v", err) 1534 } 1535 1536 // Generate web identity STS token by interacting with OpenID IDP. 1537 token, err := MockOpenIDTestUserInteraction(ctx, testAppParams, "dillon@example.io", "dillon") 1538 if err != nil { 1539 c.Fatalf("mock user err: %v", err) 1540 } 1541 1542 webID := cr.STSWebIdentity{ 1543 Client: s.TestSuiteCommon.client, 1544 STSEndpoint: s.endPoint, 1545 GetWebIDTokenExpiry: func() (*cr.WebIdentityToken, error) { 1546 return &cr.WebIdentityToken{ 1547 Token: token, 1548 }, nil 1549 }, 1550 } 1551 1552 // Create policy - with name as one of the groups in OpenID the user is 1553 // a member of. 1554 policy := "projecta" 1555 policyBytes := []byte(fmt.Sprintf(`{ 1556 "Version": "2012-10-17", 1557 "Statement": [ 1558 { 1559 "Effect": "Allow", 1560 "Action": [ 1561 "s3:PutObject", 1562 "s3:GetObject", 1563 "s3:ListBucket" 1564 ], 1565 "Resource": [ 1566 "arn:aws:s3:::%s/*" 1567 ] 1568 } 1569 ] 1570 }`, bucket)) 1571 err = s.adm.AddCannedPolicy(ctx, policy, policyBytes) 1572 if err != nil { 1573 c.Fatalf("policy add error: %v", err) 1574 } 1575 1576 value, err := webID.Retrieve() 1577 if err != nil { 1578 c.Fatalf("Expected to generate STS creds, got err: %#v", err) 1579 } 1580 1581 // Create an madmin client with user creds 1582 userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{ 1583 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 1584 Secure: s.secure, 1585 }) 1586 if err != nil { 1587 c.Fatalf("Err creating user admin client: %v", err) 1588 } 1589 userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport) 1590 1591 // Create svc acc 1592 cr := c.mustCreateSvcAccount(ctx, value.AccessKeyID, userAdmClient) 1593 1594 // 1. Check that svc account appears in listing 1595 c.assertSvcAccAppearsInListing(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey) 1596 1597 // 2. Check that svc account info can be queried 1598 c.assertSvcAccInfoQueryable(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey, true) 1599 1600 // 3. Check S3 access 1601 c.assertSvcAccS3Access(ctx, s, cr, bucket) 1602 1603 // 5. Check that service account can be deleted. 1604 c.assertSvcAccDeletion(ctx, s, userAdmClient, value.AccessKeyID, bucket) 1605 1606 // 6. Check that service account cannot be created for some other user. 1607 c.mustNotCreateSvcAccount(ctx, globalActiveCred.AccessKey, userAdmClient) 1608 } 1609 1610 var testAppParams = OpenIDClientAppParams{ 1611 ClientID: "minio-client-app", 1612 ClientSecret: "minio-client-app-secret", 1613 ProviderURL: "http://127.0.0.1:5556/dex", 1614 RedirectURL: "http://127.0.0.1:10000/oauth_callback", 1615 } 1616 1617 const ( 1618 EnvTestOpenIDServer = "_MINIO_OPENID_TEST_SERVER" 1619 EnvTestOpenIDServer2 = "_MINIO_OPENID_TEST_SERVER_2" 1620 ) 1621 1622 // SetUpOpenIDs - sets up one or more OpenID test servers using the test OpenID 1623 // container and canned data from https://github.com/minio/minio-ldap-testing 1624 // 1625 // Each set of client app params corresponds to a separate openid server, and 1626 // the i-th server in this will be applied the i-th policy in `rolePolicies`. If 1627 // a rolePolicies entry is an empty string, that server will be configured as 1628 // policy-claim based openid server. NOTE that a valid configuration can have a 1629 // policy claim based provider only if it is the only OpenID provider. 1630 func (s *TestSuiteIAM) SetUpOpenIDs(c *check, testApps []OpenIDClientAppParams, rolePolicies []string) error { 1631 ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) 1632 defer cancel() 1633 1634 for i, testApp := range testApps { 1635 configCmds := []string{ 1636 fmt.Sprintf("identity_openid:%d", i), 1637 fmt.Sprintf("config_url=%s/.well-known/openid-configuration", testApp.ProviderURL), 1638 fmt.Sprintf("client_id=%s", testApp.ClientID), 1639 fmt.Sprintf("client_secret=%s", testApp.ClientSecret), 1640 "scopes=openid,groups", 1641 fmt.Sprintf("redirect_uri=%s", testApp.RedirectURL), 1642 } 1643 if rolePolicies[i] != "" { 1644 configCmds = append(configCmds, fmt.Sprintf("role_policy=%s", rolePolicies[i])) 1645 } else { 1646 configCmds = append(configCmds, "claim_name=groups") 1647 } 1648 _, err := s.adm.SetConfigKV(ctx, strings.Join(configCmds, " ")) 1649 if err != nil { 1650 return fmt.Errorf("unable to setup OpenID for tests: %v", err) 1651 } 1652 } 1653 1654 s.RestartIAMSuite(c) 1655 return nil 1656 } 1657 1658 // SetUpOpenID - expects to setup an OpenID test server using the test OpenID 1659 // container and canned data from https://github.com/minio/minio-ldap-testing 1660 func (s *TestSuiteIAM) SetUpOpenID(c *check, serverAddr string, rolePolicy string) { 1661 ctx, cancel := context.WithTimeout(context.Background(), testDefaultTimeout) 1662 defer cancel() 1663 1664 configCmds := []string{ 1665 "identity_openid", 1666 fmt.Sprintf("config_url=%s/.well-known/openid-configuration", serverAddr), 1667 "client_id=minio-client-app", 1668 "client_secret=minio-client-app-secret", 1669 "scopes=openid,groups", 1670 "redirect_uri=http://127.0.0.1:10000/oauth_callback", 1671 } 1672 if rolePolicy != "" { 1673 configCmds = append(configCmds, fmt.Sprintf("role_policy=%s", rolePolicy)) 1674 } else { 1675 configCmds = append(configCmds, "claim_name=groups") 1676 } 1677 _, err := s.adm.SetConfigKV(ctx, strings.Join(configCmds, " ")) 1678 if err != nil { 1679 c.Fatalf("unable to setup OpenID for tests: %v", err) 1680 } 1681 1682 s.RestartIAMSuite(c) 1683 } 1684 1685 func TestIAMWithOpenIDServerSuite(t *testing.T) { 1686 for i, testCase := range iamTestSuites { 1687 t.Run( 1688 fmt.Sprintf("Test: %d, ServerType: %s", i+1, testCase.ServerTypeDescription), 1689 func(t *testing.T) { 1690 c := &check{t, testCase.serverType} 1691 suite := testCase 1692 1693 openIDServer := os.Getenv(EnvTestOpenIDServer) 1694 if openIDServer == "" { 1695 c.Skip("Skipping OpenID test as no OpenID server is provided.") 1696 } 1697 1698 suite.SetUpSuite(c) 1699 suite.SetUpOpenID(c, openIDServer, "") 1700 suite.TestOpenIDSTS(c) 1701 suite.TestOpenIDSTSDurationSeconds(c) 1702 suite.TestOpenIDServiceAcc(c) 1703 suite.TestOpenIDSTSAddUser(c) 1704 suite.TearDownSuite(c) 1705 }, 1706 ) 1707 } 1708 } 1709 1710 func TestIAMWithOpenIDWithRolePolicyServerSuite(t *testing.T) { 1711 for i, testCase := range iamTestSuites { 1712 t.Run( 1713 fmt.Sprintf("Test: %d, ServerType: %s", i+1, testCase.ServerTypeDescription), 1714 func(t *testing.T) { 1715 c := &check{t, testCase.serverType} 1716 suite := testCase 1717 1718 openIDServer := os.Getenv(EnvTestOpenIDServer) 1719 if openIDServer == "" { 1720 c.Skip("Skipping OpenID test as no OpenID server is provided.") 1721 } 1722 1723 suite.SetUpSuite(c) 1724 suite.SetUpOpenID(c, openIDServer, "readwrite") 1725 suite.TestOpenIDSTSWithRolePolicy(c, testRoleARNs[0], testRoleMap[testRoleARNs[0]]) 1726 suite.TestOpenIDServiceAccWithRolePolicy(c) 1727 suite.TearDownSuite(c) 1728 }, 1729 ) 1730 } 1731 } 1732 1733 func TestIAMWithOpenIDWithRolePolicyWithPolicyVariablesServerSuite(t *testing.T) { 1734 for i, testCase := range iamTestSuites { 1735 t.Run( 1736 fmt.Sprintf("Test: %d, ServerType: %s", i+1, testCase.ServerTypeDescription), 1737 func(t *testing.T) { 1738 c := &check{t, testCase.serverType} 1739 suite := testCase 1740 1741 openIDServer := os.Getenv(EnvTestOpenIDServer) 1742 if openIDServer == "" { 1743 c.Skip("Skipping OpenID test as no OpenID server is provided.") 1744 } 1745 1746 suite.SetUpSuite(c) 1747 suite.SetUpOpenID(c, openIDServer, "projecta,projectb,projectaorb") 1748 suite.TestOpenIDSTSWithRolePolicyWithPolVar(c, testRoleARNs[0], testRoleMap[testRoleARNs[0]]) 1749 suite.TearDownSuite(c) 1750 }, 1751 ) 1752 } 1753 } 1754 1755 const ( 1756 testRoleARN = "arn:minio:iam:::role/nOybJqMNzNmroqEKq5D0EUsRZw0" 1757 testRoleARN2 = "arn:minio:iam:::role/domXb70kze7Ugc1SaxaeFchhLP4" 1758 ) 1759 1760 var ( 1761 testRoleARNs = []string{testRoleARN, testRoleARN2} 1762 1763 // Load test client app and test role mapping depending on test 1764 // environment. 1765 testClientApps, testRoleMap = func() ([]OpenIDClientAppParams, map[string]OpenIDClientAppParams) { 1766 var apps []OpenIDClientAppParams 1767 m := map[string]OpenIDClientAppParams{} 1768 1769 openIDServer := os.Getenv(EnvTestOpenIDServer) 1770 if openIDServer != "" { 1771 apps = append(apps, OpenIDClientAppParams{ 1772 ClientID: "minio-client-app", 1773 ClientSecret: "minio-client-app-secret", 1774 ProviderURL: openIDServer, 1775 RedirectURL: "http://127.0.0.1:10000/oauth_callback", 1776 }) 1777 m[testRoleARNs[len(apps)-1]] = apps[len(apps)-1] 1778 } 1779 1780 openIDServer2 := os.Getenv(EnvTestOpenIDServer2) 1781 if openIDServer2 != "" { 1782 apps = append(apps, OpenIDClientAppParams{ 1783 ClientID: "minio-client-app-2", 1784 ClientSecret: "minio-client-app-secret-2", 1785 ProviderURL: openIDServer2, 1786 RedirectURL: "http://127.0.0.1:10000/oauth_callback", 1787 }) 1788 m[testRoleARNs[len(apps)-1]] = apps[len(apps)-1] 1789 } 1790 1791 return apps, m 1792 }() 1793 ) 1794 1795 func (s *TestSuiteIAM) TestOpenIDSTSWithRolePolicy(c *check, roleARN string, clientApp OpenIDClientAppParams) { 1796 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 1797 defer cancel() 1798 1799 bucket := getRandomBucketName() 1800 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 1801 if err != nil { 1802 c.Fatalf("bucket create error: %v", err) 1803 } 1804 1805 // Generate web identity JWT by interacting with OpenID IDP. 1806 token, err := MockOpenIDTestUserInteraction(ctx, clientApp, "dillon@example.io", "dillon") 1807 if err != nil { 1808 c.Fatalf("mock user err: %v", err) 1809 } 1810 1811 // Generate STS credential. 1812 webID := cr.STSWebIdentity{ 1813 Client: s.TestSuiteCommon.client, 1814 STSEndpoint: s.endPoint, 1815 GetWebIDTokenExpiry: func() (*cr.WebIdentityToken, error) { 1816 return &cr.WebIdentityToken{ 1817 Token: token, 1818 }, nil 1819 }, 1820 RoleARN: roleARN, 1821 } 1822 1823 value, err := webID.Retrieve() 1824 if err != nil { 1825 c.Fatalf("Expected to generate STS creds, got err: %#v", err) 1826 } 1827 // fmt.Printf("value: %#v\n", value) 1828 1829 minioClient, err := minio.New(s.endpoint, &minio.Options{ 1830 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 1831 Secure: s.secure, 1832 Transport: s.TestSuiteCommon.client.Transport, 1833 }) 1834 if err != nil { 1835 c.Fatalf("Error initializing client: %v", err) 1836 } 1837 1838 // Validate that the client from sts creds can access the bucket. 1839 c.mustListObjects(ctx, minioClient, bucket) 1840 } 1841 1842 func (s *TestSuiteIAM) TestOpenIDServiceAccWithRolePolicy(c *check) { 1843 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 1844 defer cancel() 1845 1846 bucket := getRandomBucketName() 1847 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 1848 if err != nil { 1849 c.Fatalf("bucket create error: %v", err) 1850 } 1851 1852 // Generate web identity STS token by interacting with OpenID IDP. 1853 token, err := MockOpenIDTestUserInteraction(ctx, testAppParams, "dillon@example.io", "dillon") 1854 if err != nil { 1855 c.Fatalf("mock user err: %v", err) 1856 } 1857 1858 webID := cr.STSWebIdentity{ 1859 Client: s.TestSuiteCommon.client, 1860 STSEndpoint: s.endPoint, 1861 GetWebIDTokenExpiry: func() (*cr.WebIdentityToken, error) { 1862 return &cr.WebIdentityToken{ 1863 Token: token, 1864 }, nil 1865 }, 1866 RoleARN: testRoleARN, 1867 } 1868 1869 value, err := webID.Retrieve() 1870 if err != nil { 1871 c.Fatalf("Expected to generate STS creds, got err: %#v", err) 1872 } 1873 1874 // Create an madmin client with user creds 1875 userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{ 1876 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 1877 Secure: s.secure, 1878 }) 1879 if err != nil { 1880 c.Fatalf("Err creating user admin client: %v", err) 1881 } 1882 userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport) 1883 1884 // Create svc acc 1885 cr := c.mustCreateSvcAccount(ctx, value.AccessKeyID, userAdmClient) 1886 1887 // 1. Check that svc account appears in listing 1888 c.assertSvcAccAppearsInListing(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey) 1889 1890 // 2. Check that svc account info can be queried 1891 c.assertSvcAccInfoQueryable(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey, true) 1892 1893 // 3. Check S3 access 1894 c.assertSvcAccS3Access(ctx, s, cr, bucket) 1895 1896 // 5. Check that service account can be deleted. 1897 c.assertSvcAccDeletion(ctx, s, userAdmClient, value.AccessKeyID, bucket) 1898 } 1899 1900 // Constants for Policy Variables test. 1901 var ( 1902 policyProjectA = `{ 1903 "Version": "2012-10-17", 1904 "Statement": [ 1905 { 1906 "Effect": "Allow", 1907 "Action": [ 1908 "s3:GetBucketLocation", 1909 "s3:ListAllMyBuckets" 1910 ], 1911 "Resource": "arn:aws:s3:::*" 1912 }, 1913 { 1914 "Effect": "Allow", 1915 "Action": "s3:*", 1916 "Resource": [ 1917 "arn:aws:s3:::projecta", 1918 "arn:aws:s3:::projecta/*" 1919 ], 1920 "Condition": { 1921 "ForAnyValue:StringEquals": { 1922 "jwt:groups": [ 1923 "projecta" 1924 ] 1925 } 1926 } 1927 } 1928 ] 1929 } 1930 ` 1931 policyProjectB = `{ 1932 "Version": "2012-10-17", 1933 "Statement": [ 1934 { 1935 "Effect": "Allow", 1936 "Action": [ 1937 "s3:GetBucketLocation", 1938 "s3:ListAllMyBuckets" 1939 ], 1940 "Resource": "arn:aws:s3:::*" 1941 }, 1942 { 1943 "Effect": "Allow", 1944 "Action": "s3:*", 1945 "Resource": [ 1946 "arn:aws:s3:::projectb", 1947 "arn:aws:s3:::projectb/*" 1948 ], 1949 "Condition": { 1950 "ForAnyValue:StringEquals": { 1951 "jwt:groups": [ 1952 "projectb" 1953 ] 1954 } 1955 } 1956 } 1957 ] 1958 } 1959 ` 1960 policyProjectAorB = `{ 1961 "Version": "2012-10-17", 1962 "Statement": [ 1963 { 1964 "Effect": "Allow", 1965 "Action": [ 1966 "s3:GetBucketLocation", 1967 "s3:ListAllMyBuckets" 1968 ], 1969 "Resource": "arn:aws:s3:::*" 1970 }, 1971 { 1972 "Effect": "Allow", 1973 "Action": "s3:*", 1974 "Resource": [ 1975 "arn:aws:s3:::projectaorb", 1976 "arn:aws:s3:::projectaorb/*" 1977 ], 1978 "Condition": { 1979 "ForAnyValue:StringEquals": { 1980 "jwt:groups": [ 1981 "projecta", 1982 "projectb" 1983 ] 1984 } 1985 } 1986 } 1987 ] 1988 }` 1989 1990 policyProjectsMap = map[string]string{ 1991 // grants access to bucket `projecta` if user is in group `projecta` 1992 "projecta": policyProjectA, 1993 1994 // grants access to bucket `projectb` if user is in group `projectb` 1995 "projectb": policyProjectB, 1996 1997 // grants access to bucket `projectaorb` if user is in either group 1998 // `projecta` or `projectb` 1999 "projectaorb": policyProjectAorB, 2000 } 2001 ) 2002 2003 func (s *TestSuiteIAM) TestOpenIDSTSWithRolePolicyWithPolVar(c *check, roleARN string, clientApp OpenIDClientAppParams) { 2004 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 2005 defer cancel() 2006 2007 // Create project buckets 2008 buckets := []string{"projecta", "projectb", "projectaorb", "other"} 2009 for _, bucket := range buckets { 2010 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 2011 if err != nil { 2012 c.Fatalf("bucket create error: %v", err) 2013 } 2014 } 2015 2016 // Create policies 2017 for polName, polContent := range policyProjectsMap { 2018 err := s.adm.AddCannedPolicy(ctx, polName, []byte(polContent)) 2019 if err != nil { 2020 c.Fatalf("policy add error: %v", err) 2021 } 2022 } 2023 2024 makeSTSClient := func(user, password string) *minio.Client { 2025 // Generate web identity JWT by interacting with OpenID IDP. 2026 token, err := MockOpenIDTestUserInteraction(ctx, clientApp, user, password) 2027 if err != nil { 2028 c.Fatalf("mock user err: %v", err) 2029 } 2030 2031 // Generate STS credential. 2032 webID := cr.STSWebIdentity{ 2033 Client: s.TestSuiteCommon.client, 2034 STSEndpoint: s.endPoint, 2035 GetWebIDTokenExpiry: func() (*cr.WebIdentityToken, error) { 2036 return &cr.WebIdentityToken{ 2037 Token: token, 2038 }, nil 2039 }, 2040 RoleARN: roleARN, 2041 } 2042 2043 value, err := webID.Retrieve() 2044 if err != nil { 2045 c.Fatalf("Expected to generate STS creds, got err: %#v", err) 2046 } 2047 // fmt.Printf("value: %#v\n", value) 2048 2049 minioClient, err := minio.New(s.endpoint, &minio.Options{ 2050 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 2051 Secure: s.secure, 2052 Transport: s.TestSuiteCommon.client.Transport, 2053 }) 2054 if err != nil { 2055 c.Fatalf("Error initializing client: %v", err) 2056 } 2057 2058 return minioClient 2059 } 2060 2061 // user dillon's groups attribute is ["projecta", "projectb"] 2062 dillonClient := makeSTSClient("dillon@example.io", "dillon") 2063 // Validate client's permissions 2064 c.mustListBuckets(ctx, dillonClient) 2065 c.mustListObjects(ctx, dillonClient, "projecta") 2066 c.mustListObjects(ctx, dillonClient, "projectb") 2067 c.mustListObjects(ctx, dillonClient, "projectaorb") 2068 c.mustNotListObjects(ctx, dillonClient, "other") 2069 2070 // this user's groups attribute is ["projectb"] 2071 lisaClient := makeSTSClient("ejones@example.io", "liza") 2072 // Validate client's permissions 2073 c.mustListBuckets(ctx, lisaClient) 2074 c.mustNotListObjects(ctx, lisaClient, "projecta") 2075 c.mustListObjects(ctx, lisaClient, "projectb") 2076 c.mustListObjects(ctx, lisaClient, "projectaorb") 2077 c.mustNotListObjects(ctx, lisaClient, "other") 2078 } 2079 2080 func TestIAMWithOpenIDMultipleConfigsValidation1(t *testing.T) { 2081 openIDServer := os.Getenv(EnvTestOpenIDServer) 2082 openIDServer2 := os.Getenv(EnvTestOpenIDServer2) 2083 if openIDServer == "" || openIDServer2 == "" { 2084 t.Skip("Skipping OpenID test as enough OpenID servers are not provided.") 2085 } 2086 testApps := testClientApps 2087 2088 rolePolicies := []string{ 2089 "", // Treated as claim-based provider as no role policy is given. 2090 "readwrite", 2091 } 2092 2093 for i, testCase := range iamTestSuites { 2094 t.Run( 2095 fmt.Sprintf("Test: %d, ServerType: %s", i+1, testCase.ServerTypeDescription), 2096 func(t *testing.T) { 2097 c := &check{t, testCase.serverType} 2098 suite := testCase 2099 2100 suite.SetUpSuite(c) 2101 defer suite.TearDownSuite(c) 2102 2103 err := suite.SetUpOpenIDs(c, testApps, rolePolicies) 2104 if err != nil { 2105 c.Fatalf("config with 1 claim based and 1 role based provider should pass but got: %v", err) 2106 } 2107 }, 2108 ) 2109 } 2110 } 2111 2112 func TestIAMWithOpenIDMultipleConfigsValidation2(t *testing.T) { 2113 openIDServer := os.Getenv(EnvTestOpenIDServer) 2114 openIDServer2 := os.Getenv(EnvTestOpenIDServer2) 2115 if openIDServer == "" || openIDServer2 == "" { 2116 t.Skip("Skipping OpenID test as enough OpenID servers are not provided.") 2117 } 2118 testApps := testClientApps 2119 2120 rolePolicies := []string{ 2121 "", // Treated as claim-based provider as no role policy is given. 2122 "", // Treated as claim-based provider as no role policy is given. 2123 } 2124 2125 for i, testCase := range iamTestSuites { 2126 t.Run( 2127 fmt.Sprintf("Test: %d, ServerType: %s", i+1, testCase.ServerTypeDescription), 2128 func(t *testing.T) { 2129 c := &check{t, testCase.serverType} 2130 suite := testCase 2131 2132 suite.SetUpSuite(c) 2133 defer suite.TearDownSuite(c) 2134 2135 err := suite.SetUpOpenIDs(c, testApps, rolePolicies) 2136 if err == nil { 2137 c.Fatalf("config with 2 claim based provider should fail") 2138 } 2139 }, 2140 ) 2141 } 2142 } 2143 2144 func TestIAMWithOpenIDWithMultipleRolesServerSuite(t *testing.T) { 2145 openIDServer := os.Getenv(EnvTestOpenIDServer) 2146 openIDServer2 := os.Getenv(EnvTestOpenIDServer2) 2147 if openIDServer == "" || openIDServer2 == "" { 2148 t.Skip("Skipping OpenID test as enough OpenID servers are not provided.") 2149 } 2150 testApps := testClientApps 2151 2152 rolePolicies := []string{ 2153 "consoleAdmin", 2154 "readwrite", 2155 } 2156 2157 for i, testCase := range iamTestSuites { 2158 t.Run( 2159 fmt.Sprintf("Test: %d, ServerType: %s", i+1, testCase.ServerTypeDescription), 2160 func(t *testing.T) { 2161 c := &check{t, testCase.serverType} 2162 suite := testCase 2163 2164 suite.SetUpSuite(c) 2165 err := suite.SetUpOpenIDs(c, testApps, rolePolicies) 2166 if err != nil { 2167 c.Fatalf("Error setting up openid providers for tests: %v", err) 2168 } 2169 suite.TestOpenIDSTSWithRolePolicy(c, testRoleARNs[0], testRoleMap[testRoleARNs[0]]) 2170 suite.TestOpenIDSTSWithRolePolicy(c, testRoleARNs[1], testRoleMap[testRoleARNs[1]]) 2171 suite.TestOpenIDServiceAccWithRolePolicy(c) 2172 suite.TearDownSuite(c) 2173 }, 2174 ) 2175 } 2176 } 2177 2178 // Access Management Plugin tests 2179 func TestIAM_AMPWithOpenIDWithMultipleRolesServerSuite(t *testing.T) { 2180 openIDServer := os.Getenv(EnvTestOpenIDServer) 2181 openIDServer2 := os.Getenv(EnvTestOpenIDServer2) 2182 if openIDServer == "" || openIDServer2 == "" { 2183 t.Skip("Skipping OpenID test as enough OpenID servers are not provided.") 2184 } 2185 testApps := testClientApps 2186 2187 rolePolicies := []string{ 2188 "consoleAdmin", 2189 "readwrite", 2190 } 2191 2192 for i, testCase := range iamTestSuites { 2193 t.Run( 2194 fmt.Sprintf("Test: %d, ServerType: %s", i+1, testCase.ServerTypeDescription), 2195 func(t *testing.T) { 2196 c := &check{t, testCase.serverType} 2197 suite := testCase 2198 2199 suite.SetUpSuite(c) 2200 defer suite.TearDownSuite(c) 2201 2202 err := suite.SetUpOpenIDs(c, testApps, rolePolicies) 2203 if err != nil { 2204 c.Fatalf("Error setting up openid providers for tests: %v", err) 2205 } 2206 2207 suite.SetUpAccMgmtPlugin(c) 2208 2209 suite.TestOpenIDSTSWithRolePolicyUnderAMP(c, testRoleARNs[0], testRoleMap[testRoleARNs[0]]) 2210 suite.TestOpenIDSTSWithRolePolicyUnderAMP(c, testRoleARNs[1], testRoleMap[testRoleARNs[1]]) 2211 suite.TestOpenIDServiceAccWithRolePolicyUnderAMP(c) 2212 }, 2213 ) 2214 } 2215 } 2216 2217 func (s *TestSuiteIAM) TestOpenIDSTSWithRolePolicyUnderAMP(c *check, roleARN string, clientApp OpenIDClientAppParams) { 2218 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 2219 defer cancel() 2220 2221 bucket := getRandomBucketName() 2222 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 2223 if err != nil { 2224 c.Fatalf("bucket create error: %v", err) 2225 } 2226 2227 // Generate web identity JWT by interacting with OpenID IDP. 2228 token, err := MockOpenIDTestUserInteraction(ctx, clientApp, "dillon@example.io", "dillon") 2229 if err != nil { 2230 c.Fatalf("mock user err: %v", err) 2231 } 2232 2233 // Generate STS credential. 2234 webID := cr.STSWebIdentity{ 2235 Client: s.TestSuiteCommon.client, 2236 STSEndpoint: s.endPoint, 2237 GetWebIDTokenExpiry: func() (*cr.WebIdentityToken, error) { 2238 return &cr.WebIdentityToken{ 2239 Token: token, 2240 }, nil 2241 }, 2242 RoleARN: roleARN, 2243 } 2244 2245 value, err := webID.Retrieve() 2246 if err != nil { 2247 c.Fatalf("Expected to generate STS creds, got err: %#v", err) 2248 } 2249 // fmt.Printf("value: %#v\n", value) 2250 2251 minioClient, err := minio.New(s.endpoint, &minio.Options{ 2252 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 2253 Secure: s.secure, 2254 Transport: s.TestSuiteCommon.client.Transport, 2255 }) 2256 if err != nil { 2257 c.Fatalf("Error initializing client: %v", err) 2258 } 2259 2260 // Validate that the client from sts creds can access the bucket. 2261 c.mustListObjects(ctx, minioClient, bucket) 2262 2263 // Validate that the client from STS creds cannot upload any object as 2264 // it is denied by the plugin. 2265 c.mustNotUpload(ctx, minioClient, bucket) 2266 } 2267 2268 func (s *TestSuiteIAM) TestOpenIDServiceAccWithRolePolicyUnderAMP(c *check) { 2269 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 2270 defer cancel() 2271 2272 bucket := getRandomBucketName() 2273 err := s.client.MakeBucket(ctx, bucket, minio.MakeBucketOptions{}) 2274 if err != nil { 2275 c.Fatalf("bucket create error: %v", err) 2276 } 2277 2278 // Generate web identity STS token by interacting with OpenID IDP. 2279 token, err := MockOpenIDTestUserInteraction(ctx, testAppParams, "dillon@example.io", "dillon") 2280 if err != nil { 2281 c.Fatalf("mock user err: %v", err) 2282 } 2283 2284 webID := cr.STSWebIdentity{ 2285 Client: s.TestSuiteCommon.client, 2286 STSEndpoint: s.endPoint, 2287 GetWebIDTokenExpiry: func() (*cr.WebIdentityToken, error) { 2288 return &cr.WebIdentityToken{ 2289 Token: token, 2290 }, nil 2291 }, 2292 RoleARN: testRoleARN, 2293 } 2294 2295 value, err := webID.Retrieve() 2296 if err != nil { 2297 c.Fatalf("Expected to generate STS creds, got err: %#v", err) 2298 } 2299 2300 // Create an madmin client with user creds 2301 userAdmClient, err := madmin.NewWithOptions(s.endpoint, &madmin.Options{ 2302 Creds: cr.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), 2303 Secure: s.secure, 2304 }) 2305 if err != nil { 2306 c.Fatalf("Err creating user admin client: %v", err) 2307 } 2308 userAdmClient.SetCustomTransport(s.TestSuiteCommon.client.Transport) 2309 2310 // Create svc acc 2311 cr := c.mustCreateSvcAccount(ctx, value.AccessKeyID, userAdmClient) 2312 2313 // 1. Check that svc account appears in listing 2314 c.assertSvcAccAppearsInListing(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey) 2315 2316 // 2. Check that svc account info can be queried 2317 c.assertSvcAccInfoQueryable(ctx, userAdmClient, value.AccessKeyID, cr.AccessKey, true) 2318 2319 // 3. Check S3 access 2320 c.assertSvcAccS3Access(ctx, s, cr, bucket) 2321 // 3.1 Validate that the client from STS creds cannot upload any object as 2322 // it is denied by the plugin. 2323 c.mustNotUpload(ctx, s.getUserClient(c, cr.AccessKey, cr.SecretKey, ""), bucket) 2324 2325 // Check that session policies do not apply - as policy enforcement is 2326 // delegated to plugin. 2327 { 2328 svcAK, svcSK := mustGenerateCredentials(c) 2329 2330 // This policy does not allow listing objects. 2331 policyBytes := []byte(fmt.Sprintf(`{ 2332 "Version": "2012-10-17", 2333 "Statement": [ 2334 { 2335 "Effect": "Allow", 2336 "Action": [ 2337 "s3:PutObject", 2338 "s3:GetObject" 2339 ], 2340 "Resource": [ 2341 "arn:aws:s3:::%s/*" 2342 ] 2343 } 2344 ] 2345 }`, bucket)) 2346 cr, err := userAdmClient.AddServiceAccount(ctx, madmin.AddServiceAccountReq{ 2347 Policy: policyBytes, 2348 TargetUser: value.AccessKeyID, 2349 AccessKey: svcAK, 2350 SecretKey: svcSK, 2351 }) 2352 if err != nil { 2353 c.Fatalf("Unable to create svc acc: %v", err) 2354 } 2355 svcClient := s.getUserClient(c, cr.AccessKey, cr.SecretKey, "") 2356 // Though the attached policy does not allow listing, it will be 2357 // ignored because the plugin allows it. 2358 c.mustListObjects(ctx, svcClient, bucket) 2359 } 2360 2361 // 4. Check that service account's secret key and account status can be 2362 // updated. 2363 c.assertSvcAccSecretKeyAndStatusUpdate(ctx, s, userAdmClient, value.AccessKeyID, bucket) 2364 2365 // 5. Check that service account can be deleted. 2366 c.assertSvcAccDeletion(ctx, s, userAdmClient, value.AccessKeyID, bucket) 2367 }