Skip to content

Commit

Permalink
Replumb AssumeRole (AWS)
Browse files Browse the repository at this point in the history
Simplify the AssumeRole flow: Rather than doing it via
`credential_process` as a callback from within the creds file used by
the provision pod, flatten this out so the AssumeRole is done implicitly
by the AWS SDK.

This flow remains unchanged:

The clusterdeployment controller:
- Copies the service provider secret into the CD namespace
- Creates an AWS credentials secret
- Creates the provision pod

The provision pod:
- Loads the credentials secret
- Projects the AWS config therein onto the file system
- Invokes the installer

The installer:
- Creates an AWS client using that config file
- Proceeds with installation

Before this commit:
The AWS config contained a `credential_process` which invoked
`hiveutil install-manager aws-credentials` which...
- Loaded the service provider secret
- Created an AWS client
- Used the client to AssumeRole and generate credentials with a 15m
expiration
- Printed the credentials to stdout in the format expected by AWS.

Per AWS docs[1], the SDK will automatically rerun the
`credential_process` before the expiration time to refresh the creds.

With this commit:
The clusterdeployment controller loads the service provider secret and
folds it into the AWS config as a separate profile, referenced from the
default via `source_profile`:

```
[default]
source_profile = source
role_arn = arn:aws:iam::123456789012:role/assume-role-customer

[profile source]
aws_access_key_id: ABCDEFGHIJKLMNOPQRST
aws_secret_access_key: 1234567890abcdefghijklmnopqrstuvwxyz0123
role_arn = arn:aws:iam::210987654321:role/assume-role-provider
```

Per AWS docs[2], the SDK will use the source creds to AssumeRole to
generate temporary creds, which it will automatically refresh as they
expire -- i.e. natively performing the same function as `hiveutil
install-manager aws-credentials`.

[1] https://docs.aws.amazon.com/cli/v1/userguide/cli-configure-sourcing-external.html
[2] https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-role.html

HIVE-2485
HIVE-2529
  • Loading branch information
2uasimojo committed Jul 15, 2024
1 parent 1fce770 commit 8f11ce3
Show file tree
Hide file tree
Showing 8 changed files with 36 additions and 212 deletions.
2 changes: 0 additions & 2 deletions contrib/pkg/utils/aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,6 @@ func ConfigureCreds(c client.Client) {
utils.ProjectToDir(credsSecret, constants.AWSCredsMount, constants.AWSConfigSecretKey)
os.Setenv("AWS_CONFIG_FILE", filepath.Join(constants.AWSCredsMount, constants.AWSConfigSecretKey))
}
// Allow credential_process in the config file
os.Setenv("AWS_SDK_LOAD_CONFIG", "true")
// Install cluster proxy trusted CA bundle
utils.InstallCerts(constants.TrustedCABundleDir)
}
2 changes: 1 addition & 1 deletion docs/awsassumerolecreds.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Let's create a Secret in `hive` (target namespace in HiveConfig) with credential

```console
$ cat aws-service-provider-config
[default]
[profile source]
aws_access_key_id = XXXXXX
aws_secret_access_key = XXXXX
role_arn = arn:aws:iam::123456:role/hive-aws-service-provider
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/clusterdeployment/clusterprovisions.go
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ func (r *ReconcileClusterDeployment) setupAWSCredentialForAssumeRole(cd *hivev1.
return nil
}

return install.AWSAssumeRoleCLIConfig(r.Client, cd.Spec.Platform.AWS.CredentialsAssumeRole, install.AWSAssumeRoleSecretName(cd.Name), cd.Namespace, cd, r.scheme)
return install.AWSAssumeRoleConfig(r.Client, cd.Spec.Platform.AWS.CredentialsAssumeRole, install.AWSAssumeRoleSecretName(cd.Name), cd.Namespace, cd, r.scheme)
}

func (r *ReconcileClusterDeployment) watchClusterProvisions(mgr manager.Manager, c controller.Controller) error {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,6 @@ func (r *ReconcileClusterDeprovision) setupAWSCredentialForAssumeRole(cd *hivev1
return nil
}

return install.AWSAssumeRoleCLIConfig(r.Client, cd.Spec.Platform.AWS.CredentialsAssumeRole, install.AWSAssumeRoleSecretName(cd.Name), cd.Namespace, cd, r.scheme)
return install.AWSAssumeRoleConfig(r.Client, cd.Spec.Platform.AWS.CredentialsAssumeRole, install.AWSAssumeRoleSecretName(cd.Name), cd.Namespace, cd, r.scheme)

}
48 changes: 33 additions & 15 deletions pkg/install/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,23 +74,40 @@ func CopyAWSServiceProviderSecret(client client.Client, destNamespace string, en
return controllerutils.CopySecret(client, src, dest, owner, scheme)
}

// AWSAssumeRoleCLIConfig creates a secret that can assume the role using the hiveutil
// credential_process helper.
func AWSAssumeRoleCLIConfig(client client.Client, role *hivev1aws.AssumeRole, secretName, secretNamespace string, owner metav1.Object, scheme *runtime.Scheme) error {
cmd := "/output/hiveutil"
args := []string{"install-manager", "aws-credentials"}
args = append(args, []string{"--namespace", secretNamespace}...)
args = append(args, []string{"--role-arn", role.RoleARN}...)
if role.ExternalID != "" {
args = append(args, []string{"--external-id", role.ExternalID}...)
// AWSAssumeRoleConfig creates a secret with an AWS credentials file containing:
// - Role configuration for AssumeRole, pointing to...
// - A profile containing the source credentials for AssumeRole.
func AWSAssumeRoleConfig(client client.Client, role *hivev1aws.AssumeRole, secretName, secretNamespace string, owner metav1.Object, scheme *runtime.Scheme) error {

// Credentials source
credsSecret := &corev1.Secret{}
if err := client.Get(
context.TODO(),
types.NamespacedName{
Namespace: secretNamespace,
// TODO: DRY this string construction
Name: fmt.Sprintf("%s-%s", owner.GetName(), os.Getenv(constants.HiveAWSServiceProviderCredentialsSecretRefEnvVar)),
},
credsSecret); err != nil {
return err
}
// The old credential_process flow documented creating this with [default].
// For backward compatibility, accept that, but convert to [profile source].
sourceProfile := strings.Replace(string(credsSecret.Data[constants.AWSConfigSecretKey]), `[default]`, `[profile source]`, 1)

cmd = fmt.Sprintf("%s %s", cmd, strings.Join(args, " "))
extID := ""
if role.ExternalID != "" {
extID = fmt.Sprintf("external_id = %s\n", role.ExternalID)
}

template := `[default]
credential_process = %s
`
data := fmt.Sprintf(template, cmd)
// Build the config file
configFile := fmt.Sprintf(`[default]
source_profile = source
role_arn = %s
%s
%s
`,
role.RoleARN, extID, sourceProfile)

secret := &corev1.Secret{
TypeMeta: metav1.TypeMeta{
Expand All @@ -102,9 +119,10 @@ credential_process = %s
Name: secretName,
},
Data: map[string][]byte{
constants.AWSConfigSecretKey: []byte(data),
constants.AWSConfigSecretKey: []byte(configFile),
},
}

if err := controllerutil.SetOwnerReference(owner, secret, scheme); err != nil {
return nil
}
Expand Down
157 changes: 0 additions & 157 deletions pkg/installmanager/aws_credentials.go

This file was deleted.

34 changes: 0 additions & 34 deletions pkg/installmanager/aws_credentials_test.go

This file was deleted.

1 change: 0 additions & 1 deletion pkg/installmanager/installmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ SSH_PRIV_KEY_PATH: File system path of a file containing the SSH private key cor
flags.StringVar(&im.WorkDir, "work-dir", "/output", "directory to use for all input and output")
flags.StringVar(&im.LogsDir, "logs-dir", "/logs", "directory to use for all installer logs")

cmd.AddCommand(NewInstallManagerAWSCredentials())
return cmd
}

Expand Down

0 comments on commit 8f11ce3

Please sign in to comment.