Skip to content

Commit

Permalink
feat(assumeWebIdentityRole): support AssumeRoleWithWebIdentity arn sw…
Browse files Browse the repository at this point in the history
…ap (crossplane-contrib#1258)

* feat(assumeWebIdentityRole): added support for AssumeRoleWithWebIdentity arn swapping

Signed-off-by: Jesse Sanford <jesse.sanford@autodesk.com>
Signed-off-by: Felipe Barbosa <lybrbarbosa@gmail.com>
  • Loading branch information
jessesanford authored and febarbosa182 committed May 23, 2022
1 parent def7d33 commit 2c6a810
Show file tree
Hide file tree
Showing 6 changed files with 483 additions and 0 deletions.
198 changes: 198 additions & 0 deletions AUTHENTICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- [Using kube2iam](#using-kube2iam)
- [Steps](#steps-1)
- [Using `assumeRole`](#using-assumerole)
- [Using `assumeRoleWithWebIdentity`](#using-assumerolewithwebidentity)

## Overview

Expand Down Expand Up @@ -343,3 +344,200 @@ spec:
source: InjectedIdentity
EOF
```

## Using `assumeRoleWithWebIdentity`

`provider-aws` will be configured to connect to the aws account in `RoleARN` and request
a session for `RoleARN` using it's `InjectedIdentity`

This is most useful when "sts chaining" (see [Using `assumeRole`](#using-assumerole))
is not allowed between accounts or when cross account IRSA is more suitable.

IRSA will need to be configured between the account hosting the `RoleARN` being targeted and the
K8s cluster hosting the provider pod.

The following steps follow on to the initial configuration from
[Using IAM Roles for `ServiceAccounts`](#using-iam-roles-for-serviceaccounts)
Please see that section for more info on the initial setup of IRSA.

1. Get AWS account information for account hosting the provider pod

Starting with your credentials for the account hosting the provider pod
get AWS account information.

```console
$ PROVIDER_POD_AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
$ PROVIDER_POD_REGION=us-west-2
```

Set the EKS cluster name for the cluster hosting the provider pod

```console
$ aws eks list-clusters
{
"clusters": [
"crossplane-k5dft"
]
}
$ PROVIDER_POD_CLUSTER_NAME="crossplane-k5dft"
```

2. Identify `provider-aws` service account

Set current kubectl context to provider pod cluster

```console
$ aws eks update-kubeconfig --name $PROVIDER_POD_CLUSTER_NAME
```

**NOTE:** If you are using the Upbound UXP distrobution of crossplane you may need to use
`upbound-system` for the namespace in the following commands.

```console
$ PROVIDER_POD_NAMESPACE=crossplane-system #upbound-system when using UXP
```
> You should see the `provider-aws-*`
> controller `Pod` running if you execute `kubectl get pods -n $PROVIDER_POD_NAMESPACE`.

Make sure that the appropriate `ServiceAccount` exists:

```console
$ kubectl get serviceaccounts -n $PROVIDER_POD_NAMESPACE
```
_You should see a `ServiceAccount` in the output prefixed with `provider-aws-*`._

3. Set variables for IDP and OIDC trust configuration.

```console
$ OIDC_PROVIDER=$(aws eks describe-cluster --name "${PROVIDER_POD_CLUSTER_NAME}" --region "${PROVIDER_POD_REGION}" --query "cluster.identity.oidc.issuer" --output text | sed -e "s/^https:\/\///")
```

```console
$ THUMBPRINT=$(openssl s_client -servername oidc.eks.${PROVIDER_POD_REGION}.amazonaws.com -showcerts -connect oidc.eks.${PROVIDER_POD_REGION}.amazonaws.com:443 2>&- | tac | sed -n '/-----END CERTIFICATE-----/,/-----BEGIN CERTIFICATE-----/p; /-----BEGIN CERTIFICATE-----/q' | tac | openssl x509 -fingerprint -noout | sed 's/://g' | awk -F= '{print tolower($2)}')
```

4. Create IAM Role that `provider-aws` will use in the TARGET aws account

**NOTE:** Switch your command line to credentials for the target account.

Get AWS account information and pick an IAM role name. These will be used to
setup an OIDC provider and inject credentials into the `provider-aws` controller
`Pod`.

```console
$ TARGET_AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
$ IAM_ROLE_NAME=provider-aws # name for IAM role, can be anything you want
```

Create an Identity Provider in the target acount pointing back to the OIDC url from above
```console
aws iam create-open-id-connect-provider -url https://${OIDC_PROVIDER} --thumbprint-list ${THUMBPRINT} --client-id-list sts.amazonaws.com
{
"OpenIDConnectProviderArn": "arn:aws:iam::1234:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/123456789"
}
```

Create trust relationship for IAM role:

```console
$ cat > trust.yaml <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::${PROVIDER_POD_AWS_ACCOUNT_ID}:oidc-provider/${OIDC_PROVIDER}"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"${OIDC_PROVIDER}:sub": "system:serviceaccount:${PROVIDER_POD_NAMESPACE}:provider-aws-*"
}
}
}
]
}
EOF
```

> The default `ServiceAccount` name is the provider-aws revision and changes with every provider release.
> The conditional above wildcard matches the default `ServiceAccount` name in order to keep the role consistent across provider releases.
The above policy assumes a service account name of `provider-aws-*`.

Create an IAM role:

```console
$ aws iam create-role \
--role-name "${IAM_ROLE_NAME}" \
--assume-role-policy-document file://trust.json \
--description "IAM role for provider-aws in account #${PROVIDER_POD_AWS_ACCOUNT_ID}"
```

Associate a policy with the IAM role.
This example uses `AdministratorAccess`, but you should select a policy with
the minimum permissions required to provision your resources.

```console
$ aws iam attach-role-policy --role-name "${IAM_ROLE_NAME}" --policy-arn=arn:aws:iam::aws:policy/AdministratorAccess
```

5. Create `ProviderConfig`

Set current kubectl context to provider pod cluster

```console
$ aws eks update-kubeconfig --name $PROVIDER_POD_CLUSTER_NAME
```

Ensure that `ProviderConfig` resource kind was created:

```console
$ kubectl explain providerconfig --api-version='aws.crossplane.io/v1beta1'
```

Next, the `ProviderConfig` must be configured to use `assumeRoleWithWebIdentity`
and `InjectedIdentity` in `.spec.credentials.source`

The code snippet below shows how to configure `provider-aws-target-account` to do so.

```console
$ cat <<EOF | kubectl apply -f -
apiVersion: aws.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
name: provider-aws-target-account
spec:
assumeRoleWithWebIdentity:
roleARN: "arn:aws:iam::${TARGET_AWS_ACCOUNT_ID}:role/${IAM_ROLE_NAME}"
credentials:
source: InjectedIdentity
EOF
```

Role session name is supported (see <https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html#API_AssumeRoleWithWebIdentity_RequestParameters>).

```console
$ cat <<EOF | kubectl apply -f -
apiVersion: aws.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
name: provider-aws-target-account
spec:
assumeRoleWithWebIdentity:
roleARN: "arn:aws:iam::${TARGET_AWS_ACCOUNT_ID}:role/${IAM_ROLE_NAME}"
roleSessionName: "my-optional-session-name"
credentials:
source: InjectedIdentity
EOF
```

You can now use this `provider-aws-target-account` config with compositions you want
reconciled against the `$TARGET_AWS_ACCOUNT_ID` and the web identity token
issued by the InjectedIdentity credential will be exchanged for the credentials
matching the role in the `.spec.assumeRoleWithWebIdentity.roleARN` field.

Multiple `ProviderConfigs` can be used to switch between credentials when more than
one target account is being reconciled by the aws provider.
14 changes: 14 additions & 0 deletions apis/v1beta1/providerconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ type ProviderConfigSpec struct {
// AssumeRole defines the options for assuming an IAM role
AssumeRole *AssumeRoleOptions `json:"assumeRole,omitempty"`

// AssumeRoleWithWebIdentity defines the options for assuming an IAM role with a Web Identity
AssumeRoleWithWebIdentity *AssumeRoleWithWebIdentityOptions `json:"assumeRoleWithWebIdentity,omitempty"`

// AssumeRoleARN to assume with provider credentials
// This setting will be deprecated. Use the roleARN field under assumeRole instead.
// +optional
Expand Down Expand Up @@ -91,6 +94,17 @@ type AssumeRoleOptions struct {
TransitiveTagKeys []string `json:"transitiveTagKeys,omitempty"`
}

// AssumeRoleWithWebIdentityOptions define the options for assuming an IAM Role
// Fields are similar to the STS WebIdentityRoleOptions in the AWS SDK
type AssumeRoleWithWebIdentityOptions struct {
// AssumeRoleARN to assume with provider credentials
RoleARN *string `json:"roleARN,omitempty"`

// RoleSessionName is the session name, if you wish to uniquely identify this session.
// +optional
RoleSessionName string `json:"roleSessionName,omitempty"`
}

// EndpointConfig is used to configure the AWS client for a custom endpoint.
type EndpointConfig struct {
// URL lets you configure the endpoint URL to be used in SDK calls.
Expand Down
25 changes: 25 additions & 0 deletions apis/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions package/crds/aws.crossplane.io_providerconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,18 @@ spec:
setting will be deprecated. Use the roleARN field under assumeRole
instead.
type: string
assumeRoleWithWebIdentity:
description: AssumeRoleWithWebIdentity defines the options for assuming
an IAM role with a Web Identity
properties:
roleARN:
description: AssumeRoleARN to assume with provider credentials
type: string
roleSessionName:
description: RoleSessionName is the session name, if you wish
to uniquely identify this session.
type: string
type: object
credentials:
description: Credentials required to authenticate to this provider.
properties:
Expand Down
Loading

0 comments on commit 2c6a810

Please sign in to comment.