mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-04 12:01:23 +02:00
Backport VAULT-43374: adding aws sdk tests into ce/main (#14089)
* VAULT-43374: adding aws sdk tests (#13547) * adding aws sdk tests * testing pipeline * fixing copyright * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * rebasing * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * cleaning up * cleaning up * cleaning up * cleaning up * cleaning up * testing pipeline * testing pipeline * testing pipeline * fixing lint error * addressing comments * addressing comments * fixing error * testing pipeline * testing pipeline * addressing comments in helpers.go * testing pipeline * updating delete user logic * updating delete user logic * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * addressing comments * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * testing pipeline * undo testing * Go mod --------- Co-authored-by: Tin Vo <tintvo08@gmail.com> Co-authored-by: LT Carbonell <ltcarbonell@pm.me>
This commit is contained in:
parent
7f0c0dbdad
commit
1fc1b56e22
11
go.mod
11
go.mod
@ -54,6 +54,7 @@ require (
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
|
||||
github.com/aws/aws-sdk-go v1.55.8
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.14
|
||||
github.com/aws/aws-sdk-go-v2/service/iam v1.53.8
|
||||
github.com/cenkalti/backoff/v4 v4.3.0
|
||||
github.com/chrismalek/oktasdk-go v0.0.0-20181212195951-3430665dfaa0
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.3.8
|
||||
@ -333,14 +334,14 @@ require (
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/apache/arrow-go/v18 v18.4.0 // indirect
|
||||
github.com/avast/retry-go/v4 v4.6.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.6
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.6.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.297.0 // indirect
|
||||
@ -352,8 +353,8 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect
|
||||
github.com/aws/smithy-go v1.24.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10
|
||||
github.com/aws/smithy-go v1.25.0 // indirect
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
|
||||
github.com/benbjohnson/immutable v0.4.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
|
||||
18
go.sum
18
go.sum
@ -209,8 +209,8 @@ github.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBea
|
||||
github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=
|
||||
github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.6 h1:1AX0AthnBQzMx1vbmir3Y4WsnJgiydmnJjiLu+LvXOg=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.6/go.mod h1:dy0UzBIfwSeot4grGvY1AqFWN5zgziMmWGzysDnHFcQ=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.14 h1:opVIRo/ZbbI8OIqSOKmpFaY7IwfFUOCCXBsUpJOwDdI=
|
||||
@ -223,10 +223,10 @@ github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.6.21 h1:HFn8sVT87KWnGs2Q2gO/brP
|
||||
github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.6.21/go.mod h1:BGZ/K6gLGJt8K36j6gcsD7WVxmWt0MGBYtr57iLweio=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.13 h1:uMC4oL6G3MNhodo358QEqSDjrgvzV3TUQ58nyQSGq2E=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.13/go.mod h1:Cer86AE2686DvVUe57LPve3jUBmbujuaonSX8pNzGgw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 h1:GmLa5Kw1ESqtFpXsx5MmC84QWa/ZrLZvlJGa2y+4kcQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22/go.mod h1:6sW9iWm9DK9YRpRGga/qzrzNLgKpT2cIxb7Vo2eNOp0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 h1:dY4kWZiSaXIzxnKlj17nHnBcXXBfac6UlsAx2qL6XrU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22/go.mod h1:KIpEUx0JuRZLO7U6cbV204cWAEco2iC3l061IxlwLtI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ=
|
||||
@ -235,6 +235,8 @@ github.com/aws/aws-sdk-go-v2/service/ec2 v1.297.0 h1:A+7NViqbMUCoTQFWjbSXdbzE4K5
|
||||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.297.0/go.mod h1:R+2BNtUfTfhPY0RH18oL02q116bakeBWjanrbnVBqkM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ecs v1.77.0 h1:g3RYQmK6uRU5kOuwDthemuiiTbmwyGd8Wzf+k7cYWtk=
|
||||
github.com/aws/aws-sdk-go-v2/service/ecs v1.77.0/go.mod h1:QkWmubOYmjj3cHn7A4CoUU7BKJhVeo39Gp6NH7IyhZw=
|
||||
github.com/aws/aws-sdk-go-v2/service/iam v1.53.8 h1:p0oB4eZfBfBAOasnKvHJOlNcuHVE/ieuWs7uIZgQlyQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/iam v1.53.8/go.mod h1:epCaPnGVdiX5ra1lHPfRkVuiQGxrdY8bRI2FBJU+6ok=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 h1:JRaIgADQS/U6uXDqlPiefP32yXTda7Kqfx+LgspooZM=
|
||||
@ -253,8 +255,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 h1:dzztQ1YmfPrxdrOiuZRMF6f
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBUdErbMnAFFp12Lm/U=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw=
|
||||
github.com/aws/smithy-go v1.24.3 h1:XgOAaUgx+HhVBoP4v8n6HCQoTRDhoMghKqw4LNHsDNg=
|
||||
github.com/aws/smithy-go v1.24.3/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
github.com/aws/smithy-go v1.25.0 h1:Sz/XJ64rwuiKtB6j98nDIPyYrV1nVNJ4YU74gttcl5U=
|
||||
github.com/aws/smithy-go v1.25.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA=
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
|
||||
github.com/benbjohnson/immutable v0.4.0 h1:CTqXbEerYso8YzVPxmWxh2gnoRQbbB9X1quUC8+vGZA=
|
||||
|
||||
@ -26,6 +26,14 @@ func (s *Session) MustRead(path string) *api.Secret {
|
||||
return secret
|
||||
}
|
||||
|
||||
func (s *Session) MustList(path string) *api.Secret {
|
||||
s.t.Helper()
|
||||
|
||||
secret, err := s.Client.Logical().List(path)
|
||||
require.NoError(s.t, err)
|
||||
return secret
|
||||
}
|
||||
|
||||
// MustReadRequired is a stricter version of MustRead that fails if a 404/nil is returned
|
||||
func (s *Session) MustReadRequired(path string) *api.Secret {
|
||||
s.t.Helper()
|
||||
|
||||
265
vault/external_tests/blackbox/plugins/aws/helpers.go
Normal file
265
vault/external_tests/blackbox/plugins/aws/helpers.go
Normal file
@ -0,0 +1,265 @@
|
||||
// Copyright IBM Corp. 2025, 2026
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package aws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/service/iam"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sts"
|
||||
)
|
||||
|
||||
// getPolicyArnByName returns the ARN for a policy with the given name.
|
||||
func getPolicyArnByName(ctx context.Context, iamClient *iam.Client, policyName string) (string, error) {
|
||||
paginator := iam.NewListPoliciesPaginator(iamClient, &iam.ListPoliciesInput{Scope: "All"})
|
||||
for paginator.HasMorePages() {
|
||||
page, err := paginator.NextPage(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, p := range page.Policies {
|
||||
if aws.ToString(p.PolicyName) == policyName {
|
||||
return aws.ToString(p.Arn), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("policy %s not found", policyName)
|
||||
}
|
||||
|
||||
// getRoleArnByName returns the ARN for a role with the given name.
|
||||
func getRoleArnByName(ctx context.Context, iamClient *iam.Client, roleName string) (string, error) {
|
||||
paginator := iam.NewListRolesPaginator(iamClient, &iam.ListRolesInput{})
|
||||
for paginator.HasMorePages() {
|
||||
page, err := paginator.NextPage(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, r := range page.Roles {
|
||||
if aws.ToString(r.RoleName) == roleName {
|
||||
return aws.ToString(r.Arn), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("role %s not found", roleName)
|
||||
}
|
||||
|
||||
// createTestIAMUser creates a new IAM user with a unique name, attaches the DemoUser policy, and returns the user/access key info and AWS region.
|
||||
func createTestIAMUser(t *testing.T) (
|
||||
userName string,
|
||||
accessKeyID string,
|
||||
secretAccessKey string,
|
||||
demoUserPolicyArn string,
|
||||
assumedRoleArn string,
|
||||
awsRegion string,
|
||||
) {
|
||||
t.Helper()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
cfg, err := config.LoadDefaultConfig(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load AWS config: %v", err)
|
||||
}
|
||||
awsRegion = cfg.Region
|
||||
if awsRegion == "" {
|
||||
t.Fatalf("AWS region is empty in config")
|
||||
}
|
||||
iamClient := iam.NewFromConfig(cfg)
|
||||
stsClient := sts.NewFromConfig(cfg)
|
||||
|
||||
// Get current AWS account identity (for unique name)
|
||||
caller, err := stsClient.GetCallerIdentity(ctx, &sts.GetCallerIdentityInput{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get caller identity: %v", err)
|
||||
}
|
||||
accountID := aws.ToString(caller.Account)
|
||||
|
||||
// Generate a random hex suffix for uniqueness
|
||||
const randomSuffixByteLength = 4
|
||||
suffix := make([]byte, randomSuffixByteLength)
|
||||
if _, err := rand.Read(suffix); err != nil {
|
||||
t.Fatalf("failed to generate random suffix: %v", err)
|
||||
}
|
||||
hexSuffix := hex.EncodeToString(suffix)
|
||||
userName = fmt.Sprintf("demo-GitHubActions-%s-%s", accountID, hexSuffix)
|
||||
|
||||
// Lookup DemoUser policy ARN
|
||||
demoUserPolicyArn, err = getPolicyArnByName(ctx, iamClient, "DemoUser")
|
||||
if err != nil {
|
||||
t.Fatalf("DemoUser policy not found: %v", err)
|
||||
}
|
||||
|
||||
// Lookup vault-assumed-role-credentials-demo role ARN
|
||||
assumedRoleArn, err = getRoleArnByName(ctx, iamClient, "vault-assumed-role-credentials-demo")
|
||||
if err != nil {
|
||||
t.Fatalf("vault-assumed-role-credentials-demo role not found: %v", err)
|
||||
}
|
||||
|
||||
// Create IAM user
|
||||
_, err = iamClient.CreateUser(ctx, &iam.CreateUserInput{
|
||||
UserName: aws.String(userName),
|
||||
PermissionsBoundary: aws.String(demoUserPolicyArn),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create IAM user: %v", err)
|
||||
}
|
||||
|
||||
// Attach policy to user
|
||||
_, err = iamClient.AttachUserPolicy(ctx, &iam.AttachUserPolicyInput{
|
||||
UserName: aws.String(userName),
|
||||
PolicyArn: aws.String(demoUserPolicyArn),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to attach policy: %v", err)
|
||||
}
|
||||
|
||||
// Create access key
|
||||
keyOut, err := iamClient.CreateAccessKey(ctx, &iam.CreateAccessKeyInput{
|
||||
UserName: aws.String(userName),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create access key: %v", err)
|
||||
}
|
||||
accessKeyID = aws.ToString(keyOut.AccessKey.AccessKeyId)
|
||||
secretAccessKey = aws.ToString(keyOut.AccessKey.SecretAccessKey)
|
||||
|
||||
// IAM is eventually consistent; wait briefly before verifying the user is readable.
|
||||
t.Logf("Verifying IAM user %s exists...", userName)
|
||||
waitTime := 10 * time.Second
|
||||
verifyDeadline := time.Now().Add(waitTime * 2)
|
||||
var lastErr error
|
||||
for time.Now().Before(verifyDeadline) {
|
||||
time.Sleep(waitTime)
|
||||
_, lastErr = iamClient.GetUser(ctx, &iam.GetUserInput{UserName: aws.String(userName)})
|
||||
if lastErr == nil {
|
||||
break
|
||||
}
|
||||
t.Logf("IAM user %q not readable yet; retrying: %v", userName, lastErr)
|
||||
}
|
||||
if lastErr != nil {
|
||||
t.Fatalf("failed to verify IAM user %q: %v", userName, lastErr)
|
||||
}
|
||||
|
||||
return userName, accessKeyID, secretAccessKey, demoUserPolicyArn, assumedRoleArn, awsRegion
|
||||
}
|
||||
|
||||
// getAwsUsernameTemplate returns the username template string for Vault AWS config.
|
||||
func getAwsUsernameTemplate(awsUserName string) string {
|
||||
const prefix = `{{ if (eq .Type "STS") }}{{ printf "`
|
||||
const stsSuffix = `-%s-%s" (random 20) (unix_time) | truncate 32 }}{{ else }}{{ printf "`
|
||||
const iamUserSuffix = `-%s-%s" (unix_time) (random 20) | truncate 60 }}{{ end }}`
|
||||
return prefix + awsUserName + stsSuffix + awsUserName + iamUserSuffix
|
||||
}
|
||||
|
||||
// getAllowDescribeRegionsPolicy returns a policy document allowing ec2:DescribeRegions.
|
||||
func getAllowDescribeRegionsPolicy() string {
|
||||
return `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": ["ec2:DescribeRegions"],
|
||||
"Resource": ["*"]
|
||||
}
|
||||
]
|
||||
}`
|
||||
}
|
||||
|
||||
// deleteIAMUserByAccessKey deletes the IAM user that owns the given access key.
|
||||
func deleteIAMUserByAccessKey(t *testing.T, targetAccessKeyID string) {
|
||||
t.Helper()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
cfg, err := config.LoadDefaultConfig(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load AWS config: %v", err)
|
||||
}
|
||||
iamClient := iam.NewFromConfig(cfg)
|
||||
|
||||
paginator := iam.NewListUsersPaginator(iamClient, &iam.ListUsersInput{})
|
||||
for paginator.HasMorePages() {
|
||||
page, err := paginator.NextPage(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to list IAM users: %v", err)
|
||||
}
|
||||
for _, user := range page.Users {
|
||||
userName := aws.ToString(user.UserName)
|
||||
if !strings.Contains(userName, "demo-GitHubActions") {
|
||||
continue
|
||||
}
|
||||
// List all access keys for this user
|
||||
keyPaginator := iam.NewListAccessKeysPaginator(iamClient, &iam.ListAccessKeysInput{
|
||||
UserName: &userName,
|
||||
})
|
||||
for keyPaginator.HasMorePages() {
|
||||
keyPage, err := keyPaginator.NextPage(ctx)
|
||||
if err != nil {
|
||||
t.Logf("warning: failed to list access keys for user %q: %v", userName, err)
|
||||
continue
|
||||
}
|
||||
for _, key := range keyPage.AccessKeyMetadata {
|
||||
accessKeyId := aws.ToString(key.AccessKeyId)
|
||||
if accessKeyId == targetAccessKeyID {
|
||||
// Found the user with the target access key. Detach managed policies first,
|
||||
// then delete all access keys, then delete the user.
|
||||
policyPaginator := iam.NewListAttachedUserPoliciesPaginator(iamClient, &iam.ListAttachedUserPoliciesInput{
|
||||
UserName: &userName,
|
||||
})
|
||||
for policyPaginator.HasMorePages() {
|
||||
policyPage, err := policyPaginator.NextPage(ctx)
|
||||
if err != nil {
|
||||
t.Logf("warning: failed to list attached policies for user %q: %v", userName, err)
|
||||
continue
|
||||
}
|
||||
for _, policy := range policyPage.AttachedPolicies {
|
||||
policyArn := aws.ToString(policy.PolicyArn)
|
||||
if _, err := iamClient.DetachUserPolicy(ctx, &iam.DetachUserPolicyInput{
|
||||
UserName: &userName,
|
||||
PolicyArn: &policyArn,
|
||||
}); err != nil {
|
||||
t.Logf("warning: failed to detach policy %q from user %q: %v", policyArn, userName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
keyPaginator2 := iam.NewListAccessKeysPaginator(iamClient, &iam.ListAccessKeysInput{
|
||||
UserName: &userName,
|
||||
})
|
||||
for keyPaginator2.HasMorePages() {
|
||||
keyPage2, err := keyPaginator2.NextPage(ctx)
|
||||
if err != nil {
|
||||
t.Logf("warning: failed to list access keys for cleanup on user %q: %v", userName, err)
|
||||
continue
|
||||
}
|
||||
for _, key2 := range keyPage2.AccessKeyMetadata {
|
||||
accessKeyId2 := aws.ToString(key2.AccessKeyId)
|
||||
if _, err := iamClient.DeleteAccessKey(ctx, &iam.DeleteAccessKeyInput{
|
||||
UserName: &userName,
|
||||
AccessKeyId: &accessKeyId2,
|
||||
}); err != nil {
|
||||
t.Logf("warning: failed to delete access key %q for user %q: %v", accessKeyId2, userName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Delete the user
|
||||
if _, err := iamClient.DeleteUser(ctx, &iam.DeleteUserInput{
|
||||
UserName: &userName,
|
||||
}); err != nil {
|
||||
t.Logf("warning: failed to delete user %q: %v", userName, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
102
vault/external_tests/blackbox/plugins/aws/secrets_aws_test.go
Normal file
102
vault/external_tests/blackbox/plugins/aws/secrets_aws_test.go
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright IBM Corp. 2025, 2026
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
"github.com/hashicorp/vault/sdk/helper/testcluster/blackbox"
|
||||
)
|
||||
|
||||
// TestAWS_GenerateNewUser verifies AWS secrets engine can generate IAM user credentials.
|
||||
func TestAWS_GenerateNewUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
v := blackbox.New(t)
|
||||
|
||||
accessKey := os.Getenv("AWS_ACCESS_KEY_ID")
|
||||
secretKey := os.Getenv("AWS_SECRET_ACCESS_KEY")
|
||||
if accessKey == "" || secretKey == "" {
|
||||
t.Log("AWS credentials not available - skipping AWS secrets engine test")
|
||||
t.Skip("AWS credentials not available - skipping AWS secrets engine test")
|
||||
}
|
||||
|
||||
t.Logf("Creating test IAM user via helpers.go...")
|
||||
userName, tempAccessKeyId, tempSecretAccessKey, demoUserPolicyArn, _, _ := createTestIAMUser(t)
|
||||
t.Logf("Created test IAM user: %s", userName)
|
||||
var newAccessKey string
|
||||
t.Cleanup(func() {
|
||||
if newAccessKey != "" {
|
||||
t.Logf("Cleanup: deleting IAM user created by Vault with access key: %s", newAccessKey)
|
||||
deleteIAMUserByAccessKey(t, newAccessKey)
|
||||
}
|
||||
|
||||
t.Logf("Cleanup: deleting IAM user by initial access key: %s", tempAccessKeyId)
|
||||
deleteIAMUserByAccessKey(t, tempAccessKeyId)
|
||||
})
|
||||
|
||||
path := fmt.Sprintf("aws-test-%d", time.Now().UnixNano())
|
||||
t.Logf("Enabling AWS secrets engine at path: %s", path)
|
||||
v.MustEnableSecretsEngine(path, &api.MountInput{Type: "aws"})
|
||||
|
||||
t.Logf("Configuring AWS secrets engine with root credentials and username template for user: %s", userName)
|
||||
v.MustWrite(fmt.Sprintf("%s/config/root", path), map[string]any{
|
||||
"access_key": tempAccessKeyId,
|
||||
"secret_key": tempSecretAccessKey,
|
||||
"region": "us-east-1",
|
||||
"username_template": getAwsUsernameTemplate(userName),
|
||||
})
|
||||
|
||||
roleName := "aws-enos-role"
|
||||
t.Logf("Creating Vault AWS role: %s", roleName)
|
||||
v.MustWrite(fmt.Sprintf("%s/roles/%s", path, roleName), map[string]any{
|
||||
"credential_type": "iam_user",
|
||||
"permissions_boundary_arn": demoUserPolicyArn,
|
||||
"policy_document": getAllowDescribeRegionsPolicy(),
|
||||
})
|
||||
|
||||
t.Logf("Reading and verifying AWS role configuration for role: %s", roleName)
|
||||
roleResp := v.MustRead(fmt.Sprintf("%s/roles/%s", path, roleName))
|
||||
if roleResp.Data == nil {
|
||||
t.Fatal("Expected to read AWS role configuration")
|
||||
}
|
||||
|
||||
t.Logf("Listing AWS roles at path: %s/roles", path)
|
||||
rolesList := v.MustList(fmt.Sprintf("%s/roles", path))
|
||||
if rolesList == nil || rolesList.Data == nil {
|
||||
t.Fatal("No AWS roles created! (rolesList is nil or Data is nil)")
|
||||
}
|
||||
roleKeys, ok := rolesList.Data["keys"].([]interface{})
|
||||
if !ok || len(roleKeys) == 0 {
|
||||
t.Fatal("No AWS roles created! (rolesList.Data['keys'] is empty or not a slice)")
|
||||
}
|
||||
t.Logf("Found AWS roles: %v", roleKeys)
|
||||
|
||||
t.Logf("Reading root config to verify username template is set correctly")
|
||||
rootUser := v.MustRead(fmt.Sprintf("%s/config/root", path))
|
||||
if rootUser == nil || rootUser.Data == nil {
|
||||
t.Fatalf("Expected to read root config, got nil: %#v", rootUser)
|
||||
}
|
||||
if val, ok := rootUser.Data["username_template"]; !ok || val == nil {
|
||||
t.Fatalf("username_template missing in root config: %#v", rootUser)
|
||||
}
|
||||
|
||||
t.Logf("Generating new credentials for IAM user using role: %s", roleName)
|
||||
newUser := v.MustRead(fmt.Sprintf("%s/creds/%s", path, roleName))
|
||||
if newUser == nil || newUser.Data == nil {
|
||||
t.Fatalf("Failed to generate new credentials for IAM user: %s", roleName)
|
||||
}
|
||||
if val, ok := newUser.Data["access_key"]; !ok || val == nil || val == tempAccessKeyId {
|
||||
t.Fatalf("The new access key is empty or is matching the old one: %v", val)
|
||||
}
|
||||
|
||||
newAccessKey, ok = newUser.Data["access_key"].(string)
|
||||
if !ok || newAccessKey == "" {
|
||||
t.Fatalf("Could not extract access_key from new credentials: %v", newUser.Data["access_key"])
|
||||
}
|
||||
t.Logf("Captured Vault-created access key for cleanup: %s", newAccessKey)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user