diff --git a/cmd/account/cli.go b/cmd/account/cli.go index b23fc834..409daa03 100644 --- a/cmd/account/cli.go +++ b/cmd/account/cli.go @@ -3,11 +3,14 @@ package account import ( "context" "fmt" + "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/sts" awsv1alpha1 "github.com/openshift/aws-account-operator/pkg/apis/aws/v1alpha1" "github.com/spf13/cobra" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/klog" @@ -36,7 +39,7 @@ func newCmdCli(streams genericclioptions.IOStreams, flags *genericclioptions.Con cliCmd.Flags().StringVar(&ops.accountNamespace, "account-namespace", common.AWSAccountNamespace, "The namespace to keep AWS accounts. The default value is aws-account-operator.") cliCmd.Flags().StringVarP(&ops.accountName, "account-name", "a", "", "The AWS Account CR name to generate the credentials for") - cliCmd.Flags().StringVarP(&ops.accountID, "account-id", "i", "", "The AWS Account ID we need to create temporary AWS credentials for") + cliCmd.Flags().StringVarP(&ops.accountID, "account-id", "i", "", "The AWS Account ID we need to create temporary AWS credentials for -- This argument will not work for CCS accounts") cliCmd.Flags().StringVarP(&ops.clusterID, "cluster-id", "C", "", "The Internal Cluster ID from Hive to create AWS console URL for") cliCmd.Flags().StringVarP(&ops.profile, "aws-profile", "p", "", "specify AWS profile") cliCmd.Flags().StringVarP(&ops.cfgFile, "aws-config", "c", "", "specify AWS config file path") @@ -130,14 +133,19 @@ func (o *cliOptions) run() error { } o.accountName = accountClaim.Spec.AccountLink } + var isBYOC bool + var acctSuffix string if o.accountName != "" { account, err := k8s.GetAWSAccount(ctx, o.kubeCli, o.accountNamespace, o.accountName) if err != nil { return err } accountID = account.Spec.AwsAccountID + isBYOC = account.Spec.BYOC + acctSuffix = account.Labels["iamUserId"] } else { accountID = o.accountID + isBYOC = false } callerIdentityOutput, err := awsClient.GetCallerIdentity(&sts.GetCallerIdentityInput{}) @@ -149,7 +157,59 @@ func (o *cliOptions) run() error { fmt.Fprintln(o.Out, callerIdentityOutput) } + splitArn := strings.Split(*callerIdentityOutput.Arn, "/") + username := splitArn[1] + sessionName := fmt.Sprintf("RH-SRE-%s", username) + + // If BYOC we need to role-chain to use the right creds. + // Use the OrgAccess Role by default, override if BYOC roleName := awsv1alpha1.AccountOperatorIAMRole + + // TODO: Come back to this and do a lookup for the account CR if the account ID is the only one set so we can do this too. + if isBYOC { + cm := &corev1.ConfigMap{} + err = o.kubeCli.Get(ctx, types.NamespacedName{Namespace: awsv1alpha1.AccountCrNamespace, Name: awsv1alpha1.DefaultConfigMap}, cm) + if err != nil { + klog.Error("There was an error getting the configmap.") + return err + } + roleArn := cm.Data["CCS-Access-Arn"] + + if roleArn == "" { + klog.Error("Empty SRE Jump Role in ConfigMap") + return fmt.Errorf("Empty ConfigMap Value") + } + + // Build the role-name for Access: + if acctSuffix == "" { + klog.Error("Unexpected error parsing the account CR suffix") + return fmt.Errorf("Unexpected error parsing the account CR suffix.") + } + roleName = fmt.Sprintf("BYOCAdminAccess-%s", acctSuffix) + + // Get STS Credentials + if o.verbose { + fmt.Printf("Elevating Access to SRE Jump Role for user %s\n", sessionName) + } + creds, err := awsprovider.GetAssumeRoleCredentials(awsClient, &o.cliDuration, aws.String(sessionName), aws.String(roleArn)) + if err != nil { + klog.Error("Failed to get jump-role creds for CCS") + return err + } + + awsClientInput := &awsprovider.AwsClientInput{ + AccessKeyID: *creds.AccessKeyId, + SecretAccessKey: *creds.SecretAccessKey, + SessionToken: *creds.SessionToken, + Region: "us-east-1", + } + // New Client with STS Credentials + awsClient, err = awsprovider.NewAwsClientWithInput(awsClientInput) + if err != nil { + klog.Error("Failed to assume jump-role for CCS") + return err + } + } credentials, err := awsprovider.GetAssumeRoleCredentials(awsClient, &o.cliDuration, callerIdentityOutput.UserId, aws.String(fmt.Sprintf("arn:aws:iam::%s:role/%s", accountID, roleName))) if err != nil { diff --git a/cmd/account/console.go b/cmd/account/console.go index efdee460..328493fe 100644 --- a/cmd/account/console.go +++ b/cmd/account/console.go @@ -3,11 +3,14 @@ package account import ( "context" "fmt" + "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/sts" awsv1alpha1 "github.com/openshift/aws-account-operator/pkg/apis/aws/v1alpha1" "github.com/spf13/cobra" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/klog" @@ -36,7 +39,7 @@ func newCmdConsole(streams genericclioptions.IOStreams, flags *genericclioptions consoleCmd.Flags().StringVar(&ops.accountNamespace, "account-namespace", common.AWSAccountNamespace, "The namespace to keep AWS accounts. The default value is aws-account-operator.") consoleCmd.Flags().StringVarP(&ops.accountName, "account-name", "a", "", "The AWS account cr we need to create AWS console URL for") - consoleCmd.Flags().StringVarP(&ops.accountID, "account-id", "i", "", "The AWS account ID we need to create AWS console URL for") + consoleCmd.Flags().StringVarP(&ops.accountID, "account-id", "i", "", "The AWS account ID we need to create AWS console URL for -- This argument will not work for CCS accounts") consoleCmd.Flags().StringVarP(&ops.clusterID, "cluster-id", "C", "", "The Internal Cluster ID from Hive to create AWS console URL for") consoleCmd.Flags().StringVarP(&ops.profile, "aws-profile", "p", "", "specify AWS profile") consoleCmd.Flags().StringVarP(&ops.cfgFile, "aws-config", "c", "", "specify AWS config file path") @@ -130,14 +133,19 @@ func (o *consoleOptions) run() error { } o.accountName = accountClaim.Spec.AccountLink } + var isBYOC bool + var acctSuffix string if o.accountName != "" { account, err := k8s.GetAWSAccount(ctx, o.kubeCli, o.accountNamespace, o.accountName) if err != nil { return err } accountID = account.Spec.AwsAccountID + isBYOC = account.Spec.BYOC + acctSuffix = account.Labels["iamUserId"] } else { accountID = o.accountID + isBYOC = false } callerIdentityOutput, err := awsClient.GetCallerIdentity(&sts.GetCallerIdentityInput{}) @@ -148,10 +156,62 @@ func (o *consoleOptions) run() error { if o.verbose { fmt.Fprintln(o.Out, callerIdentityOutput) } + splitArn := strings.Split(*callerIdentityOutput.Arn, "/") + username := splitArn[1] + sessionName := fmt.Sprintf("RH-SRE-%s", username) + // If BYOC we need to role-chain to use the right creds. + // Use the OrgAccess Role by default, override if BYOC roleName := awsv1alpha1.AccountOperatorIAMRole + + // TODO: Come back to this and do a lookup for the account CR if the account ID is the only one set so we can do this too. + if isBYOC { + cm := &corev1.ConfigMap{} + err = o.kubeCli.Get(ctx, types.NamespacedName{Namespace: awsv1alpha1.AccountCrNamespace, Name: awsv1alpha1.DefaultConfigMap}, cm) + if err != nil { + klog.Error("There was an error getting the configmap.") + return err + } + roleArn := cm.Data["CCS-Access-Arn"] + + if roleArn == "" { + klog.Error("Empty SRE Jump Role in ConfigMap") + return fmt.Errorf("Empty ConfigMap Value") + } + + // Build the role-name for Access: + if acctSuffix == "" { + klog.Error("Unexpected error parsing the account CR suffix") + return fmt.Errorf("Unexpected error parsing the account CR suffix.") + } + roleName = fmt.Sprintf("BYOCAdminAccess-%s", acctSuffix) + + // Get STS Credentials + if o.verbose { + fmt.Printf("Elevating Access to SRE Jump Role for user %s\n", sessionName) + } + creds, err := awsprovider.GetAssumeRoleCredentials(awsClient, &o.consoleDuration, aws.String(sessionName), aws.String(roleArn)) + if err != nil { + klog.Error("Failed to get jump-role creds for CCS") + return err + } + + awsClientInput := &awsprovider.AwsClientInput{ + AccessKeyID: *creds.AccessKeyId, + SecretAccessKey: *creds.SecretAccessKey, + SessionToken: *creds.SessionToken, + Region: "us-east-1", + } + // New Client with STS Credentials + awsClient, err = awsprovider.NewAwsClientWithInput(awsClientInput) + if err != nil { + klog.Error("Failed to assume jump-role for CCS") + return err + } + } + consoleURL, err := awsprovider.RequestSignInToken(awsClient, &o.consoleDuration, - callerIdentityOutput.UserId, aws.String(fmt.Sprintf("arn:aws:iam::%s:role/%s", accountID, roleName))) + aws.String(sessionName), aws.String(fmt.Sprintf("arn:aws:iam::%s:role/%s", accountID, roleName))) if err != nil { return err } diff --git a/docs/command/osdctl_account_cli.md b/docs/command/osdctl_account_cli.md index b965b3ba..e4a64d6a 100644 --- a/docs/command/osdctl_account_cli.md +++ b/docs/command/osdctl_account_cli.md @@ -13,7 +13,7 @@ osdctl account cli [flags] ### Options ``` - -i, --account-id string The AWS Account ID we need to create temporary AWS credentials for + -i, --account-id string The AWS Account ID we need to create temporary AWS credentials for -- This argument will not work for CCS accounts -a, --account-name string The AWS Account CR name to generate the credentials for --account-namespace string The namespace to keep AWS accounts. The default value is aws-account-operator. (default "aws-account-operator") -c, --aws-config string specify AWS config file path diff --git a/docs/command/osdctl_account_console.md b/docs/command/osdctl_account_console.md index 39b17c41..1e0aaebd 100644 --- a/docs/command/osdctl_account_console.md +++ b/docs/command/osdctl_account_console.md @@ -13,7 +13,7 @@ osdctl account console [flags] ### Options ``` - -i, --account-id string The AWS account ID we need to create AWS console URL for + -i, --account-id string The AWS account ID we need to create AWS console URL for -- This argument will not work for CCS accounts -a, --account-name string The AWS account cr we need to create AWS console URL for --account-namespace string The namespace to keep AWS accounts. The default value is aws-account-operator. (default "aws-account-operator") -c, --aws-config string specify AWS config file path diff --git a/go.mod b/go.mod index 951c61c3..b9508710 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/golang/mock v1.4.3 github.com/onsi/gomega v1.10.1 github.com/openshift/api v3.9.1-0.20191111211345-a27ff30ebf09+incompatible - github.com/openshift/aws-account-operator v0.0.0-20200529133510-076b8c994393 + github.com/openshift/aws-account-operator v0.0.0-20200914143350-bbda1c91242b github.com/openshift/hive v1.0.5 github.com/pkg/errors v0.9.1 github.com/prometheus/common v0.10.0 diff --git a/go.sum b/go.sum index 4ffc4632..72584d36 100644 --- a/go.sum +++ b/go.sum @@ -1502,8 +1502,8 @@ github.com/openshift/api v0.0.0-20200424083944-0422dc17083e/go.mod h1:VnbEzX8SAa github.com/openshift/api v3.9.1-0.20190517100836-d5b34b957e91+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY= github.com/openshift/api v3.9.1-0.20191111211345-a27ff30ebf09+incompatible h1:AvJ2SgJ7ekSlEL/wyeVMffxDkbKohp4JLge9wMtT23o= github.com/openshift/api v3.9.1-0.20191111211345-a27ff30ebf09+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY= -github.com/openshift/aws-account-operator v0.0.0-20200529133510-076b8c994393 h1:SANfjUBDo4IxaQnWkx0UKnCwFV7LedoUmIs2zS/baf8= -github.com/openshift/aws-account-operator v0.0.0-20200529133510-076b8c994393/go.mod h1:3JOgGxqzrQEGoJhGIIwrfHG6c0OjUhPuxVi0/KV14zQ= +github.com/openshift/aws-account-operator v0.0.0-20200914143350-bbda1c91242b h1:TS/IiLLrBjpqKeBZ4Yps6/UiAAIimyk5BGj29QaAGCI= +github.com/openshift/aws-account-operator v0.0.0-20200914143350-bbda1c91242b/go.mod h1:3JOgGxqzrQEGoJhGIIwrfHG6c0OjUhPuxVi0/KV14zQ= github.com/openshift/baremetal-operator v0.0.0-20200206190020-71b826cc0f0a/go.mod h1:cXwn0hhgHpORjBasg0RnZwhKaJGy9+r6qgj0HCXrs/Y= github.com/openshift/build-machinery-go v0.0.0-20200211121458-5e3d6e570160/go.mod h1:1CkcsT3aVebzRBzVTSbiKSkJMsC/CASqxesfqEMfJEc= github.com/openshift/build-machinery-go v0.0.0-20200424080330-082bf86082cc/go.mod h1:1CkcsT3aVebzRBzVTSbiKSkJMsC/CASqxesfqEMfJEc=