Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AWS IAM role: Generate temporary security credentials from Role #350

Merged
merged 26 commits into from
Oct 24, 2019

Conversation

SupriyaKasten
Copy link
Contributor

@SupriyaKasten SupriyaKasten commented Oct 11, 2019

Change Overview

AWS IAM role: Generate temporary security credentials from Role
Earlier, we added the temp credentials to profile directly but since profiles are persistent we do not want them to get invalid after the session expires, hence the change.

Pull request type

Please check the type of change your PR introduces:

  • Work in Progress
  • Refactoring (no functional changes, no api changes)
  • Trivial/Minor
  • Bugfix
  • Feature
  • Documentation

Issues

Test Plan

pkg/kando/location_test.go

 go test -v  -check.v -check.f TestLocationObjectStore
=== RUN   Test
PASS: location_test.go:35: LocationSuite.TestLocationObjectStore	3.188s
OK: 1 passed
--- PASS: Test (3.19s)
PASS
ok  	github.com/kanisterio/kanister/pkg/kando	3.227s

pkg/location/location_test.go

go test 
OK: 1 passed
PASS
ok  	github.com/kanisterio/kanister/pkg/location	2.242s

pkg/restic/restic_test.go

go test
OK: 10 passed
PASS
ok  	github.com/kanisterio/kanister/pkg/restic	0.715s

pkg/secrets/aws_test.go

go test -v  -check.v -check.f TestExtractAWSCredentialsWithSessionToken
=== RUN   Test
PASS: aws_test.go:84: AWSSecretSuite.TestExtractAWSCredentialsWithSessionToken	0.957s
OK: 1 passed
--- PASS: Test (0.96s)
PASS
ok  	github.com/kanisterio/kanister/pkg/secrets	0.980s

pkg/blockstorage/blockstorage_test.go Tested for auto refreshing temp credentials

 go test -timeout 30m
INFO[1021] Update                                        State=creating Volume=vol-006b9ab4e0e4607bb
INFO[1021] Update                                        State=creating Volume=vol-006b9ab4e0e4607bb
INFO[1021] Update                                        State=creating Volume=vol-006b9ab4e0e4607bb
INFO[1021] Update                                        State=creating Volume=vol-006b9ab4e0e4607bb
INFO[1021] Update                                        State=creating Volume=vol-006b9ab4e0e4607bb
INFO[1021] Update                                        State=creating Volume=vol-006b9ab4e0e4607bb
INFO[1022] Update                                        State=creating Volume=vol-006b9ab4e0e4607bb
INFO[1023] Update                                        State=creating Volume=vol-006b9ab4e0e4607bb
INFO[1024] Update                                        State=creating Volume=vol-006b9ab4e0e4607bb
INFO[1027] Volume creation complete                      VolumeID=vol-006b9ab4e0e4607bb
INFO[1027] Snapshotting EBS volume                       volume_id=vol-006b9ab4e0e4607bb
INFO[1028] Snapshot completed                            SnapshotID=snap-0f3a5b6fb056d8e9d
INFO[1029] Ignoring duplicate tag                        RetainedValue= kanister.io/clustername=
INFO[1029] Ignoring duplicate tag                        RetainedValue= kanister.io/version=
INFO[1029] Update                                        State=creating Volume=vol-09b206f4faefb81ca
INFO[1029] Update                                        State=creating Volume=vol-09b206f4faefb81ca
INFO[1029] Update                                        State=creating Volume=vol-09b206f4faefb81ca
INFO[1029] Update                                        State=creating Volume=vol-09b206f4faefb81ca
INFO[1030] Update                                        State=creating Volume=vol-09b206f4faefb81ca
INFO[1030] Update                                        State=creating Volume=vol-09b206f4faefb81ca
INFO[1030] Update                                        State=creating Volume=vol-09b206f4faefb81ca
INFO[1031] Update                                        State=creating Volume=vol-09b206f4faefb81ca
INFO[1032] Update                                        State=creating Volume=vol-09b206f4faefb81ca
INFO[1035] Volume creation complete                      VolumeID=vol-09b206f4faefb81ca
INFO[1035] Deleting EBS Snapshot                         SnapshotID=snap-0f3a5b6fb056d8e9d
INFO[1035] Deleting EBS Snapshot                         SnapshotID=snap-0f3a5b6fb056d8e9d
INFO[1036] Update                                        State=creating Volume=vol-00ac807909b208cac
INFO[1036] Update                                        State=creating Volume=vol-00ac807909b208cac
INFO[1036] Update                                        State=creating Volume=vol-00ac807909b208cac
INFO[1036] Update                                        State=creating Volume=vol-00ac807909b208cac
INFO[1037] Update                                        State=creating Volume=vol-00ac807909b208cac
INFO[1037] Update                                        State=creating Volume=vol-00ac807909b208cac
INFO[1037] Update                                        State=creating Volume=vol-00ac807909b208cac
INFO[1038] Update                                        State=creating Volume=vol-00ac807909b208cac
INFO[1039] Update                                        State=creating Volume=vol-00ac807909b208cac
INFO[1042] Volume creation complete                      VolumeID=vol-00ac807909b208cac
INFO[1043] Update                                        State=creating Volume=vol-07bdf8d7c181f4413
INFO[1043] Update                                        State=creating Volume=vol-07bdf8d7c181f4413
INFO[1043] Update                                        State=creating Volume=vol-07bdf8d7c181f4413
INFO[1043] Update                                        State=creating Volume=vol-07bdf8d7c181f4413
INFO[1044] Update                                        State=creating Volume=vol-07bdf8d7c181f4413
INFO[1044] Update                                        State=creating Volume=vol-07bdf8d7c181f4413
INFO[1044] Update                                        State=creating Volume=vol-07bdf8d7c181f4413
INFO[1045] Update                                        State=creating Volume=vol-07bdf8d7c181f4413
INFO[1046] Update                                        State=creating Volume=vol-07bdf8d7c181f4413
INFO[1049] Volume creation complete                      VolumeID=vol-07bdf8d7c181f4413
INFO[1049] Snapshotting EBS volume                       volume_id=vol-07bdf8d7c181f4413
INFO[1051] Snapshot completed                            SnapshotID=snap-075a9388d739c2c40
INFO[1051] Deleting EBS Snapshot                         SnapshotID=snap-075a9388d739c2c40
INFO[1051] Deleting EBS Snapshot                         SnapshotID=snap-075a9388d739c2c40
OK: 3 passed, 1 skipped
PASS
ok  	github.com/kanisterio/kanister/pkg/blockstorage	1051.606s
  • Manual
  • Unit test
  • E2E

Comment on lines 171 to 199
{
profile: &param.Profile{
Location: v1alpha1.Location{
Type: v1alpha1.LocationTypeS3Compliant,
Endpoint: "endpoint", // Also remove all of the trailing slashes
},
Credential: param.Credential{
Type: param.CredentialTypeSecret,
Secret: &v1.Secret{
Type: "secrets.kanister.io/aws",
Data: map[string][]byte{
"access_key_id": []byte("id"),
"secret_access_key": []byte("secret"),
"session_token": []byte("token"),
},
},
},
},
repo: "repo",
password: "my-secret",
expected: []string{
"export AWS_ACCESS_KEY_ID=id\n",
"export AWS_SECRET_ACCESS_KEY=secret\n",
"export AWS_SESSION_TOKEN=token\n",
"export RESTIC_REPOSITORY=s3:endpoint/repo\n",
"export RESTIC_PASSWORD=my-secret\n",
"restic",
},
},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding new test case in a followup commit!

@SupriyaKasten SupriyaKasten changed the title AWS IAM role: Generate temporary security credentials from Role (WIP)AWS IAM role: Generate temporary security credentials from Role Oct 11, 2019
Comment on lines -33 to -48
{
secret: &v1.Secret{
Type: v1.SecretType(AWSSecretType),
Data: map[string][]byte{
AWSAccessKeyID: []byte("key_id"),
AWSSecretAccessKey: []byte("secret_key"),
AWSSessionToken: []byte("session_token"),
},
},
expected: &credentials.Value{
AccessKeyID: "key_id",
SecretAccessKey: "secret_key",
SessionToken: "session_token",
},
errChecker: IsNil,
},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: add unit test

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added unit test below.

if err != nil {
return nil, "", "", errors.Wrap(err, "Failed to get temporary security credentials")
}
return config, region, role, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to return role here even after assuming role?

@SupriyaKasten SupriyaKasten changed the title (WIP)AWS IAM role: Generate temporary security credentials from Role AWS IAM role: Generate temporary security credentials from Role Oct 21, 2019
@@ -33,23 +36,39 @@ const (
SecretAccessKey = "AWS_SECRET_ACCESS_KEY"
// SessionToken represents AWS Session Key
SessionToken = "AWS_SESSION_TOKEN"

assumeRoleDuration = 25 * time.Minute
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Blockstorage 👆

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assumption being that we'll automatically renew the blockstorage creds, right?

// ConfigRole represents the key for the ARN of the role which can be assumed.
// It is optional.
ConfigRole = "role"
assumeRoleDuration = 90 * time.Minute
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Restic and Kando 👆

Copy link
Contributor

@tdmanv tdmanv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please include examples of running the tests you changes, especailly the ones that won't get run in CI due to the AWS key requirement.

pkg/config/aws/aws.go Outdated Show resolved Hide resolved
region, ok := config[ConfigRegion]
if !ok {
return nil, "", "", errors.New("region required for storage type EBS")
return nil, "", errors.New("region required for storage type EBS")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this EBS specific?


// SwitchRole assumes role with given credentials.
// Temporary credentials will be valid for 'duration'.
func SwitchRole(ctx context.Context, accessKeyID string, secretAccessKey string, role string, duration time.Duration) (*credentials.Credentials, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't really describe the behavior. creds Is a creds provider that will automatically refresh credentials.

pkg/config/aws/role.go Outdated Show resolved Hide resolved
SupriyaKasten and others added 3 commits October 21, 2019 16:32
Co-Authored-By: Thomas Manville <tom@kasten.io>
Co-Authored-By: Thomas Manville <tom@kasten.io>
@SupriyaKasten
Copy link
Contributor Author

Updated the commit description with test results.

"gopkg.in/check.v1"
)

func GetEnvOrSkip(c *check.C, varName string) string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this method already defined in testutil?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tdmanv Yes, It creates a cycle if I import pkg/testutil, hence I added the function to helpers.go
Also, this func is defined in 4 different places in kanister 😄 . Once this PR is merged, I can push another followup PR to remove the duplicate funcs and import only from helpers.go.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that works for now

)

// GetConfig returns a configuration to establish AWS connection, connected region name and the role to assume if it exists.
func GetConfig(config map[string]string) (awsConfig *aws.Config, region string, role string, err error) {
func GetConfig(ctx context.Context, config map[string]string) (awsConfig *aws.Config, region string, err error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doc might also need update. It doesn't return the role but assumes it if it exists in config.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the comment!

@@ -33,23 +36,39 @@ const (
SecretAccessKey = "AWS_SECRET_ACCESS_KEY"
// SessionToken represents AWS Session Key
SessionToken = "AWS_SESSION_TOKEN"

assumeRoleDuration = 25 * time.Minute
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assumption being that we'll automatically renew the blockstorage creds, right?

@@ -272,6 +272,7 @@ func (s *BlockStorageProviderSuite) getConfig(c *C, region string) map[string]st
}
config[awsconfig.AccessKeyID] = accessKey
config[awsconfig.SecretAccessKey] = secretAccessKey
config[awsconfig.ConfigRole] = os.Getenv(awsconfig.ConfigRole)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tdmanv this enables the tests with AWS assume role BUT it fails for me locally with the same error as we saw with before for storage_tests INFO[0000] Failed to create volume error="UnauthorizedOperation: You are not authorized to perform this operation. Encoded authorization failure message

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested successfully with different role.

Comment on lines +458 to +469
awsAccessKeyID := os.Getenv("AWS_ACCESS_KEY_ID")
awsSecretAccessKey := os.Getenv("AWS_SECRET_ACCESS_KEY")
var awsSessionToken string
if role, ok := os.LookupEnv(aws.ConfigRole); ok {
creds, err := aws.SwitchRole(ctx, awsAccessKeyID, awsSecretAccessKey, role, assumeRoleDuration)
c.Check(err, IsNil)
val, err := creds.Get()
c.Check(err, IsNil)
awsAccessKeyID = val.AccessKeyID
awsSecretAccessKey = val.SecretAccessKey
awsSessionToken = val.SessionToken
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tdmanv With these changes, We run objectstore tests with AWS IAM role, one of the 8 tests fails with my credentials:

go test

----------------------------------------------------------------------
FAIL: objectstore_test.go:423: ObjectStoreProviderSuite.TestBucketGetRegions

objectstore_test.go:429:
    c.Check(err, IsNil)
... value *errors.withStack = Containers, listing the buckets: AccessDenied: Access Denied
	status code: 403, request id: FC39088EE1A52416, host id: g9XaZ2ITdRbK4rDjjR+MCkv8sGlS8lLvubXbq4jnOZRyB/CQU7e0Gk/hyKW86wvffTo4EcQjO4M= ("Containers, listing the buckets: AccessDenied: Access Denied\n\tstatus code: 403, request id: FC39088EE1A52416, host id: g9XaZ2ITdRbK4rDjjR+MCkv8sGlS8lLvubXbq4jnOZRyB/CQU7e0Gk/hyKW86wvffTo4EcQjO4M=")

objectstore_test.go:439:
    c.Check(err, IsNil)
... value *errors.withStack = Containers, listing the buckets: AccessDenied: Access Denied
	status code: 403, request id: B02BD08131EADD44, host id: UTVEUGHfLkhv6ZUs07OHvc8D74LSMMT4sw5ZBQecbjkqVtf55jteT3+TSytkhxgQ1yHGBe6LNK4= ("Containers, listing the buckets: AccessDenied: Access Denied\n\tstatus code: 403, request id: B02BD08131EADD44, host id: UTVEUGHfLkhv6ZUs07OHvc8D74LSMMT4sw5ZBQecbjkqVtf55jteT3+TSytkhxgQ1yHGBe6LNK4=")

objectstore_test.go:439:
    c.Check(err, IsNil)
... value *errors.withStack = Containers, listing the buckets: AccessDenied: Access Denied
	status code: 403, request id: A34CE00D4BC213E7, host id: BtgIfwhLgvf+WCZDOvi6jG8zg63XVaHp1f4jUn8UL0qUMHyuFDTJhqqB4wXoiwnooF0NSy90zfE= ("Containers, listing the buckets: AccessDenied: Access Denied\n\tstatus code: 403, request id: A34CE00D4BC213E7, host id: BtgIfwhLgvf+WCZDOvi6jG8zg63XVaHp1f4jUn8UL0qUMHyuFDTJhqqB4wXoiwnooF0NSy90zfE=")

objectstore_test.go:439:
    c.Check(err, IsNil)
... value *errors.withStack = Containers, listing the buckets: AccessDenied: Access Denied
	status code: 403, request id: 9017B3A3E89EA391, host id: 6+JXjooSmq1FMlitzW787EsZSz8ktC3DWfvC64wRvsvBhrgvkKi9ikCgi2PpqR4KbeblH6KAq4w= ("Containers, listing the buckets: AccessDenied: Access Denied\n\tstatus code: 403, request id: 9017B3A3E89EA391, host id: 6+JXjooSmq1FMlitzW787EsZSz8ktC3DWfvC64wRvsvBhrgvkKi9ikCgi2PpqR4KbeblH6KAq4w=")

objectstore_test.go:439:
    c.Check(err, IsNil)
... value *errors.withStack = Containers, listing the buckets: AccessDenied: Access Denied
	status code: 403, request id: 9B88820B15FB881A, host id: 8oyRbM9NywaXjgQYISX3j1n+cTS3okFPb32nMt3fpjKXNH8TzwzcjfbQVYEj9DuNHQLwIWmZPjQ= ("Containers, listing the buckets: AccessDenied: Access Denied\n\tstatus code: 403, request id: 9B88820B15FB881A, host id: 8oyRbM9NywaXjgQYISX3j1n+cTS3okFPb32nMt3fpjKXNH8TzwzcjfbQVYEj9DuNHQLwIWmZPjQ=")

OOPS: 7 passed, 1 skipped, 1 FAILED
--- FAIL: Test (8.70s)
FAIL
exit status 1
FAIL	github.com/kanisterio/kanister/pkg/objectstore	8.729s

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SupriyaKasten is this failing before or after we get temporary credentials?

Copy link
Contributor Author

@SupriyaKasten SupriyaKasten Oct 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These fail after we get the temp creds, although 7 of the tests pass with the temp credentials. The call to s.provider.ListBuckets(ctx) fails with these temp credentials.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If so, then this is an issue with the Role we're changing to in these tests, so won't this fail if we run this test with anyone's creds?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed offline with @tdmanv and @vkamra, skipping the failing test since we want to run the tests with available role permissions(our s3 role does not have list bucket permission)

@@ -406,6 +441,10 @@ func setResourceTags(ctx context.Context, ec2Cli *EC2, resourceID string, tags m
}

func (s *ebsStorage) VolumeCreateFromSnapshot(ctx context.Context, snapshot blockstorage.Snapshot, tags map[string]string) (*blockstorage.Volume, error) {
err := s.refreshCreds()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The credentials should be automatically refreshed by the EC2 client

var _ = Suite(&ObjectStoreProviderSuite{osType: ProviderTypeGCS, region: ""})
var _ = Suite(&ObjectStoreProviderSuite{osType: ProviderTypeAzure, region: ""})

// var _ = Suite(&ObjectStoreProviderSuite{osType: ProviderTypeGCS, region: ""})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uncomment prior to merge

@mergify mergify bot merged commit 2733489 into master Oct 24, 2019
@mergify mergify bot deleted the aws-iam-profile branch October 24, 2019 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants