From 868f8302989181039e87512e30e00f534d2dd429 Mon Sep 17 00:00:00 2001 From: Rae Sharp Date: Wed, 4 Mar 2026 17:47:36 -0500 Subject: [PATCH 1/3] Adds AWS auth docs --- .../packages/providers/authentication.md | 499 +-------------- .../providers/aws-auth/_category_.json | 7 + .../providers/aws-auth/aws-access-keys.md | 324 ++++++++++ .../packages/providers/aws-auth/aws-irsa.md | 363 +++++++++++ .../packages/providers/aws-auth/aws-oidc.md | 407 ++++++++++++ .../providers/aws-auth/aws-pod-identity.md | 480 ++++++++++++++ .../providers/aws-auth/aws-web-identity.md | 601 ++++++++++++++++++ .../vale/styles/Microsoft/HeadingAcronyms.yml | 4 + .../styles/Upbound/spelling-exceptions.txt | 3 +- vercel.json | 25 + 10 files changed, 2243 insertions(+), 470 deletions(-) create mode 100644 docs/manuals/packages/providers/aws-auth/_category_.json create mode 100644 docs/manuals/packages/providers/aws-auth/aws-access-keys.md create mode 100644 docs/manuals/packages/providers/aws-auth/aws-irsa.md create mode 100644 docs/manuals/packages/providers/aws-auth/aws-oidc.md create mode 100644 docs/manuals/packages/providers/aws-auth/aws-pod-identity.md create mode 100644 docs/manuals/packages/providers/aws-auth/aws-web-identity.md 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..4308419a8 --- /dev/null +++ b/docs/manuals/packages/providers/aws-auth/aws-irsa.md @@ -0,0 +1,363 @@ +--- +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..e270e1c91 --- /dev/null +++ b/docs/manuals/packages/providers/aws-auth/aws-oidc.md @@ -0,0 +1,407 @@ +--- +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..d0786b242 --- /dev/null +++ b/docs/manuals/packages/providers/aws-auth/aws-web-identity.md @@ -0,0 +1,601 @@ +--- +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. + +## 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 + + +## Overview + +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. + + +## 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 Determine your Crossplane namespace + +```bash +# Set your namespace +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. This is also the most common mistake when configuring your +`providerConfig` — be sure that this value matches what is deployed to your +control plane. +::: + + +### 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 + +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.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 +``` + +:::tip +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 + +Check the ProviderConfig status: + +```bash +kubectl get providerConfig.aws.m default -o yaml +``` + +Test by creating a simple resource (e.g., 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 + +If you need to assume additional roles after the initial WebIdentity authentication (e.g., to access resources in a different AWS account), add an `assumeRoleChain` to your ProviderConfig: + +```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 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 (e.g., `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 does not 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 is 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 (e.g., 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 strictly 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/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", From f2e5808cb123e0ac83c9d5c3fdacc897c9f885dc Mon Sep 17 00:00:00 2001 From: Rae Sharp Date: Wed, 4 Mar 2026 18:03:55 -0500 Subject: [PATCH 2/3] Update WebIdentity --- .../providers/aws-auth/aws-web-identity.md | 120 +++++++++--------- .../styles/gitlab/SubstitutionWarning.yml | 2 +- utils/vale/styles/gitlab/Uppercase.yml | 1 + 3 files changed, 64 insertions(+), 59 deletions(-) diff --git a/docs/manuals/packages/providers/aws-auth/aws-web-identity.md b/docs/manuals/packages/providers/aws-auth/aws-web-identity.md index d0786b242..5ae868752 100644 --- a/docs/manuals/packages/providers/aws-auth/aws-web-identity.md +++ b/docs/manuals/packages/providers/aws-auth/aws-web-identity.md @@ -3,45 +3,60 @@ 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. +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. -## 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 - - -## Overview - -WebIdentity enables credential-free authentication by having the Crossplane provider exchange a web identity token directly with AWS STS: +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. +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 +## 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: + +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 | +| 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. + +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). +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 @@ -92,10 +107,9 @@ export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output t echo "AWS Account ID: $AWS_ACCOUNT_ID" ``` -### 2.2 Determine your Crossplane namespace +### 2.2 Set your Crossplane namespace ```bash -# Set your namespace export CROSSPLANE_NAMESPACE="crossplane-system" ``` @@ -124,16 +138,12 @@ cat > trust-policy.json << EOF } 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. This is also the most common mistake when configuring your -`providerConfig` — be sure that this value matches what is deployed to your -control plane. +upgrades. Make sure this value matches what's deployed in your control plane to +avoid this common mistake. ::: - ### 2.4 Create the IAM role @@ -148,7 +158,7 @@ aws iam create-role \ ### 2.5 Attach permission policies to the role -Attach the policies your Crossplane provider needs. For full access (not recommended for production): +For testing, you can attach full access (not recommended for production): ```bash # Example: Attach AdministratorAccess (for testing only) @@ -241,7 +251,7 @@ spec: EOF ``` -### 4.2 Apply the Provider +### 4.2 Apply the provider ```bash kubectl apply -f provider-aws.yaml @@ -295,7 +305,7 @@ spec: EOF ``` -### 5.2 Option B: Token from a Kubernetes Secret +### 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: @@ -332,22 +342,22 @@ kubectl create secret generic web-identity-token \ kubectl apply -f provider-config.yaml ``` -:::tip +:::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 +## Step 6: Verify the configuration -Check the ProviderConfig status: +### 6.1 Check the ProviderConfig status ```bash kubectl get providerConfig.aws.m default -o yaml ``` -Test by creating a simple resource (e.g., an S3 bucket): +### 6.2 Test with an S3 bucket resource ```yaml apiVersion: s3.aws.m.upbound.io/v1beta1 @@ -362,7 +372,7 @@ spec: name: default ``` -Check the resource status: +### 6.3 Check the resource status ```bash kubectl get buckets.s3.aws.m.upbound.io my-crossplane-test-bucket -o yaml @@ -380,7 +390,10 @@ other CRDs installed on a cluster. ## Optional: Role chaining -If you need to assume additional roles after the initial WebIdentity authentication (e.g., to access resources in a different AWS account), add an `assumeRoleChain` to your ProviderConfig: +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 @@ -396,19 +409,21 @@ spec: - 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 is useful for: +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. +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. +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 @@ -488,7 +503,7 @@ cat > trust-policy.json << EOF EOF ``` -The `upbound-provider-aws-*` wildcard matches service accounts for all provider families (e.g., `upbound-provider-aws-s3-`, `upbound-provider-aws-ec2-`, `upbound-provider-aws-rds-`). +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: @@ -512,12 +527,9 @@ spec: name: webidentity-runtimeconfig ``` - :::note -A single DeploymentRuntimeConfig is shared across all provider families. Each -provider family reads the role ARN from the shared ProviderConfig. +A single DeploymentRuntimeConfig is shared across all provider families. Each provider family reads the role ARN from the shared ProviderConfig. ::: - ## Troubleshooting @@ -534,21 +546,17 @@ 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 does not 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 | +| `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 @@ -581,21 +589,17 @@ POD_NAME=$(kubectl get pods -n $CROSSPLANE_NAMESPACE -l pkg.crossplane.io/revisi kubectl get pod $POD_NAME -n $CROSSPLANE_NAMESPACE -o jsonpath='{.spec.volumes[*].name}' ``` -You should see `aws-iam-token` in the output. If it is missing, verify the Provider has a `runtimeConfigRef` pointing to the DeploymentRuntimeConfig. +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 (e.g., separate roles and tokens for S3, EC2, and RDS operations) +- **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 strictly more secure than storing access keys as Kubernetes secrets - - +- **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/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 From 99fc0f0662d7d87f06104fe95625750e31d7dba1 Mon Sep 17 00:00:00 2001 From: Rae Sharp Date: Wed, 4 Mar 2026 18:17:09 -0500 Subject: [PATCH 3/3] remove breaks --- docs/manuals/packages/providers/aws-auth/aws-irsa.md | 1 - docs/manuals/packages/providers/aws-auth/aws-oidc.md | 1 - 2 files changed, 2 deletions(-) diff --git a/docs/manuals/packages/providers/aws-auth/aws-irsa.md b/docs/manuals/packages/providers/aws-auth/aws-irsa.md index 4308419a8..d89718a6f 100644 --- a/docs/manuals/packages/providers/aws-auth/aws-irsa.md +++ b/docs/manuals/packages/providers/aws-auth/aws-irsa.md @@ -186,7 +186,6 @@ EOF kubectl apply -f deployment-runtime-config.yaml ``` ---- ## Step 4: Install or Update the AWS Provider diff --git a/docs/manuals/packages/providers/aws-auth/aws-oidc.md b/docs/manuals/packages/providers/aws-auth/aws-oidc.md index e270e1c91..1dd297c33 100644 --- a/docs/manuals/packages/providers/aws-auth/aws-oidc.md +++ b/docs/manuals/packages/providers/aws-auth/aws-oidc.md @@ -155,7 +155,6 @@ export ROLE_ARN=$(aws iam get-role --role-name $ROLE_NAME --query "Role.Arn" --o echo "Role ARN: $ROLE_ARN" ``` ---- ## Step 3: Create the ProviderConfig