From d65513854646474aa2722ad9f5d92207fd4dc5e6 Mon Sep 17 00:00:00 2001 From: Christopher Haar Date: Tue, 27 Jan 2026 18:06:48 +0100 Subject: [PATCH 1/3] docs(guides): add crossplane with workload-identity guide Signed-off-by: Christopher Haar --- .../crossplane-with-workload-identity.md | 787 ++++++++++++++++++ 1 file changed, 787 insertions(+) create mode 100644 content/v2.1/guides/crossplane-with-workload-identity.md diff --git a/content/v2.1/guides/crossplane-with-workload-identity.md b/content/v2.1/guides/crossplane-with-workload-identity.md new file mode 100644 index 000000000..9513aa707 --- /dev/null +++ b/content/v2.1/guides/crossplane-with-workload-identity.md @@ -0,0 +1,787 @@ +--- +title: Crossplane with Workload Identity +weight: 205 +description: Configure Crossplane to pull packages from cloud provider container registries using workload identity +--- + +When running Crossplane on managed Kubernetes clusters (EKS, AKS, GKE), you can leverage Kubernetes Workload Identity to grant Crossplane access to pull packages from private cloud container registries. This allows Crossplane to install providers, functions, and configurations from registries like AWS ECR, Azure ACR, and Google Artifact Registry without managing static credentials. + +{{< hint "important" >}} +This guide configures the **Crossplane package manager** to pull packages from private registries. However, packages reference container images that run as separate pods (providers and functions). + +**Two-step image pull process:** +1. **Crossplane package manager** pulls the package, extracts the package contents (CRDs, XRDs) and creates deployments +2. **Kubernetes nodes** pull the runtime container images when creating provider/function pods + +This guide only covers step 1. For step 2, ensure your Kubernetes nodes have permissions to pull images from the private registry. This is typically configured at the cluster level: +- **AWS EKS**: Node IAM role with ECR pull permissions +- **Azure AKS**: Kubelet managed identity with `AcrPull` role +- **GCP GKE**: Node service account with Artifact Registry reader role + +Without node-level access, package installation succeeds but pods fail with `ImagePullBackOff`. +{{< /hint >}} + +## Overview + +To enable Crossplane package manager access to private registries, configure service account annotations during installation. The `crossplane` service account in the `crossplane-system` namespace requires specific annotations for each cloud provider: + +- **AWS EKS**: IAM Roles for Service Accounts (IRSA) +- **Azure AKS**: Azure Workload Identity +- **Google Cloud GKE**: GKE Workload Identity + +Select your cloud provider below for detailed setup instructions: + +{{< tabs >}} + +{{< tab "AWS EKS" >}} + +## AWS EKS Integration + +Configure Crossplane to pull packages from Amazon ECR using IAM Roles for Service Accounts (IRSA). + +### Prerequisites + +- An Amazon EKS cluster with OIDC provider enabled +- AWS CLI installed and configured +- `kubectl` configured to access your EKS cluster +- Permissions to create IAM roles and policies + +### Enable OIDC Provider + +If your EKS cluster doesn't have an OIDC provider, enable it: + +```bash +eksctl utils associate-iam-oidc-provider \ + --cluster= \ + --approve +``` + +Verify the OIDC provider: + +```bash +aws eks describe-cluster \ + --name \ + --query "cluster.identity.oidc.issuer" \ + --output text +``` + +### Create IAM Policy for ECR Access + +Create an IAM policy that grants permissions to pull images from ECR: + +```bash +cat > crossplane-ecr-policy.json <::repository/*" + } + ] +} +EOF + +aws iam create-policy \ + --policy-name CrossplaneECRPolicy \ + --policy-document file://crossplane-ecr-policy.json +``` + +{{< hint "note" >}} +Replace `` and `` with your AWS region and account ID. You can restrict the `Resource` to specific repositories if needed. +{{< /hint >}} + +### Create IAM Role with Trust Policy + +Create an IAM role that can be assumed by the Crossplane service account: + +```bash +export CLUSTER_NAME= +export AWS_REGION= +export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) +export OIDC_PROVIDER=$(aws eks describe-cluster --name $CLUSTER_NAME --region $AWS_REGION --query "cluster.identity.oidc.issuer" --output text | sed -e "s/^https:\/\///") + +cat > trust-policy.json <}} + +{{< tab "Azure AKS" >}} + +## Azure AKS Integration + +Configure Crossplane to pull packages from Azure Container Registry (ACR) using Azure Workload Identity. + +### Prerequisites + +- An AKS cluster with Workload Identity enabled +- Azure CLI installed and configured +- `kubectl` configured to access your AKS cluster +- Permissions to create Azure managed identities and role assignments + +### Enable Workload Identity on AKS + +If your AKS cluster doesn't have Workload Identity enabled, update it: + +```bash +export RESOURCE_GROUP= +export CLUSTER_NAME= + +az aks update \ + --resource-group $RESOURCE_GROUP \ + --name $CLUSTER_NAME \ + --enable-oidc-issuer \ + --enable-workload-identity +``` + +Get the OIDC issuer URL: + +```bash +export AKS_OIDC_ISSUER=$(az aks show \ + --resource-group $RESOURCE_GROUP \ + --name $CLUSTER_NAME \ + --query "oidcIssuerProfile.issuerUrl" \ + --output tsv) + +echo $AKS_OIDC_ISSUER +``` + +### Create Azure Managed Identity + +Create a managed identity for Crossplane: + +```bash +export IDENTITY_NAME=crossplane-acr-identity + +az identity create \ + --name $IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP + +export USER_ASSIGNED_CLIENT_ID=$(az identity show \ + --name $IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP \ + --query 'clientId' \ + --output tsv) + +export USER_ASSIGNED_OBJECT_ID=$(az identity show \ + --name $IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP \ + --query 'principalId' \ + --output tsv) + +echo "Client ID: $USER_ASSIGNED_CLIENT_ID" +echo "Object ID: $USER_ASSIGNED_OBJECT_ID" +``` + +### Assign ACR Pull Role + +Grant the managed identity permission to pull from ACR: + +```bash +export ACR_NAME= + +export ACR_ID=$(az acr show \ + --name $ACR_NAME \ + --query 'id' \ + --output tsv) + +az role assignment create \ + --assignee-object-id $USER_ASSIGNED_OBJECT_ID \ + --assignee-principal-type ServicePrincipal \ + --role AcrPull \ + --scope $ACR_ID +``` + +### Create Federated Identity Credential + +Create a federated identity credential that establishes trust between the managed identity and the Kubernetes service account: + +```bash +az identity federated-credential create \ + --name crossplane-federated-credential \ + --identity-name $IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP \ + --issuer $AKS_OIDC_ISSUER \ + --subject system:serviceaccount:crossplane-system:crossplane \ + --audience api://AzureADTokenExchange +``` + +### Install Crossplane with Workload Identity Configuration + +Get the tenant ID: + +```bash +export AZURE_TENANT_ID=$(az account show --query tenantId --output tsv) +``` + +Install Crossplane with the workload identity annotations and label: + +```bash +helm upgrade --install crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --create-namespace \ + --set "serviceAccount.customAnnotations.azure\.workload\.identity/client-id=$USER_ASSIGNED_CLIENT_ID" \ + --set "serviceAccount.customAnnotations.azure\.workload\.identity/tenant-id=$AZURE_TENANT_ID" \ + --set-string 'customLabels.azure\.workload\.identity/use=true' +``` + +{{< hint "note" >}} +Azure Workload Identity requires: +- Service account annotations for the client ID and tenant ID +- Label `azure.workload.identity/use: "true"` on pods (applied via `customLabels`) + +The `customLabels` setting applies the label to all Crossplane resources. The Azure Workload Identity webhook uses this label on pods to inject the necessary environment variables and token volumes. Use `--set-string` to ensure the value is treated as a string rather than a boolean. +{{< /hint >}} + +### Verify Configuration + +Check that the service account has the correct annotations: + +```bash +kubectl get sa crossplane -n crossplane-system -o yaml +``` + +Expected output should include: + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + azure.workload.identity/client-id: + azure.workload.identity/tenant-id: + name: crossplane + namespace: crossplane-system +``` + +Check that the deployment has the required labels: + +```bash +kubectl get deployment crossplane -n crossplane-system -o yaml +``` + +Expected output should include: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + azure.workload.identity/use: "true" + name: crossplane + namespace: crossplane-system +spec: + template: + metadata: + labels: + azure.workload.identity/use: "true" +``` + +### Test Package Installation from ACR + +Once configured, you can install Crossplane packages (Providers, Functions, Configurations) from your ACR. Here's an example using a Provider: + +```bash +kubectl apply -f - <}} + +{{< tab "Google Cloud GKE" >}} + +## Google Cloud GKE Integration + +Configure Crossplane to pull packages from Google Artifact Registry using GKE Workload Identity. + +### Prerequisites + +- A GKE cluster with Workload Identity enabled +- `gcloud` CLI installed and configured +- `kubectl` configured to access your GKE cluster +- Permissions to create service accounts and IAM bindings + +### Enable Workload Identity on GKE + +If your GKE cluster doesn't have Workload Identity enabled, create a new cluster with it enabled or update an existing cluster: + +**New cluster:** +```bash +export PROJECT_ID= +export CLUSTER_NAME= +export REGION= + +gcloud container clusters create $CLUSTER_NAME \ + --region=$REGION \ + --workload-pool=${PROJECT_ID}.svc.id.goog +``` + +**Existing cluster:** +```bash +gcloud container clusters update $CLUSTER_NAME \ + --region=$REGION \ + --workload-pool=${PROJECT_ID}.svc.id.goog +``` + +### Create Google Service Account + +Create a Google Cloud service account for Crossplane: + +```bash +export GSA_NAME=crossplane-gar-sa + +gcloud iam service-accounts create $GSA_NAME \ + --display-name="Crossplane Artifact Registry Service Account" \ + --project=$PROJECT_ID +``` + +Get the full service account email: + +```bash +export GSA_EMAIL=${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com +echo $GSA_EMAIL +``` + +### Grant Artifact Registry Permissions + +Grant the service account permissions to read from Artifact Registry: + +```bash +gcloud projects add-iam-policy-binding $PROJECT_ID \ + --member="serviceAccount:${GSA_EMAIL}" \ + --role="roles/artifactregistry.reader" +``` + +For specific repository access, use: + +```bash +export REPOSITORY= +export REPOSITORY_LOCATION= + +gcloud artifacts repositories add-iam-policy-binding $REPOSITORY \ + --location=$REPOSITORY_LOCATION \ + --member="serviceAccount:${GSA_EMAIL}" \ + --role="roles/artifactregistry.reader" +``` + +### Create IAM Policy Binding + +Create an IAM policy binding between the Google service account and the Kubernetes service account: + +```bash +gcloud iam service-accounts add-iam-policy-binding $GSA_EMAIL \ + --role roles/iam.workloadIdentityUser \ + --member "serviceAccount:${PROJECT_ID}.svc.id.goog[crossplane-system/crossplane]" +``` + +### Install Crossplane with Workload Identity Annotation + +Install Crossplane with the service account annotation: + +```bash +helm upgrade --install crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --create-namespace \ + --set "serviceAccount.customAnnotations.iam\.gke\.io/gcp-service-account=$GSA_EMAIL" +``` + +### Verify Configuration + +Check that the service account has the correct annotation: + +```bash +kubectl get sa crossplane -n crossplane-system -o yaml +``` + +Expected output should include: + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + iam.gke.io/gcp-service-account: crossplane-gar-sa@project-id.iam.gserviceaccount.com + name: crossplane + namespace: crossplane-system +``` + +### Test Package Installation from Artifact Registry + +Once configured, you can install Crossplane packages (Providers, Functions, Configurations) from your Artifact Registry. Here's an example using a Provider: + +```bash +kubectl apply -f - <}} + +{{< /tabs >}} + +## Configure After Installation + +If Crossplane is already installed, you can update the service account annotations using Helm upgrade with the appropriate `--set` flags shown in your cloud provider's tab above. After updating, restart the Crossplane deployment: + +```bash +kubectl rollout restart deployment/crossplane -n crossplane-system +``` From 58433ff8b738ea8e600350b859827081527a1358 Mon Sep 17 00:00:00 2001 From: Christopher Haar Date: Thu, 29 Jan 2026 11:36:15 +0100 Subject: [PATCH 2/3] lint(vale): set correct spellings and add the guide to all active versions of docs Signed-off-by: Christopher Haar --- .../crossplane-with-workload-identity.md | 825 ++++++++++++++++++ .../crossplane-with-workload-identity.md | 825 ++++++++++++++++++ .../crossplane-with-workload-identity.md | 825 ++++++++++++++++++ .../crossplane-with-workload-identity.md | 292 ++++--- .../vale/styles/Crossplane/provider-words.txt | 8 + 5 files changed, 2648 insertions(+), 127 deletions(-) create mode 100644 content/master/guides/crossplane-with-workload-identity.md create mode 100644 content/v1.20/guides/crossplane-with-workload-identity.md create mode 100644 content/v2.0/guides/crossplane-with-workload-identity.md diff --git a/content/master/guides/crossplane-with-workload-identity.md b/content/master/guides/crossplane-with-workload-identity.md new file mode 100644 index 000000000..9d9d97292 --- /dev/null +++ b/content/master/guides/crossplane-with-workload-identity.md @@ -0,0 +1,825 @@ +--- +title: Crossplane with Workload Identity +weight: 205 +description: Configure Crossplane to pull packages from cloud provider container registries using workload identity +--- + +When running Crossplane on managed Kubernetes clusters (EKS, AKS, GKE), you can use Kubernetes Workload Identity to grant Crossplane access to pull packages from private cloud container registries. This allows Crossplane to install providers, functions, and configurations from registries like AWS ECR, Azure ACR, and Google Artifact Registry without managing static credentials. + +{{% hint "important" %}} +This guide configures the **Crossplane package manager** to pull packages from private registries. Packages reference container images that run as separate pods (providers and functions). + +**Two-step image pull process:** +1. **Crossplane package manager** pulls the package, extracts the package contents (CRDs, XRDs) and creates deployments +2. **Kubernetes nodes** pull the runtime container images when creating provider/function pods + +This guide covers step 1. For step 2, ensure your Kubernetes nodes have permissions to pull images from the private registry. Typically configured at the cluster level: +- **AWS EKS**: Node IAM role with ECR pull permissions +- **Azure AKS**: Kubelet managed identity with `AcrPull` role +- **GCP GKE**: Node service account with Artifact Registry reader role + +Without node-level access, package installation succeeds but pods fail with `ImagePullBackOff`. +{{% /hint %}} + +## Introduction + +To enable Crossplane package manager access to private registries, configure service account annotations during installation. The `crossplane` service account in the `crossplane-system` namespace requires specific annotations for each cloud provider: + +- **AWS EKS**: IAM Roles for Service Accounts (IRSA) +- **Azure AKS**: Azure Workload Identity +- **Google Cloud GKE**: GKE Workload Identity + +## Cloud provider setup + +Select your cloud provider below for detailed setup instructions: + +{{% tabs %}} + +{{% tab "AWS EKS" %}} + +### Configure workload identity on AWS + +Configure Crossplane to pull packages from Amazon ECR using IAM Roles for Service Accounts (IRSA). + +##### Prerequisites + +- An Amazon EKS cluster with OIDC provider enabled +- AWS CLI installed and configured +- `kubectl` configured to access your EKS cluster +- Permissions to create IAM roles and policies + + +#### Enable the OIDC provider + + +If your EKS cluster doesn't have an OIDC provider, enable it: + +```shell +eksctl utils associate-iam-oidc-provider \ + --cluster= \ + --approve +``` + +Verify the OIDC provider: + +```shell +aws eks describe-cluster \ + --name \ + --query "cluster.identity.oidc.issuer" \ + --output text +``` + + +#### Create an IAM policy for ECR access + + +Create an IAM policy that grants permissions to pull images from ECR: + +```shell +cat > crossplane-ecr-policy.json <::repository/*" + } + ] +} +EOF + +aws iam create-policy \ + --policy-name CrossplaneECRPolicy \ + --policy-document file://crossplane-ecr-policy.json +``` + +{{% hint "note" %}} +Replace `` and `` with your AWS region and account ID. You can restrict the `Resource` to specific repositories if needed. +{{% /hint %}} + + +#### Create an IAM role with trust policy + + +Create an IAM role that the Crossplane service account can assume: + +```shell +export CLUSTER_NAME= +export AWS_REGION= +export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) +export OIDC_PROVIDER=$(aws eks describe-cluster --name $CLUSTER_NAME --region $AWS_REGION --query "cluster.identity.oidc.issuer" --output text | sed -e "s/^https:\/\///") + +cat > trust-policy.json < +#### Install Crossplane with the IRSA annotation + + +Install Crossplane with the service account annotation: + +```shell +helm upgrade --install crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --create-namespace \ + --set "serviceAccount.customAnnotations.eks\.amazonaws\.com/role-arn=arn:aws:iam::${AWS_ACCOUNT_ID}:role/CrossplaneECRRole" +``` + +##### Verify the configuration + +Check that the service account has the correct annotation: + +```shell +kubectl get sa crossplane -n crossplane-system -o yaml +``` + +Expected output should include: + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/CrossplaneECRRole + name: crossplane + namespace: crossplane-system +``` + + +#### Test package installation from ECR + + +Once configured, you can install Crossplane packages (Providers, Functions, Configurations) from your ECR registry. Here's an example using a Provider: + +```shell +kubectl apply -f - < +#### Failed to get authorization token + + +**Error:** +``` +failed to get authorization token: AccessDeniedException +``` + +**Solution:** +1. Verify the IAM role has the `ecr:GetAuthorizationToken` permission +2. Check the trust policy allows the service account to assume the role +3. Confirm the service account annotation matches the IAM role ARN + + +#### Invalid identity token + + +**Error:** +``` +An error occurred (InvalidIdentityToken) when calling the AssumeRoleWithWebIdentity operation +``` + +**Solution:** +1. Confirm the OIDC provider is active on your EKS cluster +2. Check the trust policy condition matches your cluster's OIDC provider +3. Verify the service account namespace and name in the condition are correct + + +#### Access denied to ECR repository + + +**Error:** +``` +denied: User: arn:aws:sts::123456789012:assumed-role/CrossplaneECRRole is not authorized to perform: ecr:BatchGetImage +``` + +**Solution:** +1. Verify the IAM policy includes `ecr:BatchGetImage`, `ecr:BatchCheckLayerAvailability`, and `ecr:GetDownloadUrlForLayer` +2. Check the policy's `Resource` includes your ECR repository ARN +3. Confirm the IAM role has the policy attached + +#### Service account annotation not applied + +If the service account doesn't have the annotation after installation: + +```shell +# Update via Helm +helm upgrade crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --reuse-values \ + --set "serviceAccount.customAnnotations.eks\.amazonaws\.com/role-arn=arn:aws:iam::${AWS_ACCOUNT_ID}:role/CrossplaneECRRole" + +# Restart Crossplane +kubectl rollout restart deployment/crossplane -n crossplane-system +``` + +##### Check Crossplane logs + +View logs for authentication issues: + +```shell +kubectl logs -n crossplane-system deployment/crossplane --all-containers -f +``` + +Look for: +- AWS credential errors +- ECR authentication failures +- Image pull errors with specific repository paths + +##### Learn more + +- [Amazon EKS IRSA Documentation](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) +- [Amazon ECR Authentication](https://docs.aws.amazon.com/AmazonECR/latest/userguide/security-iam.html) + +{{% /tab %}} + +{{% tab "Azure AKS" %}} + +## Configure workload identity on Azure + +Configure Crossplane to pull packages from Azure Container Registry (ACR) using Azure Workload Identity. + +#### Prerequisites + +- An AKS cluster with Workload Identity enabled +- Azure CLI installed and configured +- `kubectl` configured to access your AKS cluster +- Permissions to create Azure managed identities and role assignments + + +### Enable workload identity on AKS + + +If your AKS cluster doesn't have Workload Identity enabled, update it: + +```shell +export RESOURCE_GROUP= +export CLUSTER_NAME= + +az aks update \ + --resource-group $RESOURCE_GROUP \ + --name $CLUSTER_NAME \ + --enable-oidc-issuer \ + --enable-workload-identity +``` + +Get the OIDC issuer URL: + +```shell +export AKS_OIDC_ISSUER=$(az aks show \ + --resource-group $RESOURCE_GROUP \ + --name $CLUSTER_NAME \ + --query "oidcIssuerProfile.issuerUrl" \ + --output tsv) + +echo $AKS_OIDC_ISSUER +``` + +#### Create an Azure managed identity + +Create a managed identity for Crossplane: + +```shell +export IDENTITY_NAME=crossplane-acr-identity + +az identity create \ + --name $IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP + +export USER_ASSIGNED_CLIENT_ID=$(az identity show \ + --name $IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP \ + --query 'clientId' \ + --output tsv) + +export USER_ASSIGNED_OBJECT_ID=$(az identity show \ + --name $IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP \ + --query 'principalId' \ + --output tsv) + +echo "Client ID: $USER_ASSIGNED_CLIENT_ID" +echo "Object ID: $USER_ASSIGNED_OBJECT_ID" +``` + + +### Assign the ACR pull role + + +Grant the managed identity permission to pull from ACR: + +```shell +export ACR_NAME= + +export ACR_ID=$(az acr show \ + --name $ACR_NAME \ + --query 'id' \ + --output tsv) + +az role assignment create \ + --assignee-object-id $USER_ASSIGNED_OBJECT_ID \ + --assignee-principal-type ServicePrincipal \ + --role AcrPull \ + --scope $ACR_ID +``` + +#### Create a federated identity credential + +Create a federated identity credential that establishes trust between the managed identity and the Kubernetes service account: + +```shell +az identity federated-credential create \ + --name crossplane-federated-credential \ + --identity-name $IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP \ + --issuer $AKS_OIDC_ISSUER \ + --subject system:serviceaccount:crossplane-system:crossplane \ + --audience api://AzureADTokenExchange +``` + +#### Install Crossplane with workload identity configuration + +Get the tenant ID: + +```shell +export AZURE_TENANT_ID=$(az account show --query tenantId --output tsv) +``` + +Install Crossplane with the workload identity annotations and label: + +```shell +helm upgrade --install crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --create-namespace \ + --set "serviceAccount.customAnnotations.azure\.workload\.identity/client-id=$USER_ASSIGNED_CLIENT_ID" \ + --set "serviceAccount.customAnnotations.azure\.workload\.identity/tenant-id=$AZURE_TENANT_ID" \ + --set-string 'customLabels.azure\.workload\.identity/use=true' +``` + +{{% hint "note" %}} +Azure Workload Identity requires: +- Service account annotations for the client ID and tenant ID +- Label `azure.workload.identity/use: "true"` on pods (applied via `customLabels`) + +The `customLabels` setting applies the label to all Crossplane resources. The Azure Workload Identity webhook uses this label on pods to inject environment variables and token volumes. Use `--set-string` to treat the value as a string rather than a boolean. +{{% /hint %}} + +#### Verify the configuration + +Check that the service account has the correct annotations: + +```shell +kubectl get sa crossplane -n crossplane-system -o yaml +``` + +Expected output should include: + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + azure.workload.identity/client-id: + azure.workload.identity/tenant-id: + name: crossplane + namespace: crossplane-system +``` + +Check that the deployment has the required labels: + +```shell +kubectl get deployment crossplane -n crossplane-system -o yaml +``` + +Expected output should include: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + azure.workload.identity/use: "true" + name: crossplane + namespace: crossplane-system +spec: + template: + metadata: + labels: + azure.workload.identity/use: "true" +``` + + +### Test package installation from ACR + + +Once configured, you can install Crossplane packages (Providers, Functions, Configurations) from your ACR. Here's an example using a Provider: + +```shell +kubectl apply -f - < +#### Failed to resolve reference + + +**Error:** +``` +failed to resolve reference: failed to fetch oauth token +``` + +**Solution:** +1. Confirm workload identity is active on the AKS cluster +2. Check the OIDC issuer URL in the federated credential matches your cluster's OIDC issuer +3. Verify the subject in the federated credential matches `system:serviceaccount:crossplane-system:crossplane` + + +#### Invalid federated token + + +**Error:** +``` +invalid federated token +``` + +**Solution:** +1. Verify the federated credential audience uses `api://AzureADTokenExchange` +2. Check that the OIDC issuer URL is correct +3. Confirm the service account namespace and name match the federated credential subject + +#### Service account annotation not applied + +If the service account doesn't have the annotations after installation: + +```shell +# Update via Helm +helm upgrade crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --reuse-values \ + --set "serviceAccount.customAnnotations.azure\.workload\.identity/client-id=$USER_ASSIGNED_CLIENT_ID" \ + --set "serviceAccount.customAnnotations.azure\.workload\.identity/tenant-id=$AZURE_TENANT_ID" + +# Restart Crossplane +kubectl rollout restart deployment/crossplane -n crossplane-system +``` + +#### Check Crossplane logs + +View logs for authentication issues: + +```shell +kubectl logs -n crossplane-system deployment/crossplane --all-containers -f +``` + +Look for: +- Azure authentication errors +- ACR authentication failures +- Image pull errors with specific repository paths + +#### Learn more + +- [Azure Workload Identity Documentation](https://azure.github.io/azure-workload-identity/) +- [AKS Workload Identity Overview](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview) +- [Azure Container Registry Authentication](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-authentication) + +{{% /tab %}} + +{{% tab "Google Cloud GKE" %}} + +## Configure workload identity on GCP + +Configure Crossplane to pull packages from Google Artifact Registry using GKE Workload Identity. + +#### Prerequisites + +- A GKE cluster with Workload Identity enabled +- `gcloud` CLI installed and configured +- `kubectl` configured to access your GKE cluster +- Permissions to create service accounts and IAM bindings + + +### Enable workload identity on GKE + + +If your GKE cluster doesn't have Workload Identity enabled, create a new cluster with it enabled or update an existing cluster: + +**New cluster:** +```shell +export PROJECT_ID= +export CLUSTER_NAME= +export REGION= + +gcloud container clusters create $CLUSTER_NAME \ + --region=$REGION \ + --workload-pool=${PROJECT_ID}.svc.id.goog +``` + +**Existing cluster:** +```shell +gcloud container clusters update $CLUSTER_NAME \ + --region=$REGION \ + --workload-pool=${PROJECT_ID}.svc.id.goog +``` + +#### Create a Google service account + +Create a Google Cloud service account for Crossplane: + +```shell +export GSA_NAME=crossplane-gar-sa + +gcloud iam service-accounts create $GSA_NAME \ + --display-name="Crossplane Artifact Registry Service Account" \ + --project=$PROJECT_ID +``` + +Get the full service account email: + +```shell +export GSA_EMAIL=${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com +echo $GSA_EMAIL +``` + +#### Grant artifact registry permissions + +Grant the service account permissions to read from Artifact Registry: + +```shell +gcloud projects add-iam-policy-binding $PROJECT_ID \ + --member="serviceAccount:${GSA_EMAIL}" \ + --role="roles/artifactregistry.reader" +``` + +For specific repository access, use: + +```shell +export REPOSITORY= +export REPOSITORY_LOCATION= + +gcloud artifacts repositories add-iam-policy-binding $REPOSITORY \ + --location=$REPOSITORY_LOCATION \ + --member="serviceAccount:${GSA_EMAIL}" \ + --role="roles/artifactregistry.reader" +``` + + +### Create an IAM policy binding + + +Create an IAM policy binding between the Google service account and the Kubernetes service account: + +```shell +gcloud iam service-accounts add-iam-policy-binding $GSA_EMAIL \ + --role roles/iam.workloadIdentityUser \ + --member "serviceAccount:${PROJECT_ID}.svc.id.goog[crossplane-system/crossplane]" +``` + +#### Install Crossplane with the workload identity annotation + +Install Crossplane with the service account annotation: + +```shell +helm upgrade --install crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --create-namespace \ + --set "serviceAccount.customAnnotations.iam\.gke\.io/gcp-service-account=$GSA_EMAIL" +``` + +#### Verify the configuration + +Check that the service account has the correct annotation: + +```shell +kubectl get sa crossplane -n crossplane-system -o yaml +``` + +Expected output should include: + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + iam.gke.io/gcp-service-account: crossplane-gar-sa@project-id.iam.gserviceaccount.com + name: crossplane + namespace: crossplane-system +``` + +### Test package installation from artifact registry + +Once configured, you can install Crossplane packages (Providers, Functions, Configurations) from your Artifact Registry. Here's an example using a Provider: + +```shell +kubectl apply -f - < + +#### Failed to fetch OAuth token + + + +**Error:** +``` +failed to fetch oauth token +``` + +**Solution:** +1. Confirm workload identity is active on the GKE cluster +2. Check the IAM policy binding allows the Kubernetes service account to impersonate the Google service account +3. Verify the workload pool matches your project: `${PROJECT_ID}.svc.id.goog` + + +#### Invalid identity token + + +**Error:** +``` +invalid identity token +``` + +**Solution:** +1. Verify the IAM policy binding member format: `serviceAccount:${PROJECT_ID}.svc.id.goog[crossplane-system/crossplane]` +2. Check that workload identity is active on the node pool +3. Confirm the service account annotation is correct + +#### Service account annotation not applied + +If the service account doesn't have the annotation after installation: + +```shell +# Update via Helm +helm upgrade crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --reuse-values \ + --set "serviceAccount.customAnnotations.iam\.gke\.io/gcp-service-account=$GSA_EMAIL" + +# Restart Crossplane +kubectl rollout restart deployment/crossplane -n crossplane-system +``` + +#### Verify workload identity configuration + +Test the workload identity configuration: + +```shell +# Check if workload identity is enabled on the cluster +gcloud container clusters describe $CLUSTER_NAME \ + --region=$REGION \ + --format="value(workloadIdentityConfig.workloadPool)" + +# Verify IAM policy binding +gcloud iam service-accounts get-iam-policy $GSA_EMAIL +``` + +#### Check Crossplane logs + +View logs for authentication issues: + +```shell +kubectl logs -n crossplane-system deployment/crossplane --all-containers -f +``` + +Look for: +- Google Cloud authentication errors +- Artifact Registry authentication failures +- Image pull errors with specific repository paths + +#### Learn more + +- [GKE Workload Identity Documentation](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) +- [Artifact Registry Authentication](https://cloud.google.com/artifact-registry/docs/docker/authentication) +- [IAM Service Account Permissions](https://cloud.google.com/iam/docs/service-accounts) + +{{% /tab %}} + +{{% /tabs %}} + +## Configure after installation + +If Crossplane is already installed, you can update the service account annotations using Helm upgrade with the appropriate `--set` flags shown in your cloud provider's tab. After updating, restart the Crossplane deployment: + +```shell +kubectl rollout restart deployment/crossplane -n crossplane-system +``` diff --git a/content/v1.20/guides/crossplane-with-workload-identity.md b/content/v1.20/guides/crossplane-with-workload-identity.md new file mode 100644 index 000000000..9d9d97292 --- /dev/null +++ b/content/v1.20/guides/crossplane-with-workload-identity.md @@ -0,0 +1,825 @@ +--- +title: Crossplane with Workload Identity +weight: 205 +description: Configure Crossplane to pull packages from cloud provider container registries using workload identity +--- + +When running Crossplane on managed Kubernetes clusters (EKS, AKS, GKE), you can use Kubernetes Workload Identity to grant Crossplane access to pull packages from private cloud container registries. This allows Crossplane to install providers, functions, and configurations from registries like AWS ECR, Azure ACR, and Google Artifact Registry without managing static credentials. + +{{% hint "important" %}} +This guide configures the **Crossplane package manager** to pull packages from private registries. Packages reference container images that run as separate pods (providers and functions). + +**Two-step image pull process:** +1. **Crossplane package manager** pulls the package, extracts the package contents (CRDs, XRDs) and creates deployments +2. **Kubernetes nodes** pull the runtime container images when creating provider/function pods + +This guide covers step 1. For step 2, ensure your Kubernetes nodes have permissions to pull images from the private registry. Typically configured at the cluster level: +- **AWS EKS**: Node IAM role with ECR pull permissions +- **Azure AKS**: Kubelet managed identity with `AcrPull` role +- **GCP GKE**: Node service account with Artifact Registry reader role + +Without node-level access, package installation succeeds but pods fail with `ImagePullBackOff`. +{{% /hint %}} + +## Introduction + +To enable Crossplane package manager access to private registries, configure service account annotations during installation. The `crossplane` service account in the `crossplane-system` namespace requires specific annotations for each cloud provider: + +- **AWS EKS**: IAM Roles for Service Accounts (IRSA) +- **Azure AKS**: Azure Workload Identity +- **Google Cloud GKE**: GKE Workload Identity + +## Cloud provider setup + +Select your cloud provider below for detailed setup instructions: + +{{% tabs %}} + +{{% tab "AWS EKS" %}} + +### Configure workload identity on AWS + +Configure Crossplane to pull packages from Amazon ECR using IAM Roles for Service Accounts (IRSA). + +##### Prerequisites + +- An Amazon EKS cluster with OIDC provider enabled +- AWS CLI installed and configured +- `kubectl` configured to access your EKS cluster +- Permissions to create IAM roles and policies + + +#### Enable the OIDC provider + + +If your EKS cluster doesn't have an OIDC provider, enable it: + +```shell +eksctl utils associate-iam-oidc-provider \ + --cluster= \ + --approve +``` + +Verify the OIDC provider: + +```shell +aws eks describe-cluster \ + --name \ + --query "cluster.identity.oidc.issuer" \ + --output text +``` + + +#### Create an IAM policy for ECR access + + +Create an IAM policy that grants permissions to pull images from ECR: + +```shell +cat > crossplane-ecr-policy.json <::repository/*" + } + ] +} +EOF + +aws iam create-policy \ + --policy-name CrossplaneECRPolicy \ + --policy-document file://crossplane-ecr-policy.json +``` + +{{% hint "note" %}} +Replace `` and `` with your AWS region and account ID. You can restrict the `Resource` to specific repositories if needed. +{{% /hint %}} + + +#### Create an IAM role with trust policy + + +Create an IAM role that the Crossplane service account can assume: + +```shell +export CLUSTER_NAME= +export AWS_REGION= +export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) +export OIDC_PROVIDER=$(aws eks describe-cluster --name $CLUSTER_NAME --region $AWS_REGION --query "cluster.identity.oidc.issuer" --output text | sed -e "s/^https:\/\///") + +cat > trust-policy.json < +#### Install Crossplane with the IRSA annotation + + +Install Crossplane with the service account annotation: + +```shell +helm upgrade --install crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --create-namespace \ + --set "serviceAccount.customAnnotations.eks\.amazonaws\.com/role-arn=arn:aws:iam::${AWS_ACCOUNT_ID}:role/CrossplaneECRRole" +``` + +##### Verify the configuration + +Check that the service account has the correct annotation: + +```shell +kubectl get sa crossplane -n crossplane-system -o yaml +``` + +Expected output should include: + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/CrossplaneECRRole + name: crossplane + namespace: crossplane-system +``` + + +#### Test package installation from ECR + + +Once configured, you can install Crossplane packages (Providers, Functions, Configurations) from your ECR registry. Here's an example using a Provider: + +```shell +kubectl apply -f - < +#### Failed to get authorization token + + +**Error:** +``` +failed to get authorization token: AccessDeniedException +``` + +**Solution:** +1. Verify the IAM role has the `ecr:GetAuthorizationToken` permission +2. Check the trust policy allows the service account to assume the role +3. Confirm the service account annotation matches the IAM role ARN + + +#### Invalid identity token + + +**Error:** +``` +An error occurred (InvalidIdentityToken) when calling the AssumeRoleWithWebIdentity operation +``` + +**Solution:** +1. Confirm the OIDC provider is active on your EKS cluster +2. Check the trust policy condition matches your cluster's OIDC provider +3. Verify the service account namespace and name in the condition are correct + + +#### Access denied to ECR repository + + +**Error:** +``` +denied: User: arn:aws:sts::123456789012:assumed-role/CrossplaneECRRole is not authorized to perform: ecr:BatchGetImage +``` + +**Solution:** +1. Verify the IAM policy includes `ecr:BatchGetImage`, `ecr:BatchCheckLayerAvailability`, and `ecr:GetDownloadUrlForLayer` +2. Check the policy's `Resource` includes your ECR repository ARN +3. Confirm the IAM role has the policy attached + +#### Service account annotation not applied + +If the service account doesn't have the annotation after installation: + +```shell +# Update via Helm +helm upgrade crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --reuse-values \ + --set "serviceAccount.customAnnotations.eks\.amazonaws\.com/role-arn=arn:aws:iam::${AWS_ACCOUNT_ID}:role/CrossplaneECRRole" + +# Restart Crossplane +kubectl rollout restart deployment/crossplane -n crossplane-system +``` + +##### Check Crossplane logs + +View logs for authentication issues: + +```shell +kubectl logs -n crossplane-system deployment/crossplane --all-containers -f +``` + +Look for: +- AWS credential errors +- ECR authentication failures +- Image pull errors with specific repository paths + +##### Learn more + +- [Amazon EKS IRSA Documentation](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) +- [Amazon ECR Authentication](https://docs.aws.amazon.com/AmazonECR/latest/userguide/security-iam.html) + +{{% /tab %}} + +{{% tab "Azure AKS" %}} + +## Configure workload identity on Azure + +Configure Crossplane to pull packages from Azure Container Registry (ACR) using Azure Workload Identity. + +#### Prerequisites + +- An AKS cluster with Workload Identity enabled +- Azure CLI installed and configured +- `kubectl` configured to access your AKS cluster +- Permissions to create Azure managed identities and role assignments + + +### Enable workload identity on AKS + + +If your AKS cluster doesn't have Workload Identity enabled, update it: + +```shell +export RESOURCE_GROUP= +export CLUSTER_NAME= + +az aks update \ + --resource-group $RESOURCE_GROUP \ + --name $CLUSTER_NAME \ + --enable-oidc-issuer \ + --enable-workload-identity +``` + +Get the OIDC issuer URL: + +```shell +export AKS_OIDC_ISSUER=$(az aks show \ + --resource-group $RESOURCE_GROUP \ + --name $CLUSTER_NAME \ + --query "oidcIssuerProfile.issuerUrl" \ + --output tsv) + +echo $AKS_OIDC_ISSUER +``` + +#### Create an Azure managed identity + +Create a managed identity for Crossplane: + +```shell +export IDENTITY_NAME=crossplane-acr-identity + +az identity create \ + --name $IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP + +export USER_ASSIGNED_CLIENT_ID=$(az identity show \ + --name $IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP \ + --query 'clientId' \ + --output tsv) + +export USER_ASSIGNED_OBJECT_ID=$(az identity show \ + --name $IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP \ + --query 'principalId' \ + --output tsv) + +echo "Client ID: $USER_ASSIGNED_CLIENT_ID" +echo "Object ID: $USER_ASSIGNED_OBJECT_ID" +``` + + +### Assign the ACR pull role + + +Grant the managed identity permission to pull from ACR: + +```shell +export ACR_NAME= + +export ACR_ID=$(az acr show \ + --name $ACR_NAME \ + --query 'id' \ + --output tsv) + +az role assignment create \ + --assignee-object-id $USER_ASSIGNED_OBJECT_ID \ + --assignee-principal-type ServicePrincipal \ + --role AcrPull \ + --scope $ACR_ID +``` + +#### Create a federated identity credential + +Create a federated identity credential that establishes trust between the managed identity and the Kubernetes service account: + +```shell +az identity federated-credential create \ + --name crossplane-federated-credential \ + --identity-name $IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP \ + --issuer $AKS_OIDC_ISSUER \ + --subject system:serviceaccount:crossplane-system:crossplane \ + --audience api://AzureADTokenExchange +``` + +#### Install Crossplane with workload identity configuration + +Get the tenant ID: + +```shell +export AZURE_TENANT_ID=$(az account show --query tenantId --output tsv) +``` + +Install Crossplane with the workload identity annotations and label: + +```shell +helm upgrade --install crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --create-namespace \ + --set "serviceAccount.customAnnotations.azure\.workload\.identity/client-id=$USER_ASSIGNED_CLIENT_ID" \ + --set "serviceAccount.customAnnotations.azure\.workload\.identity/tenant-id=$AZURE_TENANT_ID" \ + --set-string 'customLabels.azure\.workload\.identity/use=true' +``` + +{{% hint "note" %}} +Azure Workload Identity requires: +- Service account annotations for the client ID and tenant ID +- Label `azure.workload.identity/use: "true"` on pods (applied via `customLabels`) + +The `customLabels` setting applies the label to all Crossplane resources. The Azure Workload Identity webhook uses this label on pods to inject environment variables and token volumes. Use `--set-string` to treat the value as a string rather than a boolean. +{{% /hint %}} + +#### Verify the configuration + +Check that the service account has the correct annotations: + +```shell +kubectl get sa crossplane -n crossplane-system -o yaml +``` + +Expected output should include: + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + azure.workload.identity/client-id: + azure.workload.identity/tenant-id: + name: crossplane + namespace: crossplane-system +``` + +Check that the deployment has the required labels: + +```shell +kubectl get deployment crossplane -n crossplane-system -o yaml +``` + +Expected output should include: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + azure.workload.identity/use: "true" + name: crossplane + namespace: crossplane-system +spec: + template: + metadata: + labels: + azure.workload.identity/use: "true" +``` + + +### Test package installation from ACR + + +Once configured, you can install Crossplane packages (Providers, Functions, Configurations) from your ACR. Here's an example using a Provider: + +```shell +kubectl apply -f - < +#### Failed to resolve reference + + +**Error:** +``` +failed to resolve reference: failed to fetch oauth token +``` + +**Solution:** +1. Confirm workload identity is active on the AKS cluster +2. Check the OIDC issuer URL in the federated credential matches your cluster's OIDC issuer +3. Verify the subject in the federated credential matches `system:serviceaccount:crossplane-system:crossplane` + + +#### Invalid federated token + + +**Error:** +``` +invalid federated token +``` + +**Solution:** +1. Verify the federated credential audience uses `api://AzureADTokenExchange` +2. Check that the OIDC issuer URL is correct +3. Confirm the service account namespace and name match the federated credential subject + +#### Service account annotation not applied + +If the service account doesn't have the annotations after installation: + +```shell +# Update via Helm +helm upgrade crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --reuse-values \ + --set "serviceAccount.customAnnotations.azure\.workload\.identity/client-id=$USER_ASSIGNED_CLIENT_ID" \ + --set "serviceAccount.customAnnotations.azure\.workload\.identity/tenant-id=$AZURE_TENANT_ID" + +# Restart Crossplane +kubectl rollout restart deployment/crossplane -n crossplane-system +``` + +#### Check Crossplane logs + +View logs for authentication issues: + +```shell +kubectl logs -n crossplane-system deployment/crossplane --all-containers -f +``` + +Look for: +- Azure authentication errors +- ACR authentication failures +- Image pull errors with specific repository paths + +#### Learn more + +- [Azure Workload Identity Documentation](https://azure.github.io/azure-workload-identity/) +- [AKS Workload Identity Overview](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview) +- [Azure Container Registry Authentication](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-authentication) + +{{% /tab %}} + +{{% tab "Google Cloud GKE" %}} + +## Configure workload identity on GCP + +Configure Crossplane to pull packages from Google Artifact Registry using GKE Workload Identity. + +#### Prerequisites + +- A GKE cluster with Workload Identity enabled +- `gcloud` CLI installed and configured +- `kubectl` configured to access your GKE cluster +- Permissions to create service accounts and IAM bindings + + +### Enable workload identity on GKE + + +If your GKE cluster doesn't have Workload Identity enabled, create a new cluster with it enabled or update an existing cluster: + +**New cluster:** +```shell +export PROJECT_ID= +export CLUSTER_NAME= +export REGION= + +gcloud container clusters create $CLUSTER_NAME \ + --region=$REGION \ + --workload-pool=${PROJECT_ID}.svc.id.goog +``` + +**Existing cluster:** +```shell +gcloud container clusters update $CLUSTER_NAME \ + --region=$REGION \ + --workload-pool=${PROJECT_ID}.svc.id.goog +``` + +#### Create a Google service account + +Create a Google Cloud service account for Crossplane: + +```shell +export GSA_NAME=crossplane-gar-sa + +gcloud iam service-accounts create $GSA_NAME \ + --display-name="Crossplane Artifact Registry Service Account" \ + --project=$PROJECT_ID +``` + +Get the full service account email: + +```shell +export GSA_EMAIL=${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com +echo $GSA_EMAIL +``` + +#### Grant artifact registry permissions + +Grant the service account permissions to read from Artifact Registry: + +```shell +gcloud projects add-iam-policy-binding $PROJECT_ID \ + --member="serviceAccount:${GSA_EMAIL}" \ + --role="roles/artifactregistry.reader" +``` + +For specific repository access, use: + +```shell +export REPOSITORY= +export REPOSITORY_LOCATION= + +gcloud artifacts repositories add-iam-policy-binding $REPOSITORY \ + --location=$REPOSITORY_LOCATION \ + --member="serviceAccount:${GSA_EMAIL}" \ + --role="roles/artifactregistry.reader" +``` + + +### Create an IAM policy binding + + +Create an IAM policy binding between the Google service account and the Kubernetes service account: + +```shell +gcloud iam service-accounts add-iam-policy-binding $GSA_EMAIL \ + --role roles/iam.workloadIdentityUser \ + --member "serviceAccount:${PROJECT_ID}.svc.id.goog[crossplane-system/crossplane]" +``` + +#### Install Crossplane with the workload identity annotation + +Install Crossplane with the service account annotation: + +```shell +helm upgrade --install crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --create-namespace \ + --set "serviceAccount.customAnnotations.iam\.gke\.io/gcp-service-account=$GSA_EMAIL" +``` + +#### Verify the configuration + +Check that the service account has the correct annotation: + +```shell +kubectl get sa crossplane -n crossplane-system -o yaml +``` + +Expected output should include: + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + iam.gke.io/gcp-service-account: crossplane-gar-sa@project-id.iam.gserviceaccount.com + name: crossplane + namespace: crossplane-system +``` + +### Test package installation from artifact registry + +Once configured, you can install Crossplane packages (Providers, Functions, Configurations) from your Artifact Registry. Here's an example using a Provider: + +```shell +kubectl apply -f - < + +#### Failed to fetch OAuth token + + + +**Error:** +``` +failed to fetch oauth token +``` + +**Solution:** +1. Confirm workload identity is active on the GKE cluster +2. Check the IAM policy binding allows the Kubernetes service account to impersonate the Google service account +3. Verify the workload pool matches your project: `${PROJECT_ID}.svc.id.goog` + + +#### Invalid identity token + + +**Error:** +``` +invalid identity token +``` + +**Solution:** +1. Verify the IAM policy binding member format: `serviceAccount:${PROJECT_ID}.svc.id.goog[crossplane-system/crossplane]` +2. Check that workload identity is active on the node pool +3. Confirm the service account annotation is correct + +#### Service account annotation not applied + +If the service account doesn't have the annotation after installation: + +```shell +# Update via Helm +helm upgrade crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --reuse-values \ + --set "serviceAccount.customAnnotations.iam\.gke\.io/gcp-service-account=$GSA_EMAIL" + +# Restart Crossplane +kubectl rollout restart deployment/crossplane -n crossplane-system +``` + +#### Verify workload identity configuration + +Test the workload identity configuration: + +```shell +# Check if workload identity is enabled on the cluster +gcloud container clusters describe $CLUSTER_NAME \ + --region=$REGION \ + --format="value(workloadIdentityConfig.workloadPool)" + +# Verify IAM policy binding +gcloud iam service-accounts get-iam-policy $GSA_EMAIL +``` + +#### Check Crossplane logs + +View logs for authentication issues: + +```shell +kubectl logs -n crossplane-system deployment/crossplane --all-containers -f +``` + +Look for: +- Google Cloud authentication errors +- Artifact Registry authentication failures +- Image pull errors with specific repository paths + +#### Learn more + +- [GKE Workload Identity Documentation](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) +- [Artifact Registry Authentication](https://cloud.google.com/artifact-registry/docs/docker/authentication) +- [IAM Service Account Permissions](https://cloud.google.com/iam/docs/service-accounts) + +{{% /tab %}} + +{{% /tabs %}} + +## Configure after installation + +If Crossplane is already installed, you can update the service account annotations using Helm upgrade with the appropriate `--set` flags shown in your cloud provider's tab. After updating, restart the Crossplane deployment: + +```shell +kubectl rollout restart deployment/crossplane -n crossplane-system +``` diff --git a/content/v2.0/guides/crossplane-with-workload-identity.md b/content/v2.0/guides/crossplane-with-workload-identity.md new file mode 100644 index 000000000..9d9d97292 --- /dev/null +++ b/content/v2.0/guides/crossplane-with-workload-identity.md @@ -0,0 +1,825 @@ +--- +title: Crossplane with Workload Identity +weight: 205 +description: Configure Crossplane to pull packages from cloud provider container registries using workload identity +--- + +When running Crossplane on managed Kubernetes clusters (EKS, AKS, GKE), you can use Kubernetes Workload Identity to grant Crossplane access to pull packages from private cloud container registries. This allows Crossplane to install providers, functions, and configurations from registries like AWS ECR, Azure ACR, and Google Artifact Registry without managing static credentials. + +{{% hint "important" %}} +This guide configures the **Crossplane package manager** to pull packages from private registries. Packages reference container images that run as separate pods (providers and functions). + +**Two-step image pull process:** +1. **Crossplane package manager** pulls the package, extracts the package contents (CRDs, XRDs) and creates deployments +2. **Kubernetes nodes** pull the runtime container images when creating provider/function pods + +This guide covers step 1. For step 2, ensure your Kubernetes nodes have permissions to pull images from the private registry. Typically configured at the cluster level: +- **AWS EKS**: Node IAM role with ECR pull permissions +- **Azure AKS**: Kubelet managed identity with `AcrPull` role +- **GCP GKE**: Node service account with Artifact Registry reader role + +Without node-level access, package installation succeeds but pods fail with `ImagePullBackOff`. +{{% /hint %}} + +## Introduction + +To enable Crossplane package manager access to private registries, configure service account annotations during installation. The `crossplane` service account in the `crossplane-system` namespace requires specific annotations for each cloud provider: + +- **AWS EKS**: IAM Roles for Service Accounts (IRSA) +- **Azure AKS**: Azure Workload Identity +- **Google Cloud GKE**: GKE Workload Identity + +## Cloud provider setup + +Select your cloud provider below for detailed setup instructions: + +{{% tabs %}} + +{{% tab "AWS EKS" %}} + +### Configure workload identity on AWS + +Configure Crossplane to pull packages from Amazon ECR using IAM Roles for Service Accounts (IRSA). + +##### Prerequisites + +- An Amazon EKS cluster with OIDC provider enabled +- AWS CLI installed and configured +- `kubectl` configured to access your EKS cluster +- Permissions to create IAM roles and policies + + +#### Enable the OIDC provider + + +If your EKS cluster doesn't have an OIDC provider, enable it: + +```shell +eksctl utils associate-iam-oidc-provider \ + --cluster= \ + --approve +``` + +Verify the OIDC provider: + +```shell +aws eks describe-cluster \ + --name \ + --query "cluster.identity.oidc.issuer" \ + --output text +``` + + +#### Create an IAM policy for ECR access + + +Create an IAM policy that grants permissions to pull images from ECR: + +```shell +cat > crossplane-ecr-policy.json <::repository/*" + } + ] +} +EOF + +aws iam create-policy \ + --policy-name CrossplaneECRPolicy \ + --policy-document file://crossplane-ecr-policy.json +``` + +{{% hint "note" %}} +Replace `` and `` with your AWS region and account ID. You can restrict the `Resource` to specific repositories if needed. +{{% /hint %}} + + +#### Create an IAM role with trust policy + + +Create an IAM role that the Crossplane service account can assume: + +```shell +export CLUSTER_NAME= +export AWS_REGION= +export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) +export OIDC_PROVIDER=$(aws eks describe-cluster --name $CLUSTER_NAME --region $AWS_REGION --query "cluster.identity.oidc.issuer" --output text | sed -e "s/^https:\/\///") + +cat > trust-policy.json < +#### Install Crossplane with the IRSA annotation + + +Install Crossplane with the service account annotation: + +```shell +helm upgrade --install crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --create-namespace \ + --set "serviceAccount.customAnnotations.eks\.amazonaws\.com/role-arn=arn:aws:iam::${AWS_ACCOUNT_ID}:role/CrossplaneECRRole" +``` + +##### Verify the configuration + +Check that the service account has the correct annotation: + +```shell +kubectl get sa crossplane -n crossplane-system -o yaml +``` + +Expected output should include: + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/CrossplaneECRRole + name: crossplane + namespace: crossplane-system +``` + + +#### Test package installation from ECR + + +Once configured, you can install Crossplane packages (Providers, Functions, Configurations) from your ECR registry. Here's an example using a Provider: + +```shell +kubectl apply -f - < +#### Failed to get authorization token + + +**Error:** +``` +failed to get authorization token: AccessDeniedException +``` + +**Solution:** +1. Verify the IAM role has the `ecr:GetAuthorizationToken` permission +2. Check the trust policy allows the service account to assume the role +3. Confirm the service account annotation matches the IAM role ARN + + +#### Invalid identity token + + +**Error:** +``` +An error occurred (InvalidIdentityToken) when calling the AssumeRoleWithWebIdentity operation +``` + +**Solution:** +1. Confirm the OIDC provider is active on your EKS cluster +2. Check the trust policy condition matches your cluster's OIDC provider +3. Verify the service account namespace and name in the condition are correct + + +#### Access denied to ECR repository + + +**Error:** +``` +denied: User: arn:aws:sts::123456789012:assumed-role/CrossplaneECRRole is not authorized to perform: ecr:BatchGetImage +``` + +**Solution:** +1. Verify the IAM policy includes `ecr:BatchGetImage`, `ecr:BatchCheckLayerAvailability`, and `ecr:GetDownloadUrlForLayer` +2. Check the policy's `Resource` includes your ECR repository ARN +3. Confirm the IAM role has the policy attached + +#### Service account annotation not applied + +If the service account doesn't have the annotation after installation: + +```shell +# Update via Helm +helm upgrade crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --reuse-values \ + --set "serviceAccount.customAnnotations.eks\.amazonaws\.com/role-arn=arn:aws:iam::${AWS_ACCOUNT_ID}:role/CrossplaneECRRole" + +# Restart Crossplane +kubectl rollout restart deployment/crossplane -n crossplane-system +``` + +##### Check Crossplane logs + +View logs for authentication issues: + +```shell +kubectl logs -n crossplane-system deployment/crossplane --all-containers -f +``` + +Look for: +- AWS credential errors +- ECR authentication failures +- Image pull errors with specific repository paths + +##### Learn more + +- [Amazon EKS IRSA Documentation](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) +- [Amazon ECR Authentication](https://docs.aws.amazon.com/AmazonECR/latest/userguide/security-iam.html) + +{{% /tab %}} + +{{% tab "Azure AKS" %}} + +## Configure workload identity on Azure + +Configure Crossplane to pull packages from Azure Container Registry (ACR) using Azure Workload Identity. + +#### Prerequisites + +- An AKS cluster with Workload Identity enabled +- Azure CLI installed and configured +- `kubectl` configured to access your AKS cluster +- Permissions to create Azure managed identities and role assignments + + +### Enable workload identity on AKS + + +If your AKS cluster doesn't have Workload Identity enabled, update it: + +```shell +export RESOURCE_GROUP= +export CLUSTER_NAME= + +az aks update \ + --resource-group $RESOURCE_GROUP \ + --name $CLUSTER_NAME \ + --enable-oidc-issuer \ + --enable-workload-identity +``` + +Get the OIDC issuer URL: + +```shell +export AKS_OIDC_ISSUER=$(az aks show \ + --resource-group $RESOURCE_GROUP \ + --name $CLUSTER_NAME \ + --query "oidcIssuerProfile.issuerUrl" \ + --output tsv) + +echo $AKS_OIDC_ISSUER +``` + +#### Create an Azure managed identity + +Create a managed identity for Crossplane: + +```shell +export IDENTITY_NAME=crossplane-acr-identity + +az identity create \ + --name $IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP + +export USER_ASSIGNED_CLIENT_ID=$(az identity show \ + --name $IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP \ + --query 'clientId' \ + --output tsv) + +export USER_ASSIGNED_OBJECT_ID=$(az identity show \ + --name $IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP \ + --query 'principalId' \ + --output tsv) + +echo "Client ID: $USER_ASSIGNED_CLIENT_ID" +echo "Object ID: $USER_ASSIGNED_OBJECT_ID" +``` + + +### Assign the ACR pull role + + +Grant the managed identity permission to pull from ACR: + +```shell +export ACR_NAME= + +export ACR_ID=$(az acr show \ + --name $ACR_NAME \ + --query 'id' \ + --output tsv) + +az role assignment create \ + --assignee-object-id $USER_ASSIGNED_OBJECT_ID \ + --assignee-principal-type ServicePrincipal \ + --role AcrPull \ + --scope $ACR_ID +``` + +#### Create a federated identity credential + +Create a federated identity credential that establishes trust between the managed identity and the Kubernetes service account: + +```shell +az identity federated-credential create \ + --name crossplane-federated-credential \ + --identity-name $IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP \ + --issuer $AKS_OIDC_ISSUER \ + --subject system:serviceaccount:crossplane-system:crossplane \ + --audience api://AzureADTokenExchange +``` + +#### Install Crossplane with workload identity configuration + +Get the tenant ID: + +```shell +export AZURE_TENANT_ID=$(az account show --query tenantId --output tsv) +``` + +Install Crossplane with the workload identity annotations and label: + +```shell +helm upgrade --install crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --create-namespace \ + --set "serviceAccount.customAnnotations.azure\.workload\.identity/client-id=$USER_ASSIGNED_CLIENT_ID" \ + --set "serviceAccount.customAnnotations.azure\.workload\.identity/tenant-id=$AZURE_TENANT_ID" \ + --set-string 'customLabels.azure\.workload\.identity/use=true' +``` + +{{% hint "note" %}} +Azure Workload Identity requires: +- Service account annotations for the client ID and tenant ID +- Label `azure.workload.identity/use: "true"` on pods (applied via `customLabels`) + +The `customLabels` setting applies the label to all Crossplane resources. The Azure Workload Identity webhook uses this label on pods to inject environment variables and token volumes. Use `--set-string` to treat the value as a string rather than a boolean. +{{% /hint %}} + +#### Verify the configuration + +Check that the service account has the correct annotations: + +```shell +kubectl get sa crossplane -n crossplane-system -o yaml +``` + +Expected output should include: + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + azure.workload.identity/client-id: + azure.workload.identity/tenant-id: + name: crossplane + namespace: crossplane-system +``` + +Check that the deployment has the required labels: + +```shell +kubectl get deployment crossplane -n crossplane-system -o yaml +``` + +Expected output should include: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + azure.workload.identity/use: "true" + name: crossplane + namespace: crossplane-system +spec: + template: + metadata: + labels: + azure.workload.identity/use: "true" +``` + + +### Test package installation from ACR + + +Once configured, you can install Crossplane packages (Providers, Functions, Configurations) from your ACR. Here's an example using a Provider: + +```shell +kubectl apply -f - < +#### Failed to resolve reference + + +**Error:** +``` +failed to resolve reference: failed to fetch oauth token +``` + +**Solution:** +1. Confirm workload identity is active on the AKS cluster +2. Check the OIDC issuer URL in the federated credential matches your cluster's OIDC issuer +3. Verify the subject in the federated credential matches `system:serviceaccount:crossplane-system:crossplane` + + +#### Invalid federated token + + +**Error:** +``` +invalid federated token +``` + +**Solution:** +1. Verify the federated credential audience uses `api://AzureADTokenExchange` +2. Check that the OIDC issuer URL is correct +3. Confirm the service account namespace and name match the federated credential subject + +#### Service account annotation not applied + +If the service account doesn't have the annotations after installation: + +```shell +# Update via Helm +helm upgrade crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --reuse-values \ + --set "serviceAccount.customAnnotations.azure\.workload\.identity/client-id=$USER_ASSIGNED_CLIENT_ID" \ + --set "serviceAccount.customAnnotations.azure\.workload\.identity/tenant-id=$AZURE_TENANT_ID" + +# Restart Crossplane +kubectl rollout restart deployment/crossplane -n crossplane-system +``` + +#### Check Crossplane logs + +View logs for authentication issues: + +```shell +kubectl logs -n crossplane-system deployment/crossplane --all-containers -f +``` + +Look for: +- Azure authentication errors +- ACR authentication failures +- Image pull errors with specific repository paths + +#### Learn more + +- [Azure Workload Identity Documentation](https://azure.github.io/azure-workload-identity/) +- [AKS Workload Identity Overview](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview) +- [Azure Container Registry Authentication](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-authentication) + +{{% /tab %}} + +{{% tab "Google Cloud GKE" %}} + +## Configure workload identity on GCP + +Configure Crossplane to pull packages from Google Artifact Registry using GKE Workload Identity. + +#### Prerequisites + +- A GKE cluster with Workload Identity enabled +- `gcloud` CLI installed and configured +- `kubectl` configured to access your GKE cluster +- Permissions to create service accounts and IAM bindings + + +### Enable workload identity on GKE + + +If your GKE cluster doesn't have Workload Identity enabled, create a new cluster with it enabled or update an existing cluster: + +**New cluster:** +```shell +export PROJECT_ID= +export CLUSTER_NAME= +export REGION= + +gcloud container clusters create $CLUSTER_NAME \ + --region=$REGION \ + --workload-pool=${PROJECT_ID}.svc.id.goog +``` + +**Existing cluster:** +```shell +gcloud container clusters update $CLUSTER_NAME \ + --region=$REGION \ + --workload-pool=${PROJECT_ID}.svc.id.goog +``` + +#### Create a Google service account + +Create a Google Cloud service account for Crossplane: + +```shell +export GSA_NAME=crossplane-gar-sa + +gcloud iam service-accounts create $GSA_NAME \ + --display-name="Crossplane Artifact Registry Service Account" \ + --project=$PROJECT_ID +``` + +Get the full service account email: + +```shell +export GSA_EMAIL=${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com +echo $GSA_EMAIL +``` + +#### Grant artifact registry permissions + +Grant the service account permissions to read from Artifact Registry: + +```shell +gcloud projects add-iam-policy-binding $PROJECT_ID \ + --member="serviceAccount:${GSA_EMAIL}" \ + --role="roles/artifactregistry.reader" +``` + +For specific repository access, use: + +```shell +export REPOSITORY= +export REPOSITORY_LOCATION= + +gcloud artifacts repositories add-iam-policy-binding $REPOSITORY \ + --location=$REPOSITORY_LOCATION \ + --member="serviceAccount:${GSA_EMAIL}" \ + --role="roles/artifactregistry.reader" +``` + + +### Create an IAM policy binding + + +Create an IAM policy binding between the Google service account and the Kubernetes service account: + +```shell +gcloud iam service-accounts add-iam-policy-binding $GSA_EMAIL \ + --role roles/iam.workloadIdentityUser \ + --member "serviceAccount:${PROJECT_ID}.svc.id.goog[crossplane-system/crossplane]" +``` + +#### Install Crossplane with the workload identity annotation + +Install Crossplane with the service account annotation: + +```shell +helm upgrade --install crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --create-namespace \ + --set "serviceAccount.customAnnotations.iam\.gke\.io/gcp-service-account=$GSA_EMAIL" +``` + +#### Verify the configuration + +Check that the service account has the correct annotation: + +```shell +kubectl get sa crossplane -n crossplane-system -o yaml +``` + +Expected output should include: + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + iam.gke.io/gcp-service-account: crossplane-gar-sa@project-id.iam.gserviceaccount.com + name: crossplane + namespace: crossplane-system +``` + +### Test package installation from artifact registry + +Once configured, you can install Crossplane packages (Providers, Functions, Configurations) from your Artifact Registry. Here's an example using a Provider: + +```shell +kubectl apply -f - < + +#### Failed to fetch OAuth token + + + +**Error:** +``` +failed to fetch oauth token +``` + +**Solution:** +1. Confirm workload identity is active on the GKE cluster +2. Check the IAM policy binding allows the Kubernetes service account to impersonate the Google service account +3. Verify the workload pool matches your project: `${PROJECT_ID}.svc.id.goog` + + +#### Invalid identity token + + +**Error:** +``` +invalid identity token +``` + +**Solution:** +1. Verify the IAM policy binding member format: `serviceAccount:${PROJECT_ID}.svc.id.goog[crossplane-system/crossplane]` +2. Check that workload identity is active on the node pool +3. Confirm the service account annotation is correct + +#### Service account annotation not applied + +If the service account doesn't have the annotation after installation: + +```shell +# Update via Helm +helm upgrade crossplane \ + crossplane-stable/crossplane \ + --namespace crossplane-system \ + --reuse-values \ + --set "serviceAccount.customAnnotations.iam\.gke\.io/gcp-service-account=$GSA_EMAIL" + +# Restart Crossplane +kubectl rollout restart deployment/crossplane -n crossplane-system +``` + +#### Verify workload identity configuration + +Test the workload identity configuration: + +```shell +# Check if workload identity is enabled on the cluster +gcloud container clusters describe $CLUSTER_NAME \ + --region=$REGION \ + --format="value(workloadIdentityConfig.workloadPool)" + +# Verify IAM policy binding +gcloud iam service-accounts get-iam-policy $GSA_EMAIL +``` + +#### Check Crossplane logs + +View logs for authentication issues: + +```shell +kubectl logs -n crossplane-system deployment/crossplane --all-containers -f +``` + +Look for: +- Google Cloud authentication errors +- Artifact Registry authentication failures +- Image pull errors with specific repository paths + +#### Learn more + +- [GKE Workload Identity Documentation](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) +- [Artifact Registry Authentication](https://cloud.google.com/artifact-registry/docs/docker/authentication) +- [IAM Service Account Permissions](https://cloud.google.com/iam/docs/service-accounts) + +{{% /tab %}} + +{{% /tabs %}} + +## Configure after installation + +If Crossplane is already installed, you can update the service account annotations using Helm upgrade with the appropriate `--set` flags shown in your cloud provider's tab. After updating, restart the Crossplane deployment: + +```shell +kubectl rollout restart deployment/crossplane -n crossplane-system +``` diff --git a/content/v2.1/guides/crossplane-with-workload-identity.md b/content/v2.1/guides/crossplane-with-workload-identity.md index 9513aa707..9d9d97292 100644 --- a/content/v2.1/guides/crossplane-with-workload-identity.md +++ b/content/v2.1/guides/crossplane-with-workload-identity.md @@ -4,24 +4,24 @@ weight: 205 description: Configure Crossplane to pull packages from cloud provider container registries using workload identity --- -When running Crossplane on managed Kubernetes clusters (EKS, AKS, GKE), you can leverage Kubernetes Workload Identity to grant Crossplane access to pull packages from private cloud container registries. This allows Crossplane to install providers, functions, and configurations from registries like AWS ECR, Azure ACR, and Google Artifact Registry without managing static credentials. +When running Crossplane on managed Kubernetes clusters (EKS, AKS, GKE), you can use Kubernetes Workload Identity to grant Crossplane access to pull packages from private cloud container registries. This allows Crossplane to install providers, functions, and configurations from registries like AWS ECR, Azure ACR, and Google Artifact Registry without managing static credentials. -{{< hint "important" >}} -This guide configures the **Crossplane package manager** to pull packages from private registries. However, packages reference container images that run as separate pods (providers and functions). +{{% hint "important" %}} +This guide configures the **Crossplane package manager** to pull packages from private registries. Packages reference container images that run as separate pods (providers and functions). **Two-step image pull process:** 1. **Crossplane package manager** pulls the package, extracts the package contents (CRDs, XRDs) and creates deployments 2. **Kubernetes nodes** pull the runtime container images when creating provider/function pods -This guide only covers step 1. For step 2, ensure your Kubernetes nodes have permissions to pull images from the private registry. This is typically configured at the cluster level: +This guide covers step 1. For step 2, ensure your Kubernetes nodes have permissions to pull images from the private registry. Typically configured at the cluster level: - **AWS EKS**: Node IAM role with ECR pull permissions - **Azure AKS**: Kubelet managed identity with `AcrPull` role - **GCP GKE**: Node service account with Artifact Registry reader role Without node-level access, package installation succeeds but pods fail with `ImagePullBackOff`. -{{< /hint >}} +{{% /hint %}} -## Overview +## Introduction To enable Crossplane package manager access to private registries, configure service account annotations during installation. The `crossplane` service account in the `crossplane-system` namespace requires specific annotations for each cloud provider: @@ -29,28 +29,32 @@ To enable Crossplane package manager access to private registries, configure ser - **Azure AKS**: Azure Workload Identity - **Google Cloud GKE**: GKE Workload Identity +## Cloud provider setup + Select your cloud provider below for detailed setup instructions: -{{< tabs >}} +{{% tabs %}} -{{< tab "AWS EKS" >}} +{{% tab "AWS EKS" %}} -## AWS EKS Integration +### Configure workload identity on AWS Configure Crossplane to pull packages from Amazon ECR using IAM Roles for Service Accounts (IRSA). -### Prerequisites +##### Prerequisites - An Amazon EKS cluster with OIDC provider enabled - AWS CLI installed and configured - `kubectl` configured to access your EKS cluster - Permissions to create IAM roles and policies -### Enable OIDC Provider + +#### Enable the OIDC provider + If your EKS cluster doesn't have an OIDC provider, enable it: -```bash +```shell eksctl utils associate-iam-oidc-provider \ --cluster= \ --approve @@ -58,18 +62,20 @@ eksctl utils associate-iam-oidc-provider \ Verify the OIDC provider: -```bash +```shell aws eks describe-cluster \ --name \ --query "cluster.identity.oidc.issuer" \ --output text ``` -### Create IAM Policy for ECR Access + +#### Create an IAM policy for ECR access + Create an IAM policy that grants permissions to pull images from ECR: -```bash +```shell cat > crossplane-ecr-policy.json <}} +{{% hint "note" %}} Replace `` and `` with your AWS region and account ID. You can restrict the `Resource` to specific repositories if needed. -{{< /hint >}} +{{% /hint %}} -### Create IAM Role with Trust Policy + +#### Create an IAM role with trust policy + -Create an IAM role that can be assumed by the Crossplane service account: +Create an IAM role that the Crossplane service account can assume: -```bash +```shell export CLUSTER_NAME= export AWS_REGION= export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) @@ -139,21 +147,23 @@ aws iam create-role \ --assume-role-policy-document file://trust-policy.json ``` -### Attach Policy to Role +#### Attach the policy to the role Attach the ECR policy to the IAM role: -```bash +```shell aws iam attach-role-policy \ --role-name CrossplaneECRRole \ --policy-arn arn:aws:iam::${AWS_ACCOUNT_ID}:policy/CrossplaneECRPolicy ``` -### Install Crossplane with IRSA Annotation + +#### Install Crossplane with the IRSA annotation + Install Crossplane with the service account annotation: -```bash +```shell helm upgrade --install crossplane \ crossplane-stable/crossplane \ --namespace crossplane-system \ @@ -161,11 +171,11 @@ helm upgrade --install crossplane \ --set "serviceAccount.customAnnotations.eks\.amazonaws\.com/role-arn=arn:aws:iam::${AWS_ACCOUNT_ID}:role/CrossplaneECRRole" ``` -### Verify Configuration +##### Verify the configuration Check that the service account has the correct annotation: -```bash +```shell kubectl get sa crossplane -n crossplane-system -o yaml ``` @@ -181,11 +191,13 @@ metadata: namespace: crossplane-system ``` -### Test Package Installation from ECR + +#### Test package installation from ECR + Once configured, you can install Crossplane packages (Providers, Functions, Configurations) from your ECR registry. Here's an example using a Provider: -```bash +```shell kubectl apply -f - < +#### Failed to get authorization token + **Error:** ``` @@ -215,9 +229,11 @@ failed to get authorization token: AccessDeniedException **Solution:** 1. Verify the IAM role has the `ecr:GetAuthorizationToken` permission 2. Check the trust policy allows the service account to assume the role -3. Ensure the service account annotation matches the IAM role ARN exactly +3. Confirm the service account annotation matches the IAM role ARN -#### Invalid Identity Token + +#### Invalid identity token + **Error:** ``` @@ -225,11 +241,13 @@ An error occurred (InvalidIdentityToken) when calling the AssumeRoleWithWebIdent ``` **Solution:** -1. Verify OIDC provider is enabled on your EKS cluster +1. Confirm the OIDC provider is active on your EKS cluster 2. Check the trust policy condition matches your cluster's OIDC provider -3. Ensure the service account namespace and name in the condition are correct +3. Verify the service account namespace and name in the condition are correct -#### Access Denied to ECR Repository + +#### Access denied to ECR repository + **Error:** ``` @@ -239,13 +257,13 @@ denied: User: arn:aws:sts::123456789012:assumed-role/CrossplaneECRRole is not au **Solution:** 1. Verify the IAM policy includes `ecr:BatchGetImage`, `ecr:BatchCheckLayerAvailability`, and `ecr:GetDownloadUrlForLayer` 2. Check the policy's `Resource` includes your ECR repository ARN -3. Ensure the IAM policy is attached to the IAM role +3. Confirm the IAM role has the policy attached -#### Service Account Annotation Not Applied +#### Service account annotation not applied If the service account doesn't have the annotation after installation: -```bash +```shell # Update via Helm helm upgrade crossplane \ crossplane-stable/crossplane \ @@ -257,11 +275,11 @@ helm upgrade crossplane \ kubectl rollout restart deployment/crossplane -n crossplane-system ``` -### Check Crossplane Logs +##### Check Crossplane logs View logs for authentication issues: -```bash +```shell kubectl logs -n crossplane-system deployment/crossplane --all-containers -f ``` @@ -270,31 +288,33 @@ Look for: - ECR authentication failures - Image pull errors with specific repository paths -### Additional Resources +##### Learn more - [Amazon EKS IRSA Documentation](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) - [Amazon ECR Authentication](https://docs.aws.amazon.com/AmazonECR/latest/userguide/security-iam.html) -{{< /tab >}} +{{% /tab %}} -{{< tab "Azure AKS" >}} +{{% tab "Azure AKS" %}} -## Azure AKS Integration +## Configure workload identity on Azure Configure Crossplane to pull packages from Azure Container Registry (ACR) using Azure Workload Identity. -### Prerequisites +#### Prerequisites - An AKS cluster with Workload Identity enabled - Azure CLI installed and configured - `kubectl` configured to access your AKS cluster - Permissions to create Azure managed identities and role assignments -### Enable Workload Identity on AKS + +### Enable workload identity on AKS + If your AKS cluster doesn't have Workload Identity enabled, update it: -```bash +```shell export RESOURCE_GROUP= export CLUSTER_NAME= @@ -307,7 +327,7 @@ az aks update \ Get the OIDC issuer URL: -```bash +```shell export AKS_OIDC_ISSUER=$(az aks show \ --resource-group $RESOURCE_GROUP \ --name $CLUSTER_NAME \ @@ -317,11 +337,11 @@ export AKS_OIDC_ISSUER=$(az aks show \ echo $AKS_OIDC_ISSUER ``` -### Create Azure Managed Identity +#### Create an Azure managed identity Create a managed identity for Crossplane: -```bash +```shell export IDENTITY_NAME=crossplane-acr-identity az identity create \ @@ -344,11 +364,13 @@ echo "Client ID: $USER_ASSIGNED_CLIENT_ID" echo "Object ID: $USER_ASSIGNED_OBJECT_ID" ``` -### Assign ACR Pull Role + +### Assign the ACR pull role + Grant the managed identity permission to pull from ACR: -```bash +```shell export ACR_NAME= export ACR_ID=$(az acr show \ @@ -363,11 +385,11 @@ az role assignment create \ --scope $ACR_ID ``` -### Create Federated Identity Credential +#### Create a federated identity credential Create a federated identity credential that establishes trust between the managed identity and the Kubernetes service account: -```bash +```shell az identity federated-credential create \ --name crossplane-federated-credential \ --identity-name $IDENTITY_NAME \ @@ -377,17 +399,17 @@ az identity federated-credential create \ --audience api://AzureADTokenExchange ``` -### Install Crossplane with Workload Identity Configuration +#### Install Crossplane with workload identity configuration Get the tenant ID: -```bash +```shell export AZURE_TENANT_ID=$(az account show --query tenantId --output tsv) ``` Install Crossplane with the workload identity annotations and label: -```bash +```shell helm upgrade --install crossplane \ crossplane-stable/crossplane \ --namespace crossplane-system \ @@ -397,19 +419,19 @@ helm upgrade --install crossplane \ --set-string 'customLabels.azure\.workload\.identity/use=true' ``` -{{< hint "note" >}} +{{% hint "note" %}} Azure Workload Identity requires: - Service account annotations for the client ID and tenant ID - Label `azure.workload.identity/use: "true"` on pods (applied via `customLabels`) -The `customLabels` setting applies the label to all Crossplane resources. The Azure Workload Identity webhook uses this label on pods to inject the necessary environment variables and token volumes. Use `--set-string` to ensure the value is treated as a string rather than a boolean. -{{< /hint >}} +The `customLabels` setting applies the label to all Crossplane resources. The Azure Workload Identity webhook uses this label on pods to inject environment variables and token volumes. Use `--set-string` to treat the value as a string rather than a boolean. +{{% /hint %}} -### Verify Configuration +#### Verify the configuration Check that the service account has the correct annotations: -```bash +```shell kubectl get sa crossplane -n crossplane-system -o yaml ``` @@ -428,7 +450,7 @@ metadata: Check that the deployment has the required labels: -```bash +```shell kubectl get deployment crossplane -n crossplane-system -o yaml ``` @@ -449,11 +471,13 @@ spec: azure.workload.identity/use: "true" ``` -### Test Package Installation from ACR + +### Test package installation from ACR + Once configured, you can install Crossplane packages (Providers, Functions, Configurations) from your ACR. Here's an example using a Provider: -```bash +```shell kubectl apply -f - < +#### Failed to resolve reference + **Error:** ``` @@ -493,11 +519,13 @@ failed to resolve reference: failed to fetch oauth token ``` **Solution:** -1. Verify Workload Identity is enabled on the AKS cluster +1. Confirm workload identity is active on the AKS cluster 2. Check the OIDC issuer URL in the federated credential matches your cluster's OIDC issuer -3. Ensure the subject in the federated credential matches `system:serviceaccount:crossplane-system:crossplane` +3. Verify the subject in the federated credential matches `system:serviceaccount:crossplane-system:crossplane` -#### Invalid Federated Token + +#### Invalid federated token + **Error:** ``` @@ -505,15 +533,15 @@ invalid federated token ``` **Solution:** -1. Verify the federated credential audience is set to `api://AzureADTokenExchange` +1. Verify the federated credential audience uses `api://AzureADTokenExchange` 2. Check that the OIDC issuer URL is correct -3. Ensure the service account namespace and name match the federated credential subject +3. Confirm the service account namespace and name match the federated credential subject -#### Service Account Annotation Not Applied +#### Service account annotation not applied If the service account doesn't have the annotations after installation: -```bash +```shell # Update via Helm helm upgrade crossplane \ crossplane-stable/crossplane \ @@ -526,11 +554,11 @@ helm upgrade crossplane \ kubectl rollout restart deployment/crossplane -n crossplane-system ``` -### Check Crossplane Logs +#### Check Crossplane logs View logs for authentication issues: -```bash +```shell kubectl logs -n crossplane-system deployment/crossplane --all-containers -f ``` @@ -539,33 +567,35 @@ Look for: - ACR authentication failures - Image pull errors with specific repository paths -### Additional Resources +#### Learn more - [Azure Workload Identity Documentation](https://azure.github.io/azure-workload-identity/) - [AKS Workload Identity Overview](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview) - [Azure Container Registry Authentication](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-authentication) -{{< /tab >}} +{{% /tab %}} -{{< tab "Google Cloud GKE" >}} +{{% tab "Google Cloud GKE" %}} -## Google Cloud GKE Integration +## Configure workload identity on GCP Configure Crossplane to pull packages from Google Artifact Registry using GKE Workload Identity. -### Prerequisites +#### Prerequisites - A GKE cluster with Workload Identity enabled - `gcloud` CLI installed and configured - `kubectl` configured to access your GKE cluster - Permissions to create service accounts and IAM bindings -### Enable Workload Identity on GKE + +### Enable workload identity on GKE + If your GKE cluster doesn't have Workload Identity enabled, create a new cluster with it enabled or update an existing cluster: **New cluster:** -```bash +```shell export PROJECT_ID= export CLUSTER_NAME= export REGION= @@ -576,17 +606,17 @@ gcloud container clusters create $CLUSTER_NAME \ ``` **Existing cluster:** -```bash +```shell gcloud container clusters update $CLUSTER_NAME \ --region=$REGION \ --workload-pool=${PROJECT_ID}.svc.id.goog ``` -### Create Google Service Account +#### Create a Google service account Create a Google Cloud service account for Crossplane: -```bash +```shell export GSA_NAME=crossplane-gar-sa gcloud iam service-accounts create $GSA_NAME \ @@ -596,16 +626,16 @@ gcloud iam service-accounts create $GSA_NAME \ Get the full service account email: -```bash +```shell export GSA_EMAIL=${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com echo $GSA_EMAIL ``` -### Grant Artifact Registry Permissions +#### Grant artifact registry permissions Grant the service account permissions to read from Artifact Registry: -```bash +```shell gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:${GSA_EMAIL}" \ --role="roles/artifactregistry.reader" @@ -613,7 +643,7 @@ gcloud projects add-iam-policy-binding $PROJECT_ID \ For specific repository access, use: -```bash +```shell export REPOSITORY= export REPOSITORY_LOCATION= @@ -623,21 +653,23 @@ gcloud artifacts repositories add-iam-policy-binding $REPOSITORY \ --role="roles/artifactregistry.reader" ``` -### Create IAM Policy Binding + +### Create an IAM policy binding + Create an IAM policy binding between the Google service account and the Kubernetes service account: -```bash +```shell gcloud iam service-accounts add-iam-policy-binding $GSA_EMAIL \ --role roles/iam.workloadIdentityUser \ --member "serviceAccount:${PROJECT_ID}.svc.id.goog[crossplane-system/crossplane]" ``` -### Install Crossplane with Workload Identity Annotation +#### Install Crossplane with the workload identity annotation Install Crossplane with the service account annotation: -```bash +```shell helm upgrade --install crossplane \ crossplane-stable/crossplane \ --namespace crossplane-system \ @@ -645,11 +677,11 @@ helm upgrade --install crossplane \ --set "serviceAccount.customAnnotations.iam\.gke\.io/gcp-service-account=$GSA_EMAIL" ``` -### Verify Configuration +#### Verify the configuration Check that the service account has the correct annotation: -```bash +```shell kubectl get sa crossplane -n crossplane-system -o yaml ``` @@ -665,11 +697,11 @@ metadata: namespace: crossplane-system ``` -### Test Package Installation from Artifact Registry +### Test package installation from artifact registry Once configured, you can install Crossplane packages (Providers, Functions, Configurations) from your Artifact Registry. Here's an example using a Provider: -```bash +```shell kubectl apply -f - < + +#### Failed to fetch OAuth token + + **Error:** ``` @@ -709,11 +745,13 @@ failed to fetch oauth token ``` **Solution:** -1. Verify Workload Identity is enabled on the GKE cluster +1. Confirm workload identity is active on the GKE cluster 2. Check the IAM policy binding allows the Kubernetes service account to impersonate the Google service account -3. Ensure the workload pool matches your project: `${PROJECT_ID}.svc.id.goog` +3. Verify the workload pool matches your project: `${PROJECT_ID}.svc.id.goog` -#### Invalid Identity Token + +#### Invalid identity token + **Error:** ``` @@ -722,14 +760,14 @@ invalid identity token **Solution:** 1. Verify the IAM policy binding member format: `serviceAccount:${PROJECT_ID}.svc.id.goog[crossplane-system/crossplane]` -2. Check that Workload Identity is enabled on the node pool -3. Ensure the service account annotation is correct +2. Check that workload identity is active on the node pool +3. Confirm the service account annotation is correct -#### Service Account Annotation Not Applied +#### Service account annotation not applied If the service account doesn't have the annotation after installation: -```bash +```shell # Update via Helm helm upgrade crossplane \ crossplane-stable/crossplane \ @@ -741,12 +779,12 @@ helm upgrade crossplane \ kubectl rollout restart deployment/crossplane -n crossplane-system ``` -### Verify Workload Identity Configuration +#### Verify workload identity configuration -Test the Workload Identity configuration: +Test the workload identity configuration: -```bash -# Check if Workload Identity is enabled on the cluster +```shell +# Check if workload identity is enabled on the cluster gcloud container clusters describe $CLUSTER_NAME \ --region=$REGION \ --format="value(workloadIdentityConfig.workloadPool)" @@ -755,11 +793,11 @@ gcloud container clusters describe $CLUSTER_NAME \ gcloud iam service-accounts get-iam-policy $GSA_EMAIL ``` -### Check Crossplane Logs +#### Check Crossplane logs View logs for authentication issues: -```bash +```shell kubectl logs -n crossplane-system deployment/crossplane --all-containers -f ``` @@ -768,20 +806,20 @@ Look for: - Artifact Registry authentication failures - Image pull errors with specific repository paths -### Additional Resources +#### Learn more - [GKE Workload Identity Documentation](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) - [Artifact Registry Authentication](https://cloud.google.com/artifact-registry/docs/docker/authentication) - [IAM Service Account Permissions](https://cloud.google.com/iam/docs/service-accounts) -{{< /tab >}} +{{% /tab %}} -{{< /tabs >}} +{{% /tabs %}} -## Configure After Installation +## Configure after installation -If Crossplane is already installed, you can update the service account annotations using Helm upgrade with the appropriate `--set` flags shown in your cloud provider's tab above. After updating, restart the Crossplane deployment: +If Crossplane is already installed, you can update the service account annotations using Helm upgrade with the appropriate `--set` flags shown in your cloud provider's tab. After updating, restart the Crossplane deployment: -```bash +```shell kubectl rollout restart deployment/crossplane -n crossplane-system ``` diff --git a/utils/vale/styles/Crossplane/provider-words.txt b/utils/vale/styles/Crossplane/provider-words.txt index f2b11dd29..181bfc080 100644 --- a/utils/vale/styles/Crossplane/provider-words.txt +++ b/utils/vale/styles/Crossplane/provider-words.txt @@ -1,16 +1,24 @@ AccessKey AccessKeys +ACR +AKS +ARN crossplane-contrib Dataflow DynmoDB EC2 +ECR EKS eu-north-1 europe-central2 GCP GCP's GKE +Kubelet +node-level +OAuth provider-aws +Two-step provider-aws-iam provider-aws-s3 provider-gcp From 0ddc895453a197588c80b3f1f9a3d7a7c7b540d4 Mon Sep 17 00:00:00 2001 From: Christopher Haar Date: Mon, 2 Feb 2026 09:50:36 +0100 Subject: [PATCH 3/3] docs(hugo): switch % to <> Signed-off-by: Christopher Haar --- .../crossplane-with-workload-identity.md | 42 +++++++++---------- .../crossplane-with-workload-identity.md | 42 +++++++++---------- .../crossplane-with-workload-identity.md | 42 +++++++++---------- .../crossplane-with-workload-identity.md | 42 +++++++++---------- 4 files changed, 84 insertions(+), 84 deletions(-) diff --git a/content/master/guides/crossplane-with-workload-identity.md b/content/master/guides/crossplane-with-workload-identity.md index 9d9d97292..0b40b0411 100644 --- a/content/master/guides/crossplane-with-workload-identity.md +++ b/content/master/guides/crossplane-with-workload-identity.md @@ -6,7 +6,7 @@ description: Configure Crossplane to pull packages from cloud provider container When running Crossplane on managed Kubernetes clusters (EKS, AKS, GKE), you can use Kubernetes Workload Identity to grant Crossplane access to pull packages from private cloud container registries. This allows Crossplane to install providers, functions, and configurations from registries like AWS ECR, Azure ACR, and Google Artifact Registry without managing static credentials. -{{% hint "important" %}} +{{< hint "important" >}} This guide configures the **Crossplane package manager** to pull packages from private registries. Packages reference container images that run as separate pods (providers and functions). **Two-step image pull process:** @@ -19,7 +19,7 @@ This guide covers step 1. For step 2, ensure your Kubernetes nodes have permissi - **GCP GKE**: Node service account with Artifact Registry reader role Without node-level access, package installation succeeds but pods fail with `ImagePullBackOff`. -{{% /hint %}} +{{< /hint >}} ## Introduction @@ -33,9 +33,9 @@ To enable Crossplane package manager access to private registries, configure ser Select your cloud provider below for detailed setup instructions: -{{% tabs %}} +{{< tabs >}} -{{% tab "AWS EKS" %}} +{{< tab "AWS EKS" >}} ### Configure workload identity on AWS @@ -105,9 +105,9 @@ aws iam create-policy \ --policy-document file://crossplane-ecr-policy.json ``` -{{% hint "note" %}} +{{< hint "note" >}} Replace `` and `` with your AWS region and account ID. You can restrict the `Resource` to specific repositories if needed. -{{% /hint %}} +{{< /hint >}} #### Create an IAM role with trust policy @@ -215,10 +215,10 @@ kubectl get provider provider-aws-s3 kubectl describe provider provider-aws-s3 ``` -##### Troubleshooting +#### Troubleshooting -#### Failed to get authorization token +##### Failed to get authorization token **Error:** @@ -232,7 +232,7 @@ failed to get authorization token: AccessDeniedException 3. Confirm the service account annotation matches the IAM role ARN -#### Invalid identity token +##### Invalid identity token **Error:** @@ -246,7 +246,7 @@ An error occurred (InvalidIdentityToken) when calling the AssumeRoleWithWebIdent 3. Verify the service account namespace and name in the condition are correct -#### Access denied to ECR repository +##### Access denied to ECR repository **Error:** @@ -259,7 +259,7 @@ denied: User: arn:aws:sts::123456789012:assumed-role/CrossplaneECRRole is not au 2. Check the policy's `Resource` includes your ECR repository ARN 3. Confirm the IAM role has the policy attached -#### Service account annotation not applied +##### Service account annotation not applied If the service account doesn't have the annotation after installation: @@ -275,7 +275,7 @@ helm upgrade crossplane \ kubectl rollout restart deployment/crossplane -n crossplane-system ``` -##### Check Crossplane logs +#### Check Crossplane logs View logs for authentication issues: @@ -288,14 +288,14 @@ Look for: - ECR authentication failures - Image pull errors with specific repository paths -##### Learn more +#### Learn more - [Amazon EKS IRSA Documentation](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) - [Amazon ECR Authentication](https://docs.aws.amazon.com/AmazonECR/latest/userguide/security-iam.html) -{{% /tab %}} +{{< /tab >}} -{{% tab "Azure AKS" %}} +{{< tab "Azure AKS" >}} ## Configure workload identity on Azure @@ -419,13 +419,13 @@ helm upgrade --install crossplane \ --set-string 'customLabels.azure\.workload\.identity/use=true' ``` -{{% hint "note" %}} +{{< hint "note" >}} Azure Workload Identity requires: - Service account annotations for the client ID and tenant ID - Label `azure.workload.identity/use: "true"` on pods (applied via `customLabels`) The `customLabels` setting applies the label to all Crossplane resources. The Azure Workload Identity webhook uses this label on pods to inject environment variables and token volumes. Use `--set-string` to treat the value as a string rather than a boolean. -{{% /hint %}} +{{< /hint >}} #### Verify the configuration @@ -573,9 +573,9 @@ Look for: - [AKS Workload Identity Overview](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview) - [Azure Container Registry Authentication](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-authentication) -{{% /tab %}} +{{< /tab >}} -{{% tab "Google Cloud GKE" %}} +{{< tab "Google Cloud GKE" >}} ## Configure workload identity on GCP @@ -812,9 +812,9 @@ Look for: - [Artifact Registry Authentication](https://cloud.google.com/artifact-registry/docs/docker/authentication) - [IAM Service Account Permissions](https://cloud.google.com/iam/docs/service-accounts) -{{% /tab %}} +{{< /tab >}} -{{% /tabs %}} +{{< /tabs >}} ## Configure after installation diff --git a/content/v1.20/guides/crossplane-with-workload-identity.md b/content/v1.20/guides/crossplane-with-workload-identity.md index 9d9d97292..0b40b0411 100644 --- a/content/v1.20/guides/crossplane-with-workload-identity.md +++ b/content/v1.20/guides/crossplane-with-workload-identity.md @@ -6,7 +6,7 @@ description: Configure Crossplane to pull packages from cloud provider container When running Crossplane on managed Kubernetes clusters (EKS, AKS, GKE), you can use Kubernetes Workload Identity to grant Crossplane access to pull packages from private cloud container registries. This allows Crossplane to install providers, functions, and configurations from registries like AWS ECR, Azure ACR, and Google Artifact Registry without managing static credentials. -{{% hint "important" %}} +{{< hint "important" >}} This guide configures the **Crossplane package manager** to pull packages from private registries. Packages reference container images that run as separate pods (providers and functions). **Two-step image pull process:** @@ -19,7 +19,7 @@ This guide covers step 1. For step 2, ensure your Kubernetes nodes have permissi - **GCP GKE**: Node service account with Artifact Registry reader role Without node-level access, package installation succeeds but pods fail with `ImagePullBackOff`. -{{% /hint %}} +{{< /hint >}} ## Introduction @@ -33,9 +33,9 @@ To enable Crossplane package manager access to private registries, configure ser Select your cloud provider below for detailed setup instructions: -{{% tabs %}} +{{< tabs >}} -{{% tab "AWS EKS" %}} +{{< tab "AWS EKS" >}} ### Configure workload identity on AWS @@ -105,9 +105,9 @@ aws iam create-policy \ --policy-document file://crossplane-ecr-policy.json ``` -{{% hint "note" %}} +{{< hint "note" >}} Replace `` and `` with your AWS region and account ID. You can restrict the `Resource` to specific repositories if needed. -{{% /hint %}} +{{< /hint >}} #### Create an IAM role with trust policy @@ -215,10 +215,10 @@ kubectl get provider provider-aws-s3 kubectl describe provider provider-aws-s3 ``` -##### Troubleshooting +#### Troubleshooting -#### Failed to get authorization token +##### Failed to get authorization token **Error:** @@ -232,7 +232,7 @@ failed to get authorization token: AccessDeniedException 3. Confirm the service account annotation matches the IAM role ARN -#### Invalid identity token +##### Invalid identity token **Error:** @@ -246,7 +246,7 @@ An error occurred (InvalidIdentityToken) when calling the AssumeRoleWithWebIdent 3. Verify the service account namespace and name in the condition are correct -#### Access denied to ECR repository +##### Access denied to ECR repository **Error:** @@ -259,7 +259,7 @@ denied: User: arn:aws:sts::123456789012:assumed-role/CrossplaneECRRole is not au 2. Check the policy's `Resource` includes your ECR repository ARN 3. Confirm the IAM role has the policy attached -#### Service account annotation not applied +##### Service account annotation not applied If the service account doesn't have the annotation after installation: @@ -275,7 +275,7 @@ helm upgrade crossplane \ kubectl rollout restart deployment/crossplane -n crossplane-system ``` -##### Check Crossplane logs +#### Check Crossplane logs View logs for authentication issues: @@ -288,14 +288,14 @@ Look for: - ECR authentication failures - Image pull errors with specific repository paths -##### Learn more +#### Learn more - [Amazon EKS IRSA Documentation](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) - [Amazon ECR Authentication](https://docs.aws.amazon.com/AmazonECR/latest/userguide/security-iam.html) -{{% /tab %}} +{{< /tab >}} -{{% tab "Azure AKS" %}} +{{< tab "Azure AKS" >}} ## Configure workload identity on Azure @@ -419,13 +419,13 @@ helm upgrade --install crossplane \ --set-string 'customLabels.azure\.workload\.identity/use=true' ``` -{{% hint "note" %}} +{{< hint "note" >}} Azure Workload Identity requires: - Service account annotations for the client ID and tenant ID - Label `azure.workload.identity/use: "true"` on pods (applied via `customLabels`) The `customLabels` setting applies the label to all Crossplane resources. The Azure Workload Identity webhook uses this label on pods to inject environment variables and token volumes. Use `--set-string` to treat the value as a string rather than a boolean. -{{% /hint %}} +{{< /hint >}} #### Verify the configuration @@ -573,9 +573,9 @@ Look for: - [AKS Workload Identity Overview](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview) - [Azure Container Registry Authentication](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-authentication) -{{% /tab %}} +{{< /tab >}} -{{% tab "Google Cloud GKE" %}} +{{< tab "Google Cloud GKE" >}} ## Configure workload identity on GCP @@ -812,9 +812,9 @@ Look for: - [Artifact Registry Authentication](https://cloud.google.com/artifact-registry/docs/docker/authentication) - [IAM Service Account Permissions](https://cloud.google.com/iam/docs/service-accounts) -{{% /tab %}} +{{< /tab >}} -{{% /tabs %}} +{{< /tabs >}} ## Configure after installation diff --git a/content/v2.0/guides/crossplane-with-workload-identity.md b/content/v2.0/guides/crossplane-with-workload-identity.md index 9d9d97292..0b40b0411 100644 --- a/content/v2.0/guides/crossplane-with-workload-identity.md +++ b/content/v2.0/guides/crossplane-with-workload-identity.md @@ -6,7 +6,7 @@ description: Configure Crossplane to pull packages from cloud provider container When running Crossplane on managed Kubernetes clusters (EKS, AKS, GKE), you can use Kubernetes Workload Identity to grant Crossplane access to pull packages from private cloud container registries. This allows Crossplane to install providers, functions, and configurations from registries like AWS ECR, Azure ACR, and Google Artifact Registry without managing static credentials. -{{% hint "important" %}} +{{< hint "important" >}} This guide configures the **Crossplane package manager** to pull packages from private registries. Packages reference container images that run as separate pods (providers and functions). **Two-step image pull process:** @@ -19,7 +19,7 @@ This guide covers step 1. For step 2, ensure your Kubernetes nodes have permissi - **GCP GKE**: Node service account with Artifact Registry reader role Without node-level access, package installation succeeds but pods fail with `ImagePullBackOff`. -{{% /hint %}} +{{< /hint >}} ## Introduction @@ -33,9 +33,9 @@ To enable Crossplane package manager access to private registries, configure ser Select your cloud provider below for detailed setup instructions: -{{% tabs %}} +{{< tabs >}} -{{% tab "AWS EKS" %}} +{{< tab "AWS EKS" >}} ### Configure workload identity on AWS @@ -105,9 +105,9 @@ aws iam create-policy \ --policy-document file://crossplane-ecr-policy.json ``` -{{% hint "note" %}} +{{< hint "note" >}} Replace `` and `` with your AWS region and account ID. You can restrict the `Resource` to specific repositories if needed. -{{% /hint %}} +{{< /hint >}} #### Create an IAM role with trust policy @@ -215,10 +215,10 @@ kubectl get provider provider-aws-s3 kubectl describe provider provider-aws-s3 ``` -##### Troubleshooting +#### Troubleshooting -#### Failed to get authorization token +##### Failed to get authorization token **Error:** @@ -232,7 +232,7 @@ failed to get authorization token: AccessDeniedException 3. Confirm the service account annotation matches the IAM role ARN -#### Invalid identity token +##### Invalid identity token **Error:** @@ -246,7 +246,7 @@ An error occurred (InvalidIdentityToken) when calling the AssumeRoleWithWebIdent 3. Verify the service account namespace and name in the condition are correct -#### Access denied to ECR repository +##### Access denied to ECR repository **Error:** @@ -259,7 +259,7 @@ denied: User: arn:aws:sts::123456789012:assumed-role/CrossplaneECRRole is not au 2. Check the policy's `Resource` includes your ECR repository ARN 3. Confirm the IAM role has the policy attached -#### Service account annotation not applied +##### Service account annotation not applied If the service account doesn't have the annotation after installation: @@ -275,7 +275,7 @@ helm upgrade crossplane \ kubectl rollout restart deployment/crossplane -n crossplane-system ``` -##### Check Crossplane logs +#### Check Crossplane logs View logs for authentication issues: @@ -288,14 +288,14 @@ Look for: - ECR authentication failures - Image pull errors with specific repository paths -##### Learn more +#### Learn more - [Amazon EKS IRSA Documentation](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) - [Amazon ECR Authentication](https://docs.aws.amazon.com/AmazonECR/latest/userguide/security-iam.html) -{{% /tab %}} +{{< /tab >}} -{{% tab "Azure AKS" %}} +{{< tab "Azure AKS" >}} ## Configure workload identity on Azure @@ -419,13 +419,13 @@ helm upgrade --install crossplane \ --set-string 'customLabels.azure\.workload\.identity/use=true' ``` -{{% hint "note" %}} +{{< hint "note" >}} Azure Workload Identity requires: - Service account annotations for the client ID and tenant ID - Label `azure.workload.identity/use: "true"` on pods (applied via `customLabels`) The `customLabels` setting applies the label to all Crossplane resources. The Azure Workload Identity webhook uses this label on pods to inject environment variables and token volumes. Use `--set-string` to treat the value as a string rather than a boolean. -{{% /hint %}} +{{< /hint >}} #### Verify the configuration @@ -573,9 +573,9 @@ Look for: - [AKS Workload Identity Overview](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview) - [Azure Container Registry Authentication](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-authentication) -{{% /tab %}} +{{< /tab >}} -{{% tab "Google Cloud GKE" %}} +{{< tab "Google Cloud GKE" >}} ## Configure workload identity on GCP @@ -812,9 +812,9 @@ Look for: - [Artifact Registry Authentication](https://cloud.google.com/artifact-registry/docs/docker/authentication) - [IAM Service Account Permissions](https://cloud.google.com/iam/docs/service-accounts) -{{% /tab %}} +{{< /tab >}} -{{% /tabs %}} +{{< /tabs >}} ## Configure after installation diff --git a/content/v2.1/guides/crossplane-with-workload-identity.md b/content/v2.1/guides/crossplane-with-workload-identity.md index 9d9d97292..0b40b0411 100644 --- a/content/v2.1/guides/crossplane-with-workload-identity.md +++ b/content/v2.1/guides/crossplane-with-workload-identity.md @@ -6,7 +6,7 @@ description: Configure Crossplane to pull packages from cloud provider container When running Crossplane on managed Kubernetes clusters (EKS, AKS, GKE), you can use Kubernetes Workload Identity to grant Crossplane access to pull packages from private cloud container registries. This allows Crossplane to install providers, functions, and configurations from registries like AWS ECR, Azure ACR, and Google Artifact Registry without managing static credentials. -{{% hint "important" %}} +{{< hint "important" >}} This guide configures the **Crossplane package manager** to pull packages from private registries. Packages reference container images that run as separate pods (providers and functions). **Two-step image pull process:** @@ -19,7 +19,7 @@ This guide covers step 1. For step 2, ensure your Kubernetes nodes have permissi - **GCP GKE**: Node service account with Artifact Registry reader role Without node-level access, package installation succeeds but pods fail with `ImagePullBackOff`. -{{% /hint %}} +{{< /hint >}} ## Introduction @@ -33,9 +33,9 @@ To enable Crossplane package manager access to private registries, configure ser Select your cloud provider below for detailed setup instructions: -{{% tabs %}} +{{< tabs >}} -{{% tab "AWS EKS" %}} +{{< tab "AWS EKS" >}} ### Configure workload identity on AWS @@ -105,9 +105,9 @@ aws iam create-policy \ --policy-document file://crossplane-ecr-policy.json ``` -{{% hint "note" %}} +{{< hint "note" >}} Replace `` and `` with your AWS region and account ID. You can restrict the `Resource` to specific repositories if needed. -{{% /hint %}} +{{< /hint >}} #### Create an IAM role with trust policy @@ -215,10 +215,10 @@ kubectl get provider provider-aws-s3 kubectl describe provider provider-aws-s3 ``` -##### Troubleshooting +#### Troubleshooting -#### Failed to get authorization token +##### Failed to get authorization token **Error:** @@ -232,7 +232,7 @@ failed to get authorization token: AccessDeniedException 3. Confirm the service account annotation matches the IAM role ARN -#### Invalid identity token +##### Invalid identity token **Error:** @@ -246,7 +246,7 @@ An error occurred (InvalidIdentityToken) when calling the AssumeRoleWithWebIdent 3. Verify the service account namespace and name in the condition are correct -#### Access denied to ECR repository +##### Access denied to ECR repository **Error:** @@ -259,7 +259,7 @@ denied: User: arn:aws:sts::123456789012:assumed-role/CrossplaneECRRole is not au 2. Check the policy's `Resource` includes your ECR repository ARN 3. Confirm the IAM role has the policy attached -#### Service account annotation not applied +##### Service account annotation not applied If the service account doesn't have the annotation after installation: @@ -275,7 +275,7 @@ helm upgrade crossplane \ kubectl rollout restart deployment/crossplane -n crossplane-system ``` -##### Check Crossplane logs +#### Check Crossplane logs View logs for authentication issues: @@ -288,14 +288,14 @@ Look for: - ECR authentication failures - Image pull errors with specific repository paths -##### Learn more +#### Learn more - [Amazon EKS IRSA Documentation](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) - [Amazon ECR Authentication](https://docs.aws.amazon.com/AmazonECR/latest/userguide/security-iam.html) -{{% /tab %}} +{{< /tab >}} -{{% tab "Azure AKS" %}} +{{< tab "Azure AKS" >}} ## Configure workload identity on Azure @@ -419,13 +419,13 @@ helm upgrade --install crossplane \ --set-string 'customLabels.azure\.workload\.identity/use=true' ``` -{{% hint "note" %}} +{{< hint "note" >}} Azure Workload Identity requires: - Service account annotations for the client ID and tenant ID - Label `azure.workload.identity/use: "true"` on pods (applied via `customLabels`) The `customLabels` setting applies the label to all Crossplane resources. The Azure Workload Identity webhook uses this label on pods to inject environment variables and token volumes. Use `--set-string` to treat the value as a string rather than a boolean. -{{% /hint %}} +{{< /hint >}} #### Verify the configuration @@ -573,9 +573,9 @@ Look for: - [AKS Workload Identity Overview](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview) - [Azure Container Registry Authentication](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-authentication) -{{% /tab %}} +{{< /tab >}} -{{% tab "Google Cloud GKE" %}} +{{< tab "Google Cloud GKE" >}} ## Configure workload identity on GCP @@ -812,9 +812,9 @@ Look for: - [Artifact Registry Authentication](https://cloud.google.com/artifact-registry/docs/docker/authentication) - [IAM Service Account Permissions](https://cloud.google.com/iam/docs/service-accounts) -{{% /tab %}} +{{< /tab >}} -{{% /tabs %}} +{{< /tabs >}} ## Configure after installation