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

feat(eks): cross-account support #600

Merged
merged 3 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/book/src/commands/use_eks.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ kconnect use eks [flags]

```bash
-a, --alias string Friendly name to give to give the connection
--assume-role-arn string ARN of the AWS role to be assumed
--aws-shared-credentials-file string Location to store AWS credentials file
-c, --cluster-id string Id of the cluster to use.
-h, --help help for eks
Expand All @@ -65,7 +66,7 @@ kconnect use eks [flags]
--password string The password to use for authentication
--region string AWS region to connect to
--region-filter string A regex filter to apply to the AWS regions list, e.g. '^us-|^eu-' will only show US and eu regions
--role-arn string ARN of the AWS role to be assumed
--role-arn string ARN of the AWS role to be logged in with
--role-filter string A filter to apply to the roles list, e.g. 'EKS' will only show roles that contain EKS in the name
--set-current Sets the current context in the kubeconfig to the selected cluster (default true)
--username string The username used for authentication
Expand Down
12 changes: 7 additions & 5 deletions pkg/plugins/discovery/aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,11 @@ func New(input *provider.PluginCreationInput) (discovery.Provider, error) {

type eksClusteProviderConfig struct {
common.ClusterProviderConfig
Region *string `json:"region"`
RegionFilter *string `json:"region-filter"`
RoleArn *string `json:"role-arn"`
RoleFilter *string `json:"role-filter"`
AssumeRoleARN *string `json:"assume-role-arn"`
Region *string `json:"region"`
RegionFilter *string `json:"region-filter"`
RoleArn *string `json:"role-arn"`
RoleFilter *string `json:"role-filter"`
}

// EKSClusterProvider will discover EKS clusters in AWS
Expand Down Expand Up @@ -129,8 +130,9 @@ func ConfigurationItems(scopeTo string) (config.ConfigurationSet, error) {
cs := aws.SharedConfig()

cs.String("aws-shared-credentials-file", os.Getenv("AWS_SHARED_CREDENTIALS_FILE"), "Location to store AWS credentials file")
cs.String("assume-role-arn", "", "ARN of the AWS role to be assumed")
cs.String("region-filter", "", "A regex filter to apply to the AWS regions list, e.g. '^us-|^eu-' will only show US and eu regions") //nolint: errcheck
cs.String("role-arn", "", "ARN of the AWS role to be assumed") //nolint: errcheck
cs.String("role-arn", "", "ARN of the AWS role to be logged in with") //nolint: errcheck
cs.String("role-filter", "", "A filter to apply to the roles list, e.g. 'EKS' will only show roles that contain EKS in the name") //nolint: errcheck

return cs, nil
Expand Down
1 change: 1 addition & 0 deletions pkg/plugins/identity/saml/saml.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ func (p *samlIdentityProvider) bindAndValidateConfig(cs config.ConfigurationSet)

func (p *samlIdentityProvider) createAccount(cs config.ConfigurationSet) (*cfg.IDPAccount, error) {
account := &cfg.IDPAccount{
Username: p.config.Username,
URL: p.config.IdpEndpoint,
Provider: p.config.IdpProvider,
MFA: "Auto",
Expand Down
48 changes: 48 additions & 0 deletions pkg/plugins/identity/saml/sp/aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"go.uber.org/zap"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sts"
Expand Down Expand Up @@ -131,6 +132,19 @@ func (p *ServiceProvider) ProcessAssertions(account *cfg.IDPAccount, samlAsserti
return nil, fmt.Errorf("logging into AWS using STS and SAMLAssertion: %w", err)
}

// switch AWS IAM role
assumeRoleARN := cfg.Get("assume-role-arn")
if assumeRoleARN != nil && assumeRoleARN.Value.(string) != "" {
awsCreds, err = p.assumeRoleARN(account, awsCreds, assumeRoleARN.Value.(string))
if err != nil {
return nil, fmt.Errorf("assuming role in AWS: %w", err)
}
if err := cfg.SetValue("assume-role-arn", assumeRoleARN.Value.(string)); err != nil {
return nil, fmt.Errorf("setting assume-role-arn config value: %w", err)
}
p.logger.Debugw("role assumed", "assume-role", assumeRoleARN.Value.(string))
}

// Create profile based on the AWS creds
identifier, err := kaws.CreateIDFromCreds(awsCreds)
if err != nil {
Expand Down Expand Up @@ -301,6 +315,40 @@ func (p *ServiceProvider) loginToStsUsingRole(account *cfg.IDPAccount, role *sam
}, nil
}

func (p *ServiceProvider) assumeRoleARN(account *cfg.IDPAccount, awsCreds *awsconfig.AWSCredentials, assumeRoleARN string) (*awsconfig.AWSCredentials, error) {
sess, err := session.NewSession(&aws.Config{
Region: &account.Region,
STSRegionalEndpoint: endpoints.RegionalSTSEndpoint,
Credentials: credentials.NewStaticCredentials(
awsCreds.AWSAccessKey,
awsCreds.AWSSecretKey,
awsCreds.AWSSessionToken,
),
})
if err != nil {
return nil, fmt.Errorf("creating aws session: %w", err)
}
assumeRoleInput := &sts.AssumeRoleInput{
RoleArn: &assumeRoleARN,
RoleSessionName: &account.Username,
DurationSeconds: aws.Int64(int64(account.SessionDuration)),
}
out, err := sts.New(sess).AssumeRole(assumeRoleInput)
if err != nil {
return nil, fmt.Errorf("failed to assume role: %w", err)
}

return &awsconfig.AWSCredentials{
AWSAccessKey: aws.StringValue(out.Credentials.AccessKeyId),
AWSSecretKey: aws.StringValue(out.Credentials.SecretAccessKey),
AWSSessionToken: aws.StringValue(out.Credentials.SessionToken),
AWSSecurityToken: aws.StringValue(out.Credentials.SessionToken),
PrincipalARN: aws.StringValue(out.AssumedRoleUser.Arn),
Expires: out.Credentials.Expiration.Local(),
Region: account.Region,
}, nil
}

// TODO: use the version form saml2aws when modules are fixed
func (p *ServiceProvider) extractDestinationURL(data []byte) (string, error) {

Expand Down