diff --git a/docs/manuals/packages/providers/authentication.md b/docs/manuals/packages/providers/authentication.md index bef2e8adb..15cc859c5 100644 --- a/docs/manuals/packages/providers/authentication.md +++ b/docs/manuals/packages/providers/authentication.md @@ -10,7 +10,7 @@ This guide covers authentication methods for Upbound Official Providers. Each pr | Provider | Authentication Methods | |----------|----------------------| -| AWS | [Upbound OIDC](#aws-upbound-oidc), [Access Keys](#aws-access-keys), [WebIdentity](#aws-webidentity), [IRSA](#aws-irsa) | +| AWS | [Upbound OIDC][aws-oidc], [Access Keys][aws-keys], [WebIdentity][aws-webidentity], [IRSA][aws-irsa], [EKS Pod Identity][aws-pod-identity] | | Azure | [Upbound OIDC](#azure-upbound-oidc), [Service Principal](#azure-service-principal), [Managed Identity](#azure-managed-identity) | | GCP | [Upbound OIDC](#gcp-upbound-oidc), [Service Account Keys](#gcp-service-account-keys), [OAuth 2.0 Token](#gcp-oauth-token), [Service Account Impersonation](#gcp-service-account-impersonation), [Workload Identity](#gcp-workload-identity) | | Kubernetes | [Upbound Identity](#kubernetes-upbound-identity), [Injected Identity](#kubernetes-injected-identity), [Injected Identity with Cloud Provider Credentials](#kubernetes-injected-identity-cloud) | @@ -26,52 +26,7 @@ The Upbound Official AWS Provider supports multiple authentication methods suita This method is only supported in control planes running on [Upbound Cloud Spaces][upbound-cloud-spaces]. ::: -Upbound authentication uses OpenID Connect (OIDC) to authenticate to AWS without storing credentials in Upbound. - -#### Add Upbound as an OpenID Connect provider - -1. Open the **[AWS IAM console][aws-iam-console]**. -2. Under AWS IAM services, select **[Identity Providers > Add Provider][identity-providers-add-provider]**. -3. Select **OpenID Connect** and use **https://proidc.upbound.io** as the Provider URL and **sts.amazonaws.com** as the Audience. -4. Select **Get thumbprint**. -5. Select **Add provider**. - -#### Create an AWS IAM Role for Upbound - -1. Create an [AWS IAM Role][aws-iam-role] with a **Custom trust policy** for the OIDC connector. - -:::tip -Provide your [AWS account ID][aws-account-id], Upbound organization and control plane names in the JSON Policy below. -::: - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "Federated": "arn:aws:iam::YOUR_AWS_ACCOUNT_ID:oidc-provider/proidc.upbound.io" - }, - "Action": "sts:AssumeRoleWithWebIdentity", - "Condition": { - "StringEquals": { - "proidc.upbound.io:sub": "mcp:ORG_NAME/CONTROL_PLANE_NAME:provider:provider-aws", - "proidc.upbound.io:aud": "sts.amazonaws.com" - } - } - } - ] -} -``` - -2. Attach the permission policies you want for the control plane assuming this role. -3. Name and create the role. -4. View the new role and copy the role ARN. - -#### Create a ProviderConfig - -Create a ProviderConfig to set the provider authentication method to `Upbound`. +Upbound OIDC uses federation via `proidc.upbound.io` to authenticate to AWS without storing credentials. An IAM role in your AWS account trusts the Upbound OIDC provider, scoped to a specific organization and control plane. The provider exchanges a short-lived identity token for temporary AWS credentials at runtime. ```yaml apiVersion: aws.upbound.io/v1beta1 @@ -86,78 +41,13 @@ spec: roleARN: ``` +For a complete setup guide, see [AWS Upbound OIDC Authentication][aws-oidc]. + ### AWS Access Keys {#aws-access-keys} + -Using AWS access keys requires storing the AWS keys as a Kubernetes secret. - -Create or [download your AWS access key][download-your-aws-access-key] ID and secret access key. The format of the text file is: - -```ini -[default] -aws_access_key_id = AKIAIOSFODNN7EXAMPLE -aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY -``` - -
-Authentication keys with SSO - -To generate authentication keys for SSO login, access your organization's AWS SSO portal. - -Select "Command line or programmatic access" - -![AWS SSO screen highlighting the option command line or programmatic access](/img/aws-sso-screen.png) - -Expand "Option 2" and copy the provided AWS credentials. - -![AWS screen showing Option 2 credentials](/img/aws-auth-option2.png) - -Use this as the contents of the `aws-credentials.txt` file. - -Below is an example `aws-credentials.txt` file with SSO authentication. - -```ini -[123456789_AdministratorAccess] -aws_access_key_id=ASIAZBZV2IPKEXAMPLEKEY -aws_secret_access_key=PPF/Wu9vTja98L5t/YNycbzEMEXAMPLEKEY -aws_session_token=ArrGMPb4X3zjshBuQHLa79fyNZ8tDHpi9ogiA8DX6HkKLJxMA6LXcUyMGN6MUe3tYuhRKwdCTkfwt6qCVMT8Ctab//3jMmrV9zXArrGMPb4X3zjshBuQHLa79fyNZ8tDHpi9ogiA8DX6HkKLJxMA6LXcUyMGN6MUe3tYuhRKwdCTkfwt6qCVMT8Ctab//3jMmrV9zXArrGMPb4X3zjshBuQHLa79fyNZ8tDHpi9ogiA8DX6HkKLJxMA6LXcUyMGN6MUe3tYuhRKwdCTkfwt6qCVMT8Ctab//3jMmrV9zXArrGMPb4X3zjshBuQHLa79fyNZ8tDHpi9ogiA8DX6HkKLJxMA6LXcUyMGN6MUe3tYuhRKwdCTkfwt6qCVMT8Ctab//3jMmrV9zXArrGMPb4X3zjshBuQHLa79fyNZ8tDHpi9ogiA8DX6HkKLJxMA6LXcUyMGN6MUe3tYuhRKwdCTkfwt6qCVMT8Ctab//3jMmrV9zXArrGMPb4X3zjshBuQHLa79fyNZ8tDHpi9ogiA8DX6HkKLJxMA6LXcUyMGN6MUe3tYuhRKwdCTkfwt6qCVMT8Ctab//3jMmrV9zXArrGMPb4X3zjshBuQHLa79fyNZ8tDHpi9ogiA8DX6HkKLJxMA6LXcUyMGN6MUe3tYuhRKwdCTkfwt6qCVMT8Ctab//3jMmrV9zX -``` - -:::tip -These credentials are only valid as long as your SSO session. When the credentials expire Crossplane can't monitor or change AWS resources. -::: - -
- -#### Create a Kubernetes secret - -Create the Kubernetes secret with `kubectl create secret generic`: - -```shell -kubectl create secret generic \ -aws-secret \ --n crossplane-system \ ---from-file=my-aws-secret=./aws-credentials.txt -``` - -To create a secret declaratively requires encoding the authentication keys as a base-64 string. - -Create a Secret object with the data containing the secret key name and the base-64 encoded keys: - -```yaml -apiVersion: v1 -kind: Secret -metadata: - name: aws-secret - namespace: crossplane-system -type: Opaque -data: - my-aws-secret: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkID0gQUtJQUlPU0ZPRE5ON0VYQU1QTEUKYXdzX3NlY3JldF9hY2Nlc3Nfa2V5ID0gd0phbHJYVXRuRkVNSS9LN01ERU5HL2JQeFJmaUNZRVhBTVBMRUtFWQ== -``` - -#### Create a ProviderConfig - -Create a ProviderConfig to set the provider authentication method to `Secret`: +Access key authentication stores static IAM credentials as a Kubernetes Secret. The provider reads the secret at runtime to authenticate with AWS APIs. This is the simplest method but requires manual credential rotation and stores long-lived secrets in the cluster. ```yaml apiVersion: aws.upbound.io/v1beta1 @@ -173,145 +63,15 @@ spec: key: my-aws-secret ``` -:::tip -To apply key based authentication by default name the ProviderConfig `default`. -::: - -To selectively apply key based authentication, name the ProviderConfig and apply it when creating managed resources. - -For example, creating a ProviderConfig named `key-based-providerconfig`: - -```yaml -apiVersion: aws.upbound.io/v1beta1 -kind: ProviderConfig -metadata: - name: key-based-providerconfig -spec: - credentials: - source: Secret - secretRef: - namespace: crossplane-system - name: aws-secret - key: my-aws-secret -``` - -Apply the ProviderConfig to a managed resource with a `providerConfigRef`: - -```yaml -apiVersion: s3.aws.upbound.io/v1beta1 -kind: Bucket -metadata: - name: my-s3-bucket -spec: - forProvider: - region: us-east-2 - providerConfigRef: - name: key-based-providerconfig -``` - -#### Role chaining - -To use [AWS IAM role chaining][aws-iam-role-chaining], add an `assumeRoleChain` object to the ProviderConfig. - -Inside the `assumeRoleChain`, list one or more roles to assume, in order: - -```yaml -apiVersion: aws.upbound.io/v1beta1 -kind: ProviderConfig -metadata: - name: default -spec: - credentials: - source: Secret - secretRef: - namespace: crossplane-system - name: aws-secret - key: my-aws-secret - assumeRoleChain: - - roleARN: "arn:aws:iam::111122223333:role/my-custom-role" -``` +For a complete setup guide, see [AWS Access Key Authentication][aws-keys]. ### AWS WebIdentity {#aws-webidentity} -When running in an Amazon managed Kubernetes cluster (EKS), the Provider may use [AssumeRoleWithWebIdentity][assumerolewithwebidentity] for authentication. - -WebIdentity uses an OpenID Connect ID token to authenticate and use a specific AWS IAM role. - :::tip WebIdentity is only supported with Crossplane running in Amazon managed Kubernetes clusters (EKS). ::: -Configuring WebIdentity with the AWS Provider requires: -* An AWS [IAM OIDC Provider][iam-oidc-provider] -* An AWS IAM Role with an editable [trust policy][trust-policy] -* A ProviderConfig to enable WebIdentity authentication - -#### Create an IAM OIDC provider - -WebIdentity relies on the EKS cluster OIDC provider. - -Follow the [AWS instructions][aws-instructions] to create an IAM OIDC provider with your EKS OIDC provider URL. - -#### Edit the IAM role - -Supporting WebIdentity requires matching the EKS OIDC information to the specific role through a role trust policy. - -:::tip -Read the [AWS trust policies blog][aws-trust-policies-blog] for more information on trust policies. -::: - -The trust policy references the OIDC provider ARN and the provider AWS service account. - -In the policy `Principal` enter `"Federated": ""`. - -Add a `Condition` to restrict access to the role to only the Provider's service account. - -The `Condition` uses `StringLike` to generically match the Provider's service account. - -
-Why use a generic match? - -The token used for authentication includes the full name of the AWS Provider's Kubernetes service account. - -The Provider's service account name ends in a hash. If the hash changes the `Condition` doesn't match. - -
- -Enter the string (with quotation marks) `":sub": "system:serviceaccount:upbound-system:provider-aws-*"`. - -:::tip -Be sure to include `:sub` after the OIDC provider ARN. - -The `system:serviceaccount:` matches the namespace where the Provider pod runs. - -By default UXP uses `upbound-system` and Crossplane uses `crossplane-system`. -::: - -The following is a full example trust policy: - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "Federated": "arn:aws:iam::111122223333:oidc-provider/oidc.eks.us-east-2.amazonaws.com/id/5C64F628ACFB6A892CC25AF3B67124C5" - }, - "Action": "sts:AssumeRoleWithWebIdentity", - "Condition": { - "StringLike": { - "oidc.eks.us-east-2.amazonaws.com/id/5C64F628ACFB6A892CC25AF3B67124C5:sub": "system:serviceaccount:crossplane-system:provider-aws-*" - } - } - } - ] -} -``` - -#### Create a ProviderConfig - -Create a ProviderConfig to set the provider authentication method to `WebIdentity`: +WebIdentity uses [AssumeRoleWithWebIdentity][assumerolewithwebidentity] to exchange a projected service account token for temporary AWS credentials. Unlike IRSA, the role ARN and token source are configured per-ProviderConfig via the `tokenConfig` field, so different ProviderConfigs can target different roles and tokens without reinstalling the provider. ```yaml apiVersion: aws.upbound.io/v1beta1 @@ -322,196 +82,20 @@ spec: credentials: source: WebIdentity webIdentity: - roleARN: "arn:aws:iam::111122223333:role/my-custom-role" -``` - -:::tip -To apply WebIdentity authentication by default name the ProviderConfig `default`. -::: - -To selectively apply WebIdentity authentication, name the ProviderConfig and apply it when creating managed resources. - -For example, creating a ProviderConfig named `webid-providerconfig`: - -```yaml -apiVersion: aws.upbound.io/v1beta1 -kind: ProviderConfig -metadata: - name: webid-providerconfig -spec: - credentials: - source: WebIdentity - webIdentity: - roleARN: "arn:aws:iam::111122223333:role/my-custom-role" -``` - -Apply the ProviderConfig to a managed resource with a `providerConfigRef`: - -```yaml -apiVersion: s3.aws.upbound.io/v1beta1 -kind: Bucket -metadata: - name: my-s3-bucket -spec: - forProvider: - region: us-east-2 - providerConfigRef: - name: webid-providerconfig + roleARN: "arn:aws:iam::111122223333:role/my-webidentity-role" + tokenConfig: + source: Filesystem + fs: + path: /var/run/secrets/aws-iam-token/token ``` -#### Role chaining - -To use [AWS IAM role chaining][aws-iam-role-chaining], add an `assumeRoleChain` object to the ProviderConfig. - -Inside the `assumeRoleChain`, list one or more roles to assume, in order: +For a complete setup guide, see [AWS Web Identity Authentication][aws-webidentity]. -```yaml -apiVersion: aws.upbound.io/v1beta1 -kind: ProviderConfig -metadata: - name: webid-providerconfig -spec: - credentials: - source: WebIdentity - webIdentity: - roleARN: "arn:aws:iam::111122223333:role/my-custom-role" - assumeRoleChain: - - roleARN: "arn:aws:iam::111122223333:role/my-assumed-role" -``` ### AWS IRSA {#aws-irsa} -When running in Amazon EKS, the Provider may use [IAM roles for service accounts][aws-iam-roles-for-service-accounts] (IRSA) for authentication. - -IRSA works by using an annotation on a Kubernetes ServiceAccount used by a Pod requesting AWS resources. The annotation matches an AWS IAM Role ARN configured with the desired permissions. - -Configuring IRSA with the AWS Provider requires: -* An AWS [IAM OIDC Provider][iam-oidc-provider] -* An AWS IAM Role with an editable [trust policy][trust-policy] -* A DeploymentRuntimeConfig to add an annotation on the AWS Provider service account -* A ProviderConfig to enable IRSA authentication - -#### Create an IAM OIDC provider - -IRSA relies on the EKS cluster OIDC provider. - -Follow the [AWS instructions][aws-instructions] to create an IAM OIDC provider with your EKS OIDC provider URL. - -#### Edit the IAM role - -Supporting IRSA requires matching the EKS OIDC information to the specific role through a role trust policy. - -:::tip -Read the [AWS trust policies blog][aws-trust-policies-blog] for more information on trust policies. -::: - -The trust policy references the OIDC provider ARN and the provider AWS service account. - -In the policy `Principal` enter `"Federated": ""`. - -Add a `Condition` to restrict access to the role to only the Provider's service account. - -The `Condition` uses `StringLike` to generically match the Provider's service account. - -
-Why use a generic match? - -The token used for authentication includes the full name of the AWS Provider's Kubernetes service account. - -The Provider's service account name ends in a hash. If the hash changes the `Condition` doesn't match. - -
- -Enter the string (with quotation marks) `":sub": "system:serviceaccount:upbound-system:provider-aws-*"`. - -:::tip -Be sure to include `:sub` after the OIDC provider ARN. - -The `system:serviceaccount:` matches the namespace where the Provider pod runs. - -By default UXP uses `upbound-system` and Crossplane uses `crossplane-system`. -::: - -The following is a full example trust policy: - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "Federated": "arn:aws:iam::622346257358:oidc-provider/oidc.eks.us-east-2.amazonaws.com/id/5C64F628ACFB6A892CC25AF3B67124C5" - }, - "Action": "sts:AssumeRoleWithWebIdentity", - "Condition": { - "StringLike": { - "oidc.eks.us-east-2.amazonaws.com/id/5C64F628ACFB6A892CC25AF3B67124C5:sub": "system:serviceaccount:crossplane-system:provider-aws-*" - } - } - } - ] -} -``` - -#### Create a DeploymentRuntimeConfig - -IRSA relies on an annotation on the service account attached to a pod to identify the IAM role to use. - -Crossplane uses a DeploymentRuntimeConfig to apply settings to the provider, including the provider service account. - -Create a DeploymentRuntimeConfig object to apply a custom annotation to the provider service account: - -```yaml -apiVersion: pkg.crossplane.io/v1beta1 -kind: DeploymentRuntimeConfig -metadata: - name: irsa-runtimeconfig -spec: - serviceAccountTemplate: - metadata: - annotations: - eks.amazonaws.com/role-arn: arn:aws:iam::622346257358:role/my-custom-role -``` - -#### Apply the DeploymentRuntimeConfig - -Install or update the provider with a `runtimeConfigRef`: - -```yaml -apiVersion: pkg.crossplane.io/v1 -kind: Provider -metadata: - name: provider-aws-s3 -spec: - package: xpkg.upbound.io/upbound/provider-aws-s3:v2.1.1 - runtimeConfigRef: - name: irsa-runtimeconfig -``` - -After the provider finishes installing, verify Crossplane applied the annotation on the service account from the DeploymentRuntimeConfig. - -:::tip - -Kubernetes applies a unique hash to the end of the service account name. Find the specific service account name with `kubectl get sa -n crossplane-system` for Crossplane or `kubectl get sa -n upbound-system` for UXP. - -::: - -```shell -kubectl describe sa -n crossplane-system provider-aws-s3-dbc7f981d81f -Name: provider-aws-s3-dbc7f981d81f -Namespace: crossplane-system -Labels: -Annotations: eks.amazonaws.com/role-arn: arn:aws:iam::111122223333:role/my-custom-role -# Removed for brevity -``` - -Apply the `runtimeConfig` to each family provider using the same IAM role. - -#### Create a ProviderConfig - -Create a ProviderConfig to set the provider authentication method to `IRSA`: +[IAM Roles for Service Accounts][aws-iam-roles-for-service-accounts] (IRSA) authenticates using an annotation on the provider's Kubernetes ServiceAccount. A DeploymentRuntimeConfig applies the IAM role ARN as an annotation, and the EKS pod identity webhook injects temporary credentials into the provider pod automatically. ```yaml apiVersion: aws.upbound.io/v1beta1 @@ -523,55 +107,25 @@ spec: source: IRSA ``` -:::tip -To apply IRSA authentication by default name the ProviderConfig `default`. -::: +For a complete setup guide, see [AWS IRSA Authentication Setup][aws-irsa]. -To selectively apply IRSA authentication, name the ProviderConfig and apply it when creating managed resources. + +### AWS EKS Pod Identity {#aws-pod-identity} + -For example, creating a ProviderConfig named `irsa-providerconfig`: +[EKS Pod Identity][eks-pod-identity] (EKS 1.24+) authenticates using a standardized `pods.eks.amazonaws.com` trust policy and an explicit association between the provider's ServiceAccount and an IAM role. Unlike IRSA, it doesn't require a cluster-specific OIDC provider, so trust policies are reusable across clusters. ```yaml apiVersion: aws.upbound.io/v1beta1 kind: ProviderConfig metadata: - name: irsa-providerconfig + name: default spec: credentials: - source: IRSA + source: PodIdentity ``` -Apply the ProviderConfig to a managed resource with a `providerConfigRef`: - -```yaml -apiVersion: s3.aws.upbound.io/v1beta1 -kind: Bucket -metadata: - name: my-s3-bucket -spec: - forProvider: - region: us-east-2 - providerConfigRef: - name: irsa-providerconfig -``` - -#### Role chaining - -To use [AWS IAM role chaining][aws-iam-role-chaining], add an `assumeRoleChain` object to the ProviderConfig. - -Inside the `assumeRoleChain`, list one or more roles to assume, in order: - -```yaml -apiVersion: aws.upbound.io/v1beta1 -kind: ProviderConfig -metadata: - name: irsa-providerconfig -spec: - credentials: - source: IRSA - assumeRoleChain: - - roleARN: "arn:aws:iam::111122223333:role/my-assumed-role" -``` +For a complete setup guide, see [AWS EKS Pod Identity Authentication][aws-pod-identity]. ## Azure authentication @@ -2142,6 +1696,8 @@ spec: [aws-instructions]: https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html [aws-trust-policies-blog]: https://aws.amazon.com/blogs/security/how-to-use-trust-policies-with-iam-roles/ [aws-iam-roles-for-service-accounts]: https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html +[eks-pod-identity]: https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html +[eks-pod-identity-agent]: https://docs.aws.amazon.com/eks/latest/userguide/pod-id-agent-setup.html [azure-portal]: https://portal.azure.com/ [microsoft-entra-id]: https://portal.azure.com/#view/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/~/Overview [subscriptions]: https://portal.azure.com/#view/Microsoft_Azure_Billing/SubscriptionsBlade @@ -2169,3 +1725,8 @@ spec: [eks-access-entries]: https://docs.aws.amazon.com/eks/latest/userguide/access-entries.html [aws-auth-configmap]: https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html [eks-pod-identity]: https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html +[aws-oidc]: /manuals/packages/providers/aws-auth/aws-oidc +[aws-keys]: /manuals/packages/providers/aws-auth/aws-access-keys +[aws-webidentity]: /manuals/packages/providers/aws-auth/aws-web-identity +[aws-irsa]: /manuals/packages/providers/aws-auth/aws-irsa +[aws-pod-identity]: /manuals/packages/providers/aws-auth/aws-pod-identity diff --git a/docs/manuals/packages/providers/aws-auth/_category_.json b/docs/manuals/packages/providers/aws-auth/_category_.json new file mode 100644 index 000000000..39b30d9cf --- /dev/null +++ b/docs/manuals/packages/providers/aws-auth/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "AWS Authentication", + "position": 3, + "collapsed": true +} + + diff --git a/docs/manuals/packages/providers/aws-auth/aws-access-keys.md b/docs/manuals/packages/providers/aws-auth/aws-access-keys.md new file mode 100644 index 000000000..d3fd8d5b8 --- /dev/null +++ b/docs/manuals/packages/providers/aws-auth/aws-access-keys.md @@ -0,0 +1,324 @@ +--- +title: AWS Access Key Authentication Setup +sidebar_position: 1 +description: Configure AWS Access Key Authentication for Upbound +--- + +This guide walks through configuring the Upbound Official AWS Provider to +authenticate using AWS access keys stored as a Kubernetes Secret. + +Access key authentication uses static AWS IAM credentials stored as a +Kubernetes Secret. The provider reads the secret and uses the access key ID and +secret access key to authenticate with AWS APIs. This authentication method is the simplest +but requires manual credential rotation and stores +long-lived secrets in the cluster. + +:::warning +When running on EKS, prefer [IRSA], [EKS Pod Identity], or [WebIdentity] over +access keys. These methods use temporary credentials and avoid storing static +secrets in the cluster. +::: + +## Prerequisites + +- A running cluster with Crossplane V2, UXPv2, or a managed Upbound Cloud control plane +- `kubectl` configured to access your control plane +- At least one Upbound AWS Provider installed on the cluster +- AWS CLI installed and configured with administrative access +- AWS IAM credentials (or permissions to create them) + +## Step 1: Create AWS IAM credentials + +### Option A: Create a new IAM user with access keys + +#### 1.1 Create the IAM user + +```bash +aws iam create-user --user-name crossplane-provider +``` + +#### 1.2 Attach permissions to the user + +For testing, attach full access (not recommended for production): + +```bash +# Example: Attach AdministratorAccess (for testing only) +aws iam attach-user-policy \ + --user-name crossplane-provider \ + --policy-arn arn:aws:iam::aws:policy/AdministratorAccess +``` + +**Recommended: Use least-privilege policies** + +```bash +# Example: Attach specific service policies +aws iam attach-user-policy \ + --user-name crossplane-provider \ + --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess + +aws iam attach-user-policy \ + --user-name crossplane-provider \ + --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess + +aws iam attach-user-policy \ + --user-name crossplane-provider \ + --policy-arn arn:aws:iam::aws:policy/AmazonRDSFullAccess +``` + +#### 1.3 Create access keys + +```bash +aws iam create-access-key --user-name crossplane-provider +``` + +This returns JSON output: + +```json +{ + "AccessKey": { + "UserName": "crossplane-provider", + "AccessKeyId": "AKIAIOSFODNN7EXAMPLE", + "Status": "Active", + "SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + "CreateDate": "2024-01-15T12:00:00+00:00" + } +} +``` + +#### 1.4 Create the credentials file + +Using the output from the previous command, create `aws-credentials.txt`: + +```bash +cat > aws-credentials.txt << EOF +[default] +aws_access_key_id = AKIAIOSFODNN7EXAMPLE +aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY +EOF +``` + + +:::warning +The secret access key is only shown once at creation time. Save it securely +before continuing. +::: + + +### Option B: Use existing access keys + +If you already have access keys, create `aws-credentials.txt` directly: + +```ini +[default] +aws_access_key_id = AKIAIOSFODNN7EXAMPLE +aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY +``` + +### Option C: SSO session credentials (temporary) + + +If you use AWS SSO: + +1. Access your organization's AWS SSO portal +2. Select **Command line or programmatic access** +3. Expand **Option 2** and copy the credentials +4. Create `aws-credentials.txt` with the copied content: + +```ini +[123456789_AdministratorAccess] +aws_access_key_id=ASIAZBZV2IPKEXAMPLEKEY +aws_secret_access_key=PPF/Wu9vTja98L5t/YNycbzEMEXAMPLEKEY +aws_session_token=ArrGMPb4X3zjshBuQHLa79fyNZ8t... +``` + +:::warning +SSO credentials are temporary. When they expire, Crossplane loses the ability to +manage AWS resources until you update the secret with fresh credentials. +::: + +## Step 2: Create the Kubernetes Secret + +### 2.1 Create the secret + +```bash +kubectl create secret generic aws-secret \ + -n crossplane-system \ + --from-file=my-aws-secret=./aws-credentials.txt +``` + +### 2.2 Verify the secret + +```bash +kubectl get secret aws-secret -n crossplane-system +``` + +## Step 3: Create the ProviderConfig + +### 3.1 Create the ProviderConfig manifest + +```bash +cat > provider-config.yaml << EOF +apiVersion: aws.m.upbound.io/v1beta1 +kind: ProviderConfig +metadata: + name: default + namespace: default +spec: + credentials: + source: Secret + secretRef: + namespace: crossplane-system + name: aws-secret + key: my-aws-secret +EOF +``` + +### 3.2 Apply the ProviderConfig + +```bash +kubectl apply -f provider-config.yaml +``` + +:::note +Naming the ProviderConfig `default` applies this authentication method +automatically to all AWS managed resources that don't specify a different +`providerConfigRef`. +::: + +## Step 4: Verify the configuration + +### 4.1 Check the ProviderConfig status + +```bash +kubectl get providerConfig.aws.m default -o yaml +``` + +### 4.2 Test with an S3 bucket resource + +```yaml +apiVersion: s3.aws.m.upbound.io/v1beta1 +kind: Bucket +metadata: + name: my-crossplane-test-bucket +spec: + forProvider: + region: us-east-2 + providerConfigRef: + kind: ProviderConfig + name: default +``` + +### 4.3 Check the resource status + +```bash +kubectl get buckets.s3.aws.m.upbound.io my-crossplane-test-bucket -o yaml +``` + +Look for `status.conditions` with `type: Ready` and `status: "True"` to confirm +authentication is working. + + +:::note +We specify `buckets.s3.aws.m.upbound.io` to avoid any potential conflicts with +other CRDs installed on a cluster. +::: + + +## Optional: Named ProviderConfig for selective authentication + +If you need multiple authentication methods or want to explicitly reference the +config, use a named ProviderConfig: + +```yaml +apiVersion: aws.m.upbound.io/v1beta1 +kind: ProviderConfig +metadata: + name: key-based-providerconfig + namespace: default +spec: + credentials: + source: Secret + secretRef: + namespace: crossplane-system + name: aws-secret + key: my-aws-secret +``` + +Reference it in managed resources: + +```yaml +apiVersion: s3.aws.m.upbound.io/v1beta1 +kind: Bucket +metadata: + name: my-s3-bucket +spec: + forProvider: + region: us-east-2 + providerConfigRef: + kind: ProviderConfig + name: key-based-providerconfig +``` + +## Troubleshooting + +### Check provider logs + +```bash +# Find the provider pod +kubectl get pods -n crossplane-system | grep provider-aws + +# View logs +kubectl logs -n crossplane-system -f +``` + +### Common issues + + + + +| Issue | Solution | +|-------|----------| +| `InvalidClientTokenId` | Verify the access key ID is correct and the IAM user exists | +| `SignatureDoesNotMatch` | Check the secret access key for typos or extra whitespace in the credentials file | +| `ExpiredToken` | SSO credentials expired. Regenerate them and update the Kubernetes Secret | +| `AccessDenied` | The IAM user lacks required permissions. Attach the necessary IAM policies | + + + + +### Update credentials + +To rotate or replace credentials, recreate the secret and restart the provider: + +```bash +kubectl delete secret aws-secret -n crossplane-system +kubectl create secret generic aws-secret \ + -n crossplane-system \ + --from-file=my-aws-secret=./aws-credentials.txt + +# Restart provider pods to pick up new credentials +kubectl rollout restart deployment \ + -n crossplane-system \ + -l pkg.crossplane.io/provider=provider-aws +``` + +## Security best practices + + + + + +- **Rotate credentials regularly** - AWS recommends rotating access keys every 90 days; update the Kubernetes Secret after each rotation +- **Use least privilege** - Grant only the permissions the provider needs via IAM policies +- **Avoid committing credentials** - Never commit `aws-credentials.txt` to version control; add it to `.gitignore` +- **Prefer identity-based methods on EKS** - When running on EKS, IRSA, EKS Pod Identity, or WebIdentity are more secure because they use temporary credentials and eliminate static secrets +- **Restrict Secret access** - Use Kubernetes RBAC to limit which service accounts can read the credentials secret + + + + + + +[irsa]: /manuals/packages/providers/aws-auth/aws-irsa +[eks-pod-identity]: /manuals/packages/providers/aws-auth/aws-pod-identity +[webidentity]: /manuals/packages/providers/aws-auth/aws-web-identity diff --git a/docs/manuals/packages/providers/aws-auth/aws-irsa.md b/docs/manuals/packages/providers/aws-auth/aws-irsa.md new file mode 100644 index 000000000..d89718a6f --- /dev/null +++ b/docs/manuals/packages/providers/aws-auth/aws-irsa.md @@ -0,0 +1,362 @@ +--- +title: AWS IRSA Authentication Setup +sidebar_position: 2 +description: Configure AWS IRSA Authentication for Upbound +--- + + +This guide explains how to configure IAM Roles for Service Accounts (IRSA) +authentication between your AWS EKS Crossplane control plane and AWS services. + +IRSA allows Kubernetes pods to assume AWS IAM roles without storing credentials. +IRSA works by: + +1. Annotating a Kubernetes ServiceAccount with an IAM Role ARN +2. AWS validates the pod's identity token against the EKS OIDC provider +3. The pod receives temporary AWS credentials to assume the role + + +## Prerequisites + +- An existing Amazon EKS cluster +- `kubectl` configured to access your EKS cluster +- AWS CLI installed and configured with appropriate permissions +- A control plane (Crossplane V2/UXPv2/Upbound Space managed control plane) on your EKS cluster + +## Step 1: Create an IAM OIDC Provider for Your EKS Cluster + +IRSA requires an IAM OIDC identity provider associated with your EKS cluster. + +### 1.1 Set environment variables + +```bash +export CLUSTER_NAME="your-cluster-name" +export AWS_REGION="us-east-2" +``` + +### 1.2 Get your EKS cluster's OIDC issuer URL + +```bash +export OIDC_URL=$(aws eks describe-cluster \ + --name $CLUSTER_NAME \ + --region $AWS_REGION \ + --query "cluster.identity.oidc.issuer" \ + --output text) + +echo $OIDC_URL +# Example output: https://oidc.eks.us-east-2.amazonaws.com/id/5C64F628ACFB6A892CC25AF3B67124C5 +``` + +### 1.3 Check if OIDC provider already exists + +```bash +export OIDC_ID=$(echo $OIDC_URL | cut -d '/' -f 5) +aws iam list-open-id-connect-providers | grep $OIDC_ID +``` + +### 1.4 Create the OIDC provider (if it doesn't exist) + +```bash +eksctl utils associate-iam-oidc-provider \ + --cluster $CLUSTER_NAME \ + --region $AWS_REGION \ + --approve +``` + + +## Step 2: Create an IAM role with trust policy + +### 2.1 Get your AWS Account ID + +```bash +export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) +echo "AWS Account ID: $AWS_ACCOUNT_ID" +``` + +### 2.2 Set your Crossplane namespace + +```bash +export CROSSPLANE_NAMESPACE="crossplane-system" +``` + +### 2.3 Create the trust policy document + +Create a file named `trust-policy.json`: + +```bash +cat > trust-policy.json << EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/oidc.eks.${AWS_REGION}.amazonaws.com/id/${OIDC_ID}" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringLike": { + "oidc.eks.${AWS_REGION}.amazonaws.com/id/${OIDC_ID}:sub": "system:serviceaccount:${CROSSPLANE_NAMESPACE}:upbound-provider-aws-*" + } + } + } + ] +} +EOF +``` + +:::note +The `StringLike` condition with `upbound-provider-aws-*` is used because the AWS +Provider's service account name includes a hash that may change between +upgrades. Make sure this value matches what's deployed in your control plane to +avoid this common mistake. +::: + + +### 2.4 Create the IAM role + +```bash +export ROLE_NAME="crossplane-provider-aws" + +aws iam create-role \ + --role-name $ROLE_NAME \ + --assume-role-policy-document file://trust-policy.json \ + --description "IAM role for Crossplane AWS Provider using IRSA" +``` + +### 2.5 Attach permission policies to the role + +For testing, you can attach full access (not recommended for production): + +```bash +# Example: Attach AdministratorAccess (for testing only) +aws iam attach-role-policy \ + --role-name $ROLE_NAME \ + --policy-arn arn:aws:iam::aws:policy/AdministratorAccess +``` + +**Recommended: Use least-privilege policies** + +```bash +# Example: Attach specific service policies +aws iam attach-role-policy \ + --role-name $ROLE_NAME \ + --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess + +aws iam attach-role-policy \ + --role-name $ROLE_NAME \ + --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess + +aws iam attach-role-policy \ + --role-name $ROLE_NAME \ + --policy-arn arn:aws:iam::aws:policy/AmazonRDSFullAccess +``` + +### 2.6 Get the role ARN + +```bash +export ROLE_ARN=$(aws iam get-role --role-name $ROLE_NAME --query "Role.Arn" --output text) +echo "Role ARN: $ROLE_ARN" +``` + +## Step 3: Create a DeploymentRuntimeConfig + +The DeploymentRuntimeConfig annotates the provider's service account with the +IAM role ARN. + +### 3.1 Create the DeploymentRuntimeConfig manifest + +```bash +cat > deployment-runtime-config.yaml << EOF +apiVersion: pkg.crossplane.io/v1beta1 +kind: DeploymentRuntimeConfig +metadata: + name: irsa-runtimeconfig +spec: + serviceAccountTemplate: + metadata: + annotations: + eks.amazonaws.com/role-arn: ${ROLE_ARN} +EOF +``` + +### 3.2 Apply the DeploymentRuntimeConfig + +```bash +kubectl apply -f deployment-runtime-config.yaml +``` + + +## Step 4: Install or Update the AWS Provider + +### 4.1 Create the Provider manifest with runtimeConfigRef + +```bash +cat > provider-aws.yaml << EOF +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-aws-s3 +spec: + package: xpkg.upbound.io/upbound/provider-aws-s3:v2.3.0 + runtimeConfigRef: + name: irsa-runtimeconfig +EOF +``` + +### 4.2 Apply the provider + +```bash +kubectl apply -f provider-aws.yaml +``` + +Wait for the Provider to become healthy. + +### 4.3 Verify the service account annotation + +```bash +# Get the provider's service account name +SA_NAME=$(kubectl get sa -n $CROSSPLANE_NAMESPACE -o name | grep provider-aws-s3) + +# Describe the service account and verify the annotation +kubectl describe $SA_NAME -n $CROSSPLANE_NAMESPACE | grep -A2 "Annotations" +``` + +Expected output should show: +``` +Annotations: eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/crossplane-provider-aws +``` + +## Step 5: Create the ProviderConfig + +### 5.1 Create the ProviderConfig manifest + +```bash +cat > provider-config.yaml << EOF +apiVersion: aws.m.upbound.io/v1beta1 +kind: ProviderConfig +metadata: + name: default + namespace: default +spec: + credentials: + source: IRSA +EOF +``` + +### 5.2 Apply the ProviderConfig + +```bash +kubectl apply -f provider-config.yaml +``` + +## Step 6: Verify the Configuration + +### 6.1 Check the ProviderConfig status + +```bash +kubectl get providerConfig.aws.m default -o yaml +``` + +### 6.2 Test with an S3 bucket resource + +```yaml +apiVersion: s3.aws.m.upbound.io/v1beta1 +kind: Bucket +metadata: + name: my-crossplane-test-bucket +spec: + forProvider: + region: us-east-2 + providerConfigRef: + kind: ProviderConfig + name: default +``` + +### 6.3 Check the resource status + +```bash +kubectl get buckets.s3.aws.m.upbound.io my-crossplane-test-bucket -o yaml +``` + +Look for `status.conditions` with `type: Ready` and `status: "True"` to confirm authentication is working. + + +:::note +We specify `buckets.s3.aws.m.upbound.io` to avoid any potential conflicts with +other CRDs installed on a cluster. +::: + + +## Optional: Role chaining + +If you need to assume additional roles after the initial IRSA authentication, +add an `assumeRoleChain` to your ProviderConfig: + +```yaml +apiVersion: aws.m.upbound.io/v1beta1 +kind: ProviderConfig +metadata: + name: default + namespace: default +spec: + credentials: + source: IRSA + assumeRoleChain: + - roleARN: "arn:aws:iam::111122223333:role/my-assumed-role" +``` + +## Optional: Configure multiple provider families + +When using multiple AWS provider families (S3, EC2, RDS, etc.), apply the same `runtimeConfigRef` to each provider: + +```yaml +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-aws-ec2 +spec: + package: xpkg.upbound.io/upbound/provider-aws-ec2:v2.1.1 + runtimeConfigRef: + name: irsa-runtimeconfig +--- +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-aws-rds +spec: + package: xpkg.upbound.io/upbound/provider-aws-rds:v2.1.1 + runtimeConfigRef: + name: irsa-runtimeconfig +``` + + +## Troubleshooting + +### Check provider logs + +```bash +kubectl logs -n $CROSSPLANE_NAMESPACE -l pkg.crossplane.io/provider=provider-aws-s3 +``` + +### Common issues + +| Issue | Solution | +|-------|----------| +| Provider pod fails to start or authenticate | Check the provider pod logs with `kubectl logs -n $CROSSPLANE_NAMESPACE -l pkg.crossplane.io/provider=provider-aws-s3` | +| `AccessDenied` errors | Verify the trust policy has the correct OIDC provider ARN; ensure the service account namespace matches the trust policy condition; check that the IAM role has required permission policies attached | +| Service account not annotated | Verify the DeploymentRuntimeConfig is correctly applied; restart the provider by deleting its pods; check if the provider references the correct runtimeConfigRef | +| OIDC thumbprint issues | If you see certificate validation errors, verify the OIDC issuer URL with `aws eks describe-cluster --name $CLUSTER_NAME --region $AWS_REGION --query "cluster.identity.oidc.issuer"` | + +## Security best practices + + +Upbound recommends the following best practices when configuring IRSA: + +- **No stored credentials** - IRSA uses projected service account tokens and temporary `STS` credentials, eliminating static secrets +- **Use least privilege** - Grant only the permissions the provider needs via IAM policies +- **Scope trust policies narrowly** - Use the most specific `sub` condition that matches your provider service accounts; avoid overly broad wildcards +- **Audit role assumptions** - Enable AWS CloudTrail to log all `AssumeRoleWithWebIdentity` calls for this role +- **Rotate the OIDC thumbprint** - If your EKS cluster's OIDC certificate is rotated, update the IAM OIDC provider thumbprint +- **Prefer IRSA over Access Keys** - When running on EKS, IRSA is more secure than storing access keys as Kubernetes secrets + diff --git a/docs/manuals/packages/providers/aws-auth/aws-oidc.md b/docs/manuals/packages/providers/aws-auth/aws-oidc.md new file mode 100644 index 000000000..1dd297c33 --- /dev/null +++ b/docs/manuals/packages/providers/aws-auth/aws-oidc.md @@ -0,0 +1,406 @@ +--- +title: AWS Upbound OIDC Authentication +sidebar_position: 2 +description: Configure AWS OIDC Authentication for Upbound +--- + + +This guide explains how to configure Upbound OIDC authentication between your +managed control plane on Upbound Cloud Spaces and AWS services. OpenID Connect +federation allows Upbound to connect to AWS without storing credentials. + +:::important +Upbound OIDC authentication is only supported on control planes running in **Upbound Cloud Spaces**. +::: + +## Prerequisites + +- A control plane running on Upbound Cloud Spaces +- `kubectl` configured to access your control plane +- AWS CLI installed and configured with administrative access +- At least one Upbound AWS Provider installed on the control plane +- Your Upbound **organization name** and **control plane name** + + +## Step 1: Add Upbound as an OpenID Connect Provider in AWS + +### 1.1 Set environment variables + +```bash +export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) +export ORG_NAME="your-upbound-org" +export CONTROL_PLANE_NAME="your-control-plane" + +echo "AWS Account ID: $AWS_ACCOUNT_ID" +``` + +#### Option A: Create OIDC provider with AWS CLI + +```bash +# Get the thumbprint for proidc.upbound.io +THUMBPRINT=$(openssl s_client -servername proidc.upbound.io \ + -showcerts -connect proidc.upbound.io:443 /dev/null \ + | openssl x509 -fingerprint -noout \ + | sed 's/://g' | cut -d= -f2 | tr '[:upper:]' '[:lower:]') + +aws iam create-open-id-connect-provider \ + --url https://proidc.upbound.io \ + --client-id-list sts.amazonaws.com \ + --thumbprint-list $THUMBPRINT +``` + + +#### Option B: Create OIDC provider in AWS Console + + +1. Open the [AWS IAM Console](https://console.aws.amazon.com/iam/) +2. Navigate to **Identity Providers** > **Add Provider** +3. Select **OpenID Connect** +4. Enter Provider URL: `https://proidc.upbound.io` +5. Set Audience: `sts.amazonaws.com` +6. Click **Get thumbprint** +7. Click **Add provider** + +### 1.4 Verify the OIDC provider + +```bash +aws iam list-open-id-connect-providers | grep proidc.upbound.io +``` + +## Step 2: Create an IAM role with trust policy + +### 2.1 Create the trust policy document + +Create a file named `trust-policy.json`. This policy allows only your specific +control plane and provider to assume the role: + +```bash +cat > trust-policy.json << EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/proidc.upbound.io" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + "proidc.upbound.io:sub": "mcp:${ORG_NAME}/${CONTROL_PLANE_NAME}:provider:provider-aws", + "proidc.upbound.io:aud": "sts.amazonaws.com" + } + } + } + ] +} +EOF +``` + +:::note +The `sub` condition follows the format +`mcp:ORG_NAME/CONTROL_PLANE_NAME:provider:provider-aws`. This scopes the trust +to a single control plane. Update the organization and control plane names to +match your environment. +::: + + +### 2.2 Create the IAM role + +```bash +export ROLE_NAME="upbound-oidc-provider-aws" + +aws iam create-role \ + --role-name $ROLE_NAME \ + --assume-role-policy-document file://trust-policy.json \ + --description "IAM role for Crossplane AWS Provider using Upbound OIDC" +``` + +### 2.3 Attach permission policies to the role + +For testing, you can attach full access (not recommended for production): + +```bash +# Example: Attach AdministratorAccess (for testing only) +aws iam attach-role-policy \ + --role-name $ROLE_NAME \ + --policy-arn arn:aws:iam::aws:policy/AdministratorAccess +``` +:::important +In your production environment, adhere to the principle of least privilege when +you apply policies to your roles. +::: + +**Recommended: Use least-privilege policies** + +```bash +# Example: Attach specific service policies +aws iam attach-role-policy \ + --role-name $ROLE_NAME \ + --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess + +aws iam attach-role-policy \ + --role-name $ROLE_NAME \ + --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess + +aws iam attach-role-policy \ + --role-name $ROLE_NAME \ + --policy-arn arn:aws:iam::aws:policy/AmazonRDSFullAccess +``` + +### 2.4 Get the role ARN + +```bash +export ROLE_ARN=$(aws iam get-role --role-name $ROLE_NAME --query "Role.Arn" --output text) +echo "Role ARN: $ROLE_ARN" +``` + + +## Step 3: Create the ProviderConfig + +Unlike IRSA, Upbound OIDC doesn't require a DeploymentRuntimeConfig. The +ProviderConfig alone handles authentication. + +### 3.1 Create the ProviderConfig manifest + +```bash +cat > provider-config.yaml << EOF +apiVersion: aws.m.upbound.io/v1beta1 +kind: ProviderConfig +metadata: + name: default + namespace: default +spec: + credentials: + source: Upbound + upbound: + webIdentity: + roleARN: ${ROLE_ARN} +EOF +``` + +### 3.2 Apply the ProviderConfig + +```bash +kubectl apply -f provider-config.yaml +``` +:::note +Naming the ProviderConfig `default` applies this authentication method +automatically to all AWS managed resources that don't specify a different +`providerConfigRef`. +::: + +## Step 4: Verify the Configuration + +### 4.1 Check the ProviderConfig status + +```bash +kubectl get providerConfig.aws.m default -o yaml +``` + +### 4.2 Test with an S3 bucket resource + +```yaml +apiVersion: s3.aws.m.upbound.io/v1beta1 +kind: Bucket +metadata: + name: my-crossplane-test-bucket +spec: + forProvider: + region: us-east-2 + providerConfigRef: + kind: ProviderConfig + name: default +``` + +### 4.3 Check the resource status + +```bash +kubectl get buckets.s3.aws.m.upbound.io my-crossplane-test-bucket -o yaml +``` + +Look for `status.conditions` with `type: Ready` and `status: "True"` to confirm authentication is working. + + + +## Optional: Role chaining + +To assume additional roles after the initial OIDC authentication, add an +`assumeRoleChain` to your ProviderConfig. + +The example below shows how to access resources in a different AWS account: + +```yaml +apiVersion: aws.m.upbound.io/v1beta1 +kind: ProviderConfig +metadata: + name: default + namespace: default +spec: + credentials: + source: Upbound + upbound: + webIdentity: + roleARN: + assumeRoleChain: + - roleARN: "arn:aws:iam::111122223333:role/my-cross-account-role" +``` + + +The provider first authenticates via Upbound OIDC, then sequentially assumes +each role in the chain. This is useful for: + + +- **Cross-account access**: Managing resources in AWS accounts different from the one hosting the OIDC trust +- **Privilege separation**: Using a minimal initial role that escalates to a more permissive role for specific operations + +:::note +The target role in the chain must have a trust policy that allows +`sts:AssumeRole` from the initial OIDC role. +::: + +## Optional: Named ProviderConfig for selective authentication + +If you need multiple authentication methods or want to explicitly reference the config: + +```yaml +apiVersion: aws.m.upbound.io/v1beta1 +kind: ProviderConfig +metadata: + name: upbound-oidc-config + namespace: default +spec: + credentials: + source: Upbound + upbound: + webIdentity: + roleARN: arn:aws:iam::123456789012:role/upbound-oidc-provider-aws +``` + +Reference it in managed resources: + +```yaml +apiVersion: s3.aws.m.upbound.io/v1beta1 +kind: Bucket +metadata: + name: my-s3-bucket +spec: + forProvider: + region: us-east-2 + providerConfigRef: + kind: ProviderConfig + name: upbound-oidc-config +``` + +## Optional: Configure multiple control plane access + +To grant multiple control planes access to the same IAM role, add additional +conditions to the trust policy: + +### Explicit control plane names (recommended) + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "arn:aws:iam::123456789012:oidc-provider/proidc.upbound.io" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + "proidc.upbound.io:aud": "sts.amazonaws.com" + }, + "ForAnyValue:StringEquals": { + "proidc.upbound.io:sub": [ + "mcp:my-org/control-plane-dev:provider:provider-aws", + "mcp:my-org/control-plane-staging:provider:provider-aws", + "mcp:my-org/control-plane-prod:provider:provider-aws" + ] + } + } + } + ] +} +``` + +### Wildcard for all organization control planes + +You can also use `StringLike` with a wildcard to trust all control planes in an organization: + +```json +"Condition": { + "StringEquals": { + "proidc.upbound.io:aud": "sts.amazonaws.com" + }, + "StringLike": { + "proidc.upbound.io:sub": "mcp:my-org/*:provider:provider-aws" + } +} +``` +:::warning +Wildcards broaden the trust scope. Any control plane in the organization can +assume this role. Use explicit control plane names in production environments. +::: + +## Troubleshooting + +### Check provider logs + +```bash +# Find the provider pod +kubectl get pods -n crossplane-system | grep provider-aws + +# View logs +kubectl logs -n crossplane-system -f +``` + +### Common issues + + +| Issue | Solution | +|-------|----------| +| `AccessDenied` when assuming role | Verify the trust policy has the correct OIDC provider ARN and the `sub` condition matches your org/control plane names exactly | +| `InvalidIdentityToken` | Confirm the OIDC provider `proidc.upbound.io` is registered in your AWS account | +| `ExpiredTokenException` | The web identity token has expired; this is usually transient and retries automatically | +| Provider pod not authenticating | Set `credentials.source` to `Upbound` (not `IRSA` or `Secret`) | +| Wrong control plane name in sub | The `sub` field is case-sensitive; double-check org and control plane names in the trust policy | +| Role chaining `AccessDenied` | Verify the target role's trust policy allows `sts:AssumeRole` from the initial OIDC role ARN | + + + +### Verify OIDC Provider Configuration + +```bash +# List OIDC providers and find the Upbound one +aws iam list-open-id-connect-providers + +# Get details of the Upbound OIDC provider +aws iam get-open-id-connect-provider \ + --open-id-connect-provider-arn arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/proidc.upbound.io +``` + +### Verify trust policy + +```bash +aws iam get-role --role-name $ROLE_NAME --query "Role.AssumeRolePolicyDocument" --output json +``` + +Confirm the output contains the correct `Federated` principal and `sub` condition. + +## Security best practices + +Use the following best practices when setting up OIDC in your environment: + + +- **No stored credentials** - Upbound OIDC uses federated identity tokens, eliminating static credentials entirely +- **Scope trust narrowly** - Always specify the exact control plane name in the `sub` condition rather than using wildcards +- **Use least privilege** - Grant only the permissions the provider needs via IAM policies +- **Audit role assumptions** - Enable AWS CloudTrail to log all `AssumeRoleWithWebIdentity` calls for this role +- **Review trust policies regularly** - Remove control plane entries when decommissioned +- **Prefer Upbound OIDC over Access Keys** - When running on Upbound Cloud Spaces, this method is more secure than storing access keys as Kubernetes secrets + + + diff --git a/docs/manuals/packages/providers/aws-auth/aws-pod-identity.md b/docs/manuals/packages/providers/aws-auth/aws-pod-identity.md new file mode 100644 index 000000000..3238540f1 --- /dev/null +++ b/docs/manuals/packages/providers/aws-auth/aws-pod-identity.md @@ -0,0 +1,480 @@ +--- +title: AWS EKS Pod Identity Authentication +sidebar_position: 2 +description: Configure AWS EKS Pod Identity Authentication for Upbound +--- + +This guide explains how to configure EKS Pod Identity authentication between your +AWS EKS Crossplane control planes and AWS services. Unlike IRSA, EKS Pod +Identity doesn't require an OIDC provider. This method uses the EKS Pod +Identity Agent and the built-in `pods.eks.amazonaws.com` service principal for +managing IAM roles and credentials. + +## Prerequisites + +- An existing Amazon EKS cluster running Kubernetes 1.24 or later +- `kubectl` configured to access your EKS cluster +- AWS CLI installed and configured with appropriate permissions +- Crossplane or UXPv2 installed on your EKS cluster +- At least one Upbound AWS Provider installed on the cluster + +### IRSA and Pod Identity comparison + +| | IRSA | EKS Pod Identity | +|---|---|---| +| Requires OIDC provider | Yes | No | +| Role association | ServiceAccount annotation | EKS Pod Identity association (API/CLI) | +| Credential delivery | EKS pod identity webhook injects environment variables | Pod Identity Agent intercepts credential requests | +| Trust policy | Custom per-cluster OIDC trust policy | Standardized `pods.eks.amazonaws.com` trust policy | +| Cross-account | Requires OIDC provider per cluster | Reusable trust policy across clusters | +| ProviderConfig `source` | `IRSA` | `PodIdentity` | + +Pod Identity is simpler to set up than IRSA due to its standardized trust policy +and lack of cluster-specific OIDC provider ARNs. + + + +## Step 1: Install the EKS Pod Identity Agent + + + +The Pod Identity Agent is an EKS add-on that must be installed on your cluster. + + +### 1.1 Set environment variables + +```bash +export CLUSTER_NAME="your-cluster-name" +export AWS_REGION="us-east-2" +``` + +### 1.2 Install the add-on + +```bash +aws eks create-addon \ + --cluster-name $CLUSTER_NAME \ + --addon-name eks-pod-identity-agent \ + --region $AWS_REGION +``` + +### 1.3 Verify the agent is running + +```bash +kubectl get daemonset eks-pod-identity-agent -n kube-system +``` + +Expected output should show the agent running on all nodes: + +``` +NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE +eks-pod-identity-agent 3 3 3 3 3 +``` + +## Step 2: Create an IAM role with trust policy + +### 2.1 Get your AWS Account ID + +```bash +export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) +echo "AWS Account ID: $AWS_ACCOUNT_ID" +``` + +### 2.2 Create the trust policy document + +Unlike IRSA, the Pod Identity trust policy uses the standardized +`pods.eks.amazonaws.com` service principal. This trust policy works across all +EKS clusters without modification: + +```bash +cat > trust-policy.json << EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "pods.eks.amazonaws.com" + }, + "Action": [ + "sts:AssumeRole", + "sts:TagSession" + ] + } + ] +} +EOF +``` + +:::note +The `sts:TagSession` action is required for EKS Pod Identity. The agent tags +sessions with cluster and namespace metadata that can be used in IAM condition +keys for additional access control. +::: + + +### 2.3 Create the IAM role + +```bash +export ROLE_NAME="crossplane-provider-aws-pod-identity" + +aws iam create-role \ + --role-name $ROLE_NAME \ + --assume-role-policy-document file://trust-policy.json \ + --description "IAM role for Crossplane AWS Provider using EKS Pod Identity" +``` + +### 2.4 Attach permission policies to the role + +Attach the policies your Crossplane provider needs. For full access (not +recommended for production): + +```bash +# Example: Attach AdministratorAccess (for testing only) +aws iam attach-role-policy \ + --role-name $ROLE_NAME \ + --policy-arn arn:aws:iam::aws:policy/AdministratorAccess +``` + +**Recommended: Use least-privilege policies** + +```bash +# Example: Attach specific service policies +aws iam attach-role-policy \ + --role-name $ROLE_NAME \ + --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess + +aws iam attach-role-policy \ + --role-name $ROLE_NAME \ + --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess + +aws iam attach-role-policy \ + --role-name $ROLE_NAME \ + --policy-arn arn:aws:iam::aws:policy/AmazonRDSFullAccess +``` + +### 2.5 Get the role ARN + +```bash +export ROLE_ARN=$(aws iam get-role --role-name $ROLE_NAME --query "Role.Arn" --output text) +echo "Role ARN: $ROLE_ARN" +``` + +## Step 3: Create a DeploymentRuntimeConfig + + +EKS Pod Identity matches pods to roles by ServiceAccount **name** and +**namespace**. The provider's autogenerated ServiceAccount name includes a +random hash, so you must use a DeploymentRuntimeConfig to set a predictable +ServiceAccount name that matches the Pod Identity association you'll create in +Step 4. + + + +### 3.1 Determine your Crossplane namespace + +```bash +export CROSSPLANE_NAMESPACE="crossplane-system" +``` + +### 3.2 Create the DeploymentRuntimeConfig manifest + +```bash +export SA_NAME="provider-aws-pod-identity" + +cat > deployment-runtime-config.yaml << EOF +apiVersion: pkg.crossplane.io/v1beta1 +kind: DeploymentRuntimeConfig +metadata: + name: pod-identity-runtimeconfig +spec: + serviceAccountTemplate: + metadata: + name: ${SA_NAME} +EOF +``` + +### 3.3 Apply the DeploymentRuntimeConfig + +```bash +kubectl apply -f deployment-runtime-config.yaml +``` + +## Step 4: Create the EKS Pod Identity association + +The association links the ServiceAccount name and namespace to the IAM role. + +### 4.1 Create the association + +```bash +aws eks create-pod-identity-association \ + --cluster-name $CLUSTER_NAME \ + --namespace $CROSSPLANE_NAMESPACE \ + --service-account $SA_NAME \ + --role-arn $ROLE_ARN \ + --region $AWS_REGION +``` + +### 4.2 Verify the association + +```bash +aws eks list-pod-identity-associations \ + --cluster-name $CLUSTER_NAME \ + --region $AWS_REGION +``` + +You should see your association in the output with the correct namespace, +service account, and role ARN. + +## Step 5: Install or Update the AWS Provider + +### 5.1 Create the Provider manifest with runtimeConfigRef + +```bash +cat > provider-aws.yaml << EOF +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-aws-s3 +spec: + package: xpkg.upbound.io/upbound/provider-aws-s3:v2.3.0 + runtimeConfigRef: + name: pod-identity-runtimeconfig +EOF +``` + +### 5.2 Apply the Provider + +```bash +kubectl apply -f provider-aws.yaml +``` + +Wait for the Provider to become healthy. + +### 5.3 Verify the service account name + +```bash +kubectl get sa -n $CROSSPLANE_NAMESPACE $SA_NAME +``` + +Confirm the ServiceAccount exists and its name matches the Pod Identity +association. + +## Step 6: Create the ProviderConfig + +### 6.1 Create the ProviderConfig manifest + +```bash +cat > provider-config.yaml << EOF +apiVersion: aws.m.upbound.io/v1beta1 +kind: ProviderConfig +metadata: + name: default + namespace: default +spec: + credentials: + source: PodIdentity +EOF +``` + +### 6.2 Apply the ProviderConfig + +```bash +kubectl apply -f provider-config.yaml +``` +:::note +Naming the ProviderConfig `default` applies this authentication method +automatically to all AWS managed resources that don't specify a different +`providerConfigRef`. +::: + +## Step 7: Verify the Configuration + +Check the ProviderConfig status: + +```bash +kubectl get providerConfig.aws.m default -o yaml +``` + +Test by creating an S3 bucket: +```yaml +apiVersion: s3.aws.m.upbound.io/v1beta1 +kind: Bucket +metadata: + name: my-crossplane-test-bucket +spec: + forProvider: + region: us-east-2 + providerConfigRef: + kind: ProviderConfig + name: default +``` + +Check the resource status: + +```bash +kubectl get buckets.s3.aws.m.upbound.io my-crossplane-test-bucket -o yaml +``` + +Look for `status.conditions` with `type: Ready` and `status: "True"` to confirm authentication is working. + + +:::note +We specify `buckets.s3.aws.m.upbound.io` to avoid any potential conflicts with +other CRDs installed on a cluster. +::: + + +## Optional: Role chaining + +To assume additional roles after the initial OIDC authentication, add an +`assumeRoleChain` to your ProviderConfig. + +The example below shows how to access resources in a different AWS account: + + +```yaml +apiVersion: aws.m.upbound.io/v1beta1 +kind: ProviderConfig +metadata: + name: default + namespace: default +spec: + credentials: + source: PodIdentity + assumeRoleChain: + - roleARN: "arn:aws:iam::111122223333:role/my-cross-account-role" +``` + +The provider first authenticates via Pod Identity, then sequentially assumes each role in the chain. This method is useful for: + +- **Cross-account access**: Managing resources in AWS accounts different from the one hosting the EKS cluster +- **Privilege separation**: Using a minimal initial role that escalates to a more permissive role for specific operations + +:::note +The target role in the chain must have a trust policy that allows +`sts:AssumeRole` from the initial Pod Identity role. +::: + +## Optional: Configure multiple provider families + +When using multiple AWS provider families (S3, EC2, RDS, etc.), each provider +needs a Pod Identity association. You can either: + +**Option A: Shared ServiceAccount name** + +Use the same `serviceAccountTemplate.metadata.name` across all providers so a +single Pod Identity association covers them all: + +```yaml +apiVersion: pkg.crossplane.io/v1beta1 +kind: DeploymentRuntimeConfig +metadata: + name: pod-identity-runtimeconfig +spec: + serviceAccountTemplate: + metadata: + name: provider-aws-pod-identity +``` + +Apply the same `runtimeConfigRef` to each provider: + +```yaml +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-aws-ec2 +spec: + package: xpkg.upbound.io/upbound/provider-aws-ec2:v2.3.0 + runtimeConfigRef: + name: pod-identity-runtimeconfig +--- +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-aws-rds +spec: + package: xpkg.upbound.io/upbound/provider-aws-rds:v2.3.0 + runtimeConfigRef: + name: pod-identity-runtimeconfig +``` +:::note +With this approach, all provider family pods share the same ServiceAccount name. +Since EKS Pod Identity matches on namespace + ServiceAccount name, a single +association covers all providers. +::: + +**Option B: Separate associations per provider** + +Create distinct +ServiceAccount names and Pod Identity associations for each provider family. +This enables per-provider IAM role isolation but requires more configuration. + +## Troubleshooting + +### Check provider logs + +```bash +# Find the provider pod +kubectl get pods -n $CROSSPLANE_NAMESPACE | grep provider-aws + +# View logs +kubectl logs -n $CROSSPLANE_NAMESPACE -f +``` + +### Common issues + +| Issue | Solution | +|-------|----------| +| `AccessDenied` when calling AWS APIs | Verify the Pod Identity association has the correct namespace, ServiceAccount name, and role ARN | +| Provider pod not receiving credentials | Confirm the `eks-pod-identity-agent` DaemonSet is running on the node where the provider pod is scheduled | +| ServiceAccount name mismatch | Check that the DeploymentRuntimeConfig `serviceAccountTemplate.metadata.name` matches the Pod Identity association's service account | +| Provider pod not authenticating | Set `credentials.source` to `PodIdentity` (not `IRSA`, `Secret`, or `WebIdentity`) | +| Role chaining `AccessDenied` | Verify the target role's trust policy allows `sts:AssumeRole` from the initial Pod Identity role ARN | +| `sts:TagSession` errors | Confirm the IAM role's trust policy includes the `sts:TagSession` action | + + +### Verify the Pod Identity agent + +```bash +# Check agent pods are running +kubectl get pods -n kube-system -l app.kubernetes.io/name=eks-pod-identity-agent + +# Check agent logs for errors +kubectl logs -n kube-system -l app.kubernetes.io/name=eks-pod-identity-agent --tail=50 +``` + +### Verify the Pod Identity association + +```bash +aws eks list-pod-identity-associations \ + --cluster-name $CLUSTER_NAME \ + --region $AWS_REGION \ + --query "associations[?serviceAccount=='${SA_NAME}']" +``` + +### Verify the trust policy + +```bash +aws iam get-role --role-name $ROLE_NAME --query "Role.AssumeRolePolicyDocument" --output json +``` + +Confirm the output contains the `pods.eks.amazonaws.com` service principal with +both `sts:AssumeRole` and `sts:TagSession` actions. + +## Security best practices +Use the following best practices when setting up OIDC in your environment: + + + + + +- **No stored credentials** - Pod Identity uses the EKS Pod Identity Agent to inject temporary credentials, eliminating static secrets +- **No OIDC provider required** - Reduces the IAM configuration surface compared to IRSA; no cluster-specific OIDC trust policies to manage +- **Standardized trust policy** - The `pods.eks.amazonaws.com` service principal trust policy is portable across clusters and doesn't contain cluster-specific identifiers +- **Use least privilege** - Grant only the permissions the provider needs via IAM policies +- **Use session tags for fine-grained access** - Pod Identity tags sessions with `eks-cluster-arn`, `kubernetes-namespace`, and `kubernetes-service-account` attributes that can be used in IAM policy conditions for additional access control +- **Audit role assumptions** - Enable AWS CloudTrail to log all `AssumeRole` calls from the `pods.eks.amazonaws.com` service principal +- **Prefer Pod Identity or IRSA over Access Keys** - When running on EKS, identity-based methods are strictly more secure than storing access keys as Kubernetes secrets + + + diff --git a/docs/manuals/packages/providers/aws-auth/aws-web-identity.md b/docs/manuals/packages/providers/aws-auth/aws-web-identity.md new file mode 100644 index 000000000..5ae868752 --- /dev/null +++ b/docs/manuals/packages/providers/aws-auth/aws-web-identity.md @@ -0,0 +1,605 @@ +--- +title: AWS Web Identity Authentication +sidebar_position: 2 +description: Configure AWS Web Identity Authentication for Upbound +--- + + + +This guide provides step-by-step instructions to configure WebIdentity +authentication between your Crossplane control plane running on Amazon EKS and +AWS services. This method uses the EKS cluster's OpenID Connect provider to +exchange Kubernetes service account tokens for temporary AWS credentials without +storing static secrets. + +WebIdentity enables credential-free authentication by having the Crossplane +provider exchange a web identity token directly with AWS STS: +1. The EKS cluster's OIDC provider is registered as a trusted identity provider in AWS IAM +2. An IAM role trusts this OIDC provider, scoped to the provider's service account +3. The provider reads a web identity token and calls `sts:AssumeRoleWithWebIdentity` with the role ARN and token configured in the ProviderConfig +4. AWS returns temporary credentials the provider uses to manage resources + +The token source is configurable per-ProviderConfig via the `tokenConfig` API. +Tokens can be read from a filesystem path or a Kubernetes Secret. + +## How WebIdentity differs from IRSA + + +Both methods run on EKS and use the same underlying OIDC federation mechanism, +but they differ in how the role ARN and token are communicated to the provider: + + +| | IRSA | WebIdentity | +|---|---|---| +| Role ARN specified in | ServiceAccount annotation (via DeploymentRuntimeConfig) | ProviderConfig | +| Token source | Injected by EKS pod identity webhook | Configurable per-ProviderConfig (`tokenConfig`) | +| Requires DeploymentRuntimeConfig | Yes | Yes, to project a token with the `sts.amazonaws.com` audience | +| Multiple roles without restarting pods | No | Yes, each ProviderConfig can target a different role and token | +| ProviderConfig `source` | `IRSA` | `WebIdentity` | + + +WebIdentity is useful when you want to control the role ARN and token at the +ProviderConfig level rather than at the provider installation level, or when you +need multiple ProviderConfigs pointing to different roles and token sources. + + + +## Prerequisites + +- An existing Amazon EKS cluster +- `kubectl` configured to access your EKS cluster +- AWS CLI installed and configured with appropriate permissions +- A control plane (Crossplane V2/UXPv2/Upbound Space managed control plane) on your EKS cluster + +## Step 1: Create an IAM OIDC Provider for Your EKS Cluster + +WebIdentity requires an IAM OIDC identity provider associated with your EKS +cluster. This step is identical to the IRSA setup. If you have already +configured an OIDC provider for your cluster, skip to [Step +2](#step-2-create-an-iam-role-with-trust-policy). + +### 1.1 Set environment variables + +```bash +export CLUSTER_NAME="your-cluster-name" +export AWS_REGION="us-east-2" +``` + +### 1.2 Get your EKS cluster's OIDC issuer URL + +```bash +export OIDC_URL=$(aws eks describe-cluster \ + --name $CLUSTER_NAME \ + --region $AWS_REGION \ + --query "cluster.identity.oidc.issuer" \ + --output text) + +echo $OIDC_URL +# Example output: https://oidc.eks.us-east-2.amazonaws.com/id/5C64F628ACFB6A892CC25AF3B67124C5 +``` + +### 1.3 Check if OIDC provider already exists + +```bash +# Extract the OIDC ID from the URL +export OIDC_ID=$(echo $OIDC_URL | cut -d '/' -f 5) + +# Check if the OIDC provider exists +aws iam list-open-id-connect-providers | grep $OIDC_ID +``` + +### 1.4 Create the OIDC provider (if it doesn't exist) + +```bash +eksctl utils associate-iam-oidc-provider \ + --cluster $CLUSTER_NAME \ + --region $AWS_REGION \ + --approve +``` + + +## Step 2: Create an IAM role with trust policy + +### 2.1 Get your AWS Account ID + +```bash +export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) +echo "AWS Account ID: $AWS_ACCOUNT_ID" +``` + +### 2.2 Set your Crossplane namespace + +```bash +export CROSSPLANE_NAMESPACE="crossplane-system" +``` + +### 2.3 Create the trust policy document + +Create a file named `trust-policy.json`: + +```bash +cat > trust-policy.json << EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/oidc.eks.${AWS_REGION}.amazonaws.com/id/${OIDC_ID}" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringLike": { + "oidc.eks.${AWS_REGION}.amazonaws.com/id/${OIDC_ID}:sub": "system:serviceaccount:${CROSSPLANE_NAMESPACE}:upbound-provider-aws-*" + } + } + } + ] +} +EOF +``` +:::note +The `StringLike` condition with `upbound-provider-aws-*` is used because the AWS +Provider's service account name includes a hash suffix that may change between +upgrades. Make sure this value matches what's deployed in your control plane to +avoid this common mistake. +::: + +### 2.4 Create the IAM role + +```bash +export ROLE_NAME="crossplane-provider-aws-webidentity" + +aws iam create-role \ + --role-name $ROLE_NAME \ + --assume-role-policy-document file://trust-policy.json \ + --description "IAM role for Crossplane AWS Provider using WebIdentity" +``` + +### 2.5 Attach permission policies to the role + +For testing, you can attach full access (not recommended for production): + +```bash +# Example: Attach AdministratorAccess (for testing only) +aws iam attach-role-policy \ + --role-name $ROLE_NAME \ + --policy-arn arn:aws:iam::aws:policy/AdministratorAccess +``` + +**Recommended: Use least-privilege policies** + +```bash +# Example: Attach specific service policies +aws iam attach-role-policy \ + --role-name $ROLE_NAME \ + --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess + +aws iam attach-role-policy \ + --role-name $ROLE_NAME \ + --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess + +aws iam attach-role-policy \ + --role-name $ROLE_NAME \ + --policy-arn arn:aws:iam::aws:policy/AmazonRDSFullAccess +``` + +### 2.6 Get the role ARN + +```bash +export ROLE_ARN=$(aws iam get-role --role-name $ROLE_NAME --query "Role.Arn" --output text) +echo "Role ARN: $ROLE_ARN" +``` + + +## Step 3: Create a DeploymentRuntimeConfig + +WebIdentity requires the provider pod to have a projected service account token with the `sts.amazonaws.com` audience. The default Kubernetes projected token uses the API server audience, which AWS STS rejects. A DeploymentRuntimeConfig projects a token volume with the correct audience into the provider pod. + +### 3.1 Create the DeploymentRuntimeConfig manifest + +```bash +cat > deployment-runtime-config.yaml << EOF +apiVersion: pkg.crossplane.io/v1beta1 +kind: DeploymentRuntimeConfig +metadata: + name: webidentity-runtimeconfig +spec: + deploymentTemplate: + spec: + selector: {} + template: + spec: + containers: + - name: package-runtime + volumeMounts: + - name: aws-iam-token + mountPath: /var/run/secrets/aws-iam-token + readOnly: true + volumes: + - name: aws-iam-token + projected: + sources: + - serviceAccountToken: + audience: sts.amazonaws.com + expirationSeconds: 86400 + path: token +EOF +``` + +### 3.2 Apply the DeploymentRuntimeConfig + +```bash +kubectl apply -f deployment-runtime-config.yaml +``` + + +## Step 4: Install or Update the AWS Provider + +### 4.1 Create the Provider manifest with runtimeConfigRef + +```bash +cat > provider-aws.yaml << EOF +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: upbound-provider-aws-s3 +spec: + package: xpkg.upbound.io/upbound/provider-aws-s3:v2.3.0 + runtimeConfigRef: + name: webidentity-runtimeconfig +EOF +``` + +### 4.2 Apply the provider + +```bash +kubectl apply -f provider-aws.yaml +``` + +Wait for the Provider to become healthy. + +### 4.3 Verify the projected token volume + +Confirm the provider pod has the `aws-iam-token` volume: + +```bash +POD_NAME=$(kubectl get pods -n $CROSSPLANE_NAMESPACE -l pkg.crossplane.io/revision -o jsonpath='{.items[0].metadata.name}') +kubectl get pod $POD_NAME -n $CROSSPLANE_NAMESPACE -o jsonpath='{.spec.volumes[*].name}' +``` + +You should see `aws-iam-token` in the output. + + +## Step 5: Create the ProviderConfig + +The role ARN and token source are specified directly in the ProviderConfig, and the provider handles the token exchange itself. + +The `tokenConfig` field controls where the provider reads the web identity token from: + +| Token Source | `tokenConfig.source` | Use case | +|---|---|---| +| Filesystem | `Filesystem` | Token projected into the pod via the DeploymentRuntimeConfig volume (recommended) | +| Kubernetes Secret | `Secret` | Token managed externally and stored in a Secret | + +### 5.1 Option A: Token from a filesystem path (recommended) + +Use `tokenConfig.source: Filesystem` to read the token from the projected volume created by the DeploymentRuntimeConfig in Step 3: + +```bash +cat > provider-config.yaml << EOF +apiVersion: aws.m.upbound.io/v1beta1 +kind: ProviderConfig +metadata: + name: default + namespace: default +spec: + credentials: + source: WebIdentity + webIdentity: + roleARN: ${ROLE_ARN} + tokenConfig: + source: Filesystem + fs: + path: /var/run/secrets/aws-iam-token/token +EOF +``` + +### 5.2 Option B: Token from a Kubernetes secret + +Use `tokenConfig.source: Secret` to read the web identity token from a Kubernetes Secret. This allows each ProviderConfig to use a different token: + +```yaml +apiVersion: aws.m.upbound.io/v1beta1 +kind: ProviderConfig +metadata: + name: default + namespace: default +spec: + credentials: + source: WebIdentity + webIdentity: + roleARN: arn:aws:iam::123456789012:role/my-webidentity-role + tokenConfig: + source: Secret + secretRef: + key: token + name: web-identity-token + namespace: default +``` + +Create the Secret containing the token: + +```bash +kubectl create secret generic web-identity-token \ + --from-literal=token="" \ + -n default +``` + +### 5.3 Apply the ProviderConfig + +```bash +kubectl apply -f provider-config.yaml +``` + +:::note +Naming the ProviderConfig `default` applies this authentication method +automatically to all AWS managed resources that don't specify a different +`providerConfigRef`. +::: + + +## Step 6: Verify the configuration + +### 6.1 Check the ProviderConfig status + +```bash +kubectl get providerConfig.aws.m default -o yaml +``` + +### 6.2 Test with an S3 bucket resource + +```yaml +apiVersion: s3.aws.m.upbound.io/v1beta1 +kind: Bucket +metadata: + name: my-crossplane-test-bucket +spec: + forProvider: + region: us-east-2 + providerConfigRef: + kind: ProviderConfig + name: default +``` + +### 6.3 Check the resource status + +```bash +kubectl get buckets.s3.aws.m.upbound.io my-crossplane-test-bucket -o yaml +``` + +Look for `status.conditions` with `type: Ready` and `status: "True"` to confirm authentication is working. + + +:::note +We specify `buckets.s3.aws.m.upbound.io` to avoid any potential conflicts with +other CRDs installed on a cluster. +::: + + + +## Optional: Role chaining + +To assume additional roles after the initial OIDC authentication, add an +`assumeRoleChain` to your ProviderConfig. + +The example below shows how to access resources in a different AWS account: + +```yaml +apiVersion: aws.m.upbound.io/v1beta1 +kind: ProviderConfig +metadata: + name: default +spec: + credentials: + source: WebIdentity + webIdentity: + roleARN: "arn:aws:iam::111122223333:role/my-webidentity-role" + assumeRoleChain: + - roleARN: "arn:aws:iam::444455556666:role/my-cross-account-role" +``` + +The provider first authenticates via WebIdentity, then sequentially assumes each role in the chain. This method is useful for: + +- **Cross-account access**: Managing resources in AWS accounts different from the one hosting the EKS cluster +- **Privilege separation**: Using a minimal initial role that escalates to a more permissive role for specific operations + +:::note +The target role in the chain must have a trust policy that allows `sts:AssumeRole` from the initial WebIdentity role. +::: + + +## Optional: Named ProviderConfig for selective authentication + +WebIdentity's per-ProviderConfig configuration is its key advantage. Each +ProviderConfig can target a different role and token source without changing the +provider installation or restarting pods. + +```yaml +apiVersion: aws.m.upbound.io/v1beta1 +kind: ProviderConfig +metadata: + name: webidentity-s3-admin + namespace: default +spec: + credentials: + source: WebIdentity + webIdentity: + roleARN: arn:aws:iam::123456789012:role/crossplane-s3-admin + tokenConfig: + source: Secret + secretRef: + key: token + name: s3-admin-token + namespace: default +--- +apiVersion: aws.m.upbound.io/v1beta1 +kind: ProviderConfig +metadata: + name: webidentity-ec2-admin + namespace: default +spec: + credentials: + source: WebIdentity + webIdentity: + roleARN: arn:aws:iam::123456789012:role/crossplane-ec2-admin + tokenConfig: + source: Secret + secretRef: + key: token + name: ec2-admin-token + namespace: default +``` + +Reference them in managed resources: + +```yaml +apiVersion: s3.aws.m.upbound.io/v1beta1 +kind: Bucket +metadata: + name: my-s3-bucket +spec: + forProvider: + region: us-east-2 + providerConfigRef: + kind: ProviderConfig + name: webidentity-s3-admin +``` + + +## Optional: Configure multiple provider families + +When using multiple AWS provider families (S3, EC2, RDS, etc.), the trust policy must allow each provider's service account to assume the role. Use a broad wildcard: + +```bash +cat > trust-policy.json << EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/oidc.eks.${AWS_REGION}.amazonaws.com/id/${OIDC_ID}" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringLike": { + "oidc.eks.${AWS_REGION}.amazonaws.com/id/${OIDC_ID}:sub": "system:serviceaccount:${CROSSPLANE_NAMESPACE}:upbound-provider-aws-*" + } + } + } + ] +} +EOF +``` + +The `upbound-provider-aws-*` wildcard matches service accounts for all provider families (`upbound-provider-aws-s3-`, `upbound-provider-aws-ec2-`, `upbound-provider-aws-rds-`). + +Apply the same `runtimeConfigRef` to each provider so the projected token volume is available in all provider pods: + +```yaml +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: upbound-provider-aws-ec2 +spec: + package: xpkg.upbound.io/upbound/provider-aws-ec2:v2.3.0 + runtimeConfigRef: + name: webidentity-runtimeconfig +--- +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: upbound-provider-aws-rds +spec: + package: xpkg.upbound.io/upbound/provider-aws-rds:v2.3.0 + runtimeConfigRef: + name: webidentity-runtimeconfig +``` + +:::note +A single DeploymentRuntimeConfig is shared across all provider families. Each provider family reads the role ARN from the shared ProviderConfig. +::: + + +## Troubleshooting + +### Check provider logs + +```bash +# Find the provider pod +kubectl get pods -n $CROSSPLANE_NAMESPACE | grep provider-aws + +# View logs +kubectl logs -n $CROSSPLANE_NAMESPACE -f +``` + +### Common issues + +| Issue | Solution | +|-------|----------| +| `AccessDenied` when assuming role | Verify the trust policy has the correct OIDC provider ARN and the `sub` condition matches the provider's service account | +| `InvalidIdentityToken` | Confirm the EKS OIDC provider is registered in IAM; check that the OIDC ID in the trust policy matches your cluster | +| `InvalidIdentityToken: Incorrect token audience` | The token doesn't have the `sts.amazonaws.com` audience. Verify the DeploymentRuntimeConfig projects a `serviceAccountToken` with `audience: sts.amazonaws.com` and the Provider has a `runtimeConfigRef` pointing to it | +| `ExpiredTokenException` | The projected service account token has expired; this is usually transient and retries automatically | +| Provider pod not authenticating | Ensure `credentials.source` is set to `WebIdentity` (not `IRSA`, `Secret`, or `Upbound`) | +| Service account name mismatch | Check the actual SA name with `kubectl get sa -n $CROSSPLANE_NAMESPACE` and verify the trust policy wildcard matches it | +| Role chaining `AccessDenied` | Verify the target role's trust policy allows `sts:AssumeRole` from the initial WebIdentity role ARN | +| `tokenConfig` Secret not found | Verify the Secret name, namespace, and key match the `tokenConfig.secretRef` in the ProviderConfig | +| `tokenConfig` Filesystem token not found | Confirm the token file is mounted into the provider pod at the path specified in `tokenConfig.fs.path` | + +### Verify the OIDC provider + +```bash +# List OIDC providers and find your EKS cluster's provider +aws iam list-open-id-connect-providers + +# Get details +aws iam get-open-id-connect-provider \ + --open-id-connect-provider-arn arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/oidc.eks.${AWS_REGION}.amazonaws.com/id/${OIDC_ID} +``` + +### Verify trust policy + +```bash +aws iam get-role --role-name $ROLE_NAME --query "Role.AssumeRolePolicyDocument" --output json +``` + +Confirm the output contains the correct `Federated` principal and `sub` condition matching your provider's service account. + +### Verify the projected service account token + +The provider pod must have the `aws-iam-token` projected volume from the DeploymentRuntimeConfig. Confirm it exists: + +```bash +# Get a provider pod name +POD_NAME=$(kubectl get pods -n $CROSSPLANE_NAMESPACE -l pkg.crossplane.io/revision -o jsonpath='{.items[0].metadata.name}' 2>/dev/null | grep provider-aws) + +# Check for the projected token volume mount +kubectl get pod $POD_NAME -n $CROSSPLANE_NAMESPACE -o jsonpath='{.spec.volumes[*].name}' +``` + +You should see `aws-iam-token` in the output. If it's missing, verify the Provider has a `runtimeConfigRef` pointing to the DeploymentRuntimeConfig. + + +## Security best practices + +- **No stored credentials** - WebIdentity uses web identity tokens and temporary STS credentials, eliminating long-lived static secrets +- **Use `tokenConfig` over environment variables** - The `tokenConfig` API supersedes the deprecated `AWS_WEB_IDENTITY_TOKEN_FILE` and `AWS_ROLE_ARN` environment variable approach. Always prefer `tokenConfig` for new configurations +- **Use least privilege** - Grant only the permissions the provider needs via IAM policies +- **Scope trust policies narrowly** - Use the most specific `sub` condition that matches your provider service accounts; avoid overly broad wildcards +- **Leverage multiple ProviderConfigs** - Use WebIdentity's per-ProviderConfig role and token targeting to implement fine-grained access control like separate roles and tokens for S3, EC2, and RDS operations +- **Audit role assumptions** - Enable AWS CloudTrail to log all `AssumeRoleWithWebIdentity` calls for this role +- **Prefer WebIdentity or IRSA over Access Keys** - When running on EKS, both OIDC-based methods are more secure than storing access keys as Kubernetes secrets + + diff --git a/utils/vale/styles/Microsoft/HeadingAcronyms.yml b/utils/vale/styles/Microsoft/HeadingAcronyms.yml index 8ef014f66..65bbface7 100644 --- a/utils/vale/styles/Microsoft/HeadingAcronyms.yml +++ b/utils/vale/styles/Microsoft/HeadingAcronyms.yml @@ -34,3 +34,7 @@ exceptions: - CA - AI - UI + - URL + - ID + - IRSA + - SSO diff --git a/utils/vale/styles/Upbound/spelling-exceptions.txt b/utils/vale/styles/Upbound/spelling-exceptions.txt index 1b77a532d..928bb74e3 100644 --- a/utils/vale/styles/Upbound/spelling-exceptions.txt +++ b/utils/vale/styles/Upbound/spelling-exceptions.txt @@ -194,5 +194,6 @@ xrd aws onboarding XRCs - +ARNs +autogenerated diff --git a/utils/vale/styles/gitlab/SubstitutionWarning.yml b/utils/vale/styles/gitlab/SubstitutionWarning.yml index ed9dab6b2..900282d9e 100644 --- a/utils/vale/styles/gitlab/SubstitutionWarning.yml +++ b/utils/vale/styles/gitlab/SubstitutionWarning.yml @@ -18,7 +18,7 @@ swap: deselect: clear deselected: cleared distro: distribution - file name: filename +# file name: filename filesystem: file system GFM: GLFM info: information diff --git a/utils/vale/styles/gitlab/Uppercase.yml b/utils/vale/styles/gitlab/Uppercase.yml index d0ca9d553..f0e78526d 100644 --- a/utils/vale/styles/gitlab/Uppercase.yml +++ b/utils/vale/styles/gitlab/Uppercase.yml @@ -248,3 +248,4 @@ exceptions: - VPA - WPA - AGE + - STS diff --git a/vercel.json b/vercel.json index 877180e04..c40c6850b 100644 --- a/vercel.json +++ b/vercel.json @@ -250,6 +250,31 @@ "destination": "/manuals/packages/providers/authentication#aws-authentication", "permanent": true }, + { + "source": "/manuals/packages/providers/authentication#aws-upbound-oidc", + "destination": "/manuals/packages/providers/aws-auth/aws-oidc", + "permanent": true + }, + { + "source": "/manuals/packages/providers/authentication#aws-access-keys", + "destination": "/manuals/packages/providers/aws-auth/aws-access-keys", + "permanent": true + }, + { + "source": "/manuals/packages/providers/authentication#aws-webidentity", + "destination": "/manuals/packages/providers/aws-auth/aws-web-identity", + "permanent": true + }, + { + "source": "/manuals/packages/providers/authentication#aws-irsa", + "destination": "/manuals/packages/providers/aws-auth/aws-irsa", + "permanent": true + }, + { + "source": "/manuals/packages/providers/authentication#aws-pod-identity", + "destination": "/manuals/packages/providers/aws-auth/aws-pod-identity", + "permanent": true + }, { "source": "/providers/provider-aws(/)?", "destination": "/manuals/packages/providers/provider-aws",