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(assumeWebIdentityRole): support AssumeRoleWithWebIdentity arn swap #1258

Merged
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
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))
jessesanford marked this conversation as resolved.
Show resolved Hide resolved
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