From 3355c1819dec403509cc4aac1dbba106d3ec3d20 Mon Sep 17 00:00:00 2001 From: Rae Sharp Date: Thu, 12 Mar 2026 11:52:34 -0400 Subject: [PATCH 1/4] initial commit --- docs/manuals/uxp/howtos/secrets-proxy.md | 333 +++++++++++++++++++++++ 1 file changed, 333 insertions(+) create mode 100644 docs/manuals/uxp/howtos/secrets-proxy.md diff --git a/docs/manuals/uxp/howtos/secrets-proxy.md b/docs/manuals/uxp/howtos/secrets-proxy.md new file mode 100644 index 00000000..62e61715 --- /dev/null +++ b/docs/manuals/uxp/howtos/secrets-proxy.md @@ -0,0 +1,333 @@ +--- +title: Use the Secrets Proxy +description: "Learn how to configure the Secrets Proxy to access secrets from an external Vault secret store" +sidebar_position: 50 +--- + +This guide explains how to configure the Secrets Proxy on an existing UXP +instance to retrieve secrets from an external HashiCorp Vault instance instead +of storing them as Kubernetes Secrets. + +The example composition creates IAM users and access keys. Rather than writing +connection secrets to Kubernetes, the Secrets Proxy transparently reads and +writes them to Vault. + +## Prerequisites + +Before you begin, ensure you have: + +* A running UXP instance with the Secrets Proxy feature enabled +* A running HashiCorp Vault instance +* [kubectl](https://kubernetes.io/docs/tasks/tools/) configured to point to your UXP cluster +* The [Vault CLI](https://developer.hashicorp.com/vault/docs/install) installed with `VAULT_ADDR` and `VAULT_TOKEN` set +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) configured with credentials that can manage IAM resources + +:::note +To enable the Secrets Proxy on an existing UXP installation, upgrade with +`--set upbound.secretsProxy.enabled=true`. +::: + +## Bootstrap a test Vault instance + +If you don't have a Vault instance, you can deploy one in dev mode into your +cluster using the official Helm chart. Dev mode starts with a pre-configured +root token and KV v2 secrets engine enabled at `secret/`. It is not persistent +and is only suitable for testing. + +1. Add the HashiCorp Helm repo and install Vault in dev mode: + + ```shell + helm repo add hashicorp https://helm.releases.hashicorp.com + helm repo update + + helm install vault hashicorp/vault \ + --namespace vault-system \ + --create-namespace \ + --set server.dev.enabled=true \ + --set server.dev.devRootToken=root + ``` + +2. Wait for the Vault pod to be ready, then set your CLI environment variables: + + ```shell + kubectl wait pod/vault-0 -n vault-system --for=condition=Ready --timeout=60s + + kubectl port-forward svc/vault 8200:8200 -n vault-system & + export VAULT_ADDR=http://127.0.0.1:8200 + export VAULT_TOKEN=root + ``` + + The Secrets Proxy sidecar reaches Vault in-cluster at + `http://vault.vault-system.svc.cluster.local:8200`. + +:::warning +Dev mode Vault is in-memory only. All data is lost when the pod restarts. +::: + +## Configure Vault + +Store the AWS credentials that Crossplane providers use to manage resources, +and create a policy granting the Secrets Proxy read access. + +1. Store the AWS credentials in Vault. The Secrets Proxy serves these to + providers as if they were a Kubernetes Secret, using the AWS credentials `ini` + format expected by the provider family: + + ```shell + vault kv put secret/crossplane-system/aws-official-creds credentials=" + [default] + aws_access_key_id = + aws_secret_access_key = + " + ``` + +2. Create a Vault policy that grants read access to secrets in the + `crossplane-system` scope: + + ```shell + vault policy write crossplane-policy - <<'EOF' + path "secret/data/crossplane-system/*" { + capabilities = ["read", "list"] + } + EOF + ``` + +## Register your cluster with Vault + +Configure Vault's Kubernetes auth method to trust service accounts running in +your UXP cluster. This allows the Secrets Proxy sidecar to authenticate to +Vault and read secrets on behalf of provider pods. + +1. Create a service account for Vault token review and grant it permission to + validate tokens cluster-wide: + + ```shell + kubectl create serviceaccount vault-auth -n crossplane-system + + kubectl create clusterrolebinding vault-auth-delegator \ + --clusterrole=system:auth-delegator \ + --serviceaccount=crossplane-system:vault-auth + ``` + +2. Generate a long-lived token for the reviewer service account: + + ```shell + REVIEWER_JWT=$(kubectl create token vault-auth \ + -n crossplane-system --duration=8760h) + ``` + +3. Retrieve the cluster CA certificate and API server URL: + + ```shell + KUBE_CA_CERT=$(kubectl config view --raw --minify --flatten \ + --output='jsonpath={.clusters[].cluster.certificate-authority-data}' \ + | base64 --decode) + + KUBE_HOST=$(kubectl config view --raw --minify --flatten \ + --output='jsonpath={.clusters[].cluster.server}') + ``` + +4. Enable the Kubernetes auth method in Vault and configure it with the cluster + details: + + ```shell + vault auth enable kubernetes + + vault write auth/kubernetes/config \ + kubernetes_host="${KUBE_HOST}" \ + kubernetes_ca_cert="${KUBE_CA_CERT}" \ + token_reviewer_jwt="${REVIEWER_JWT}" + ``` + +5. Create a Vault role that binds all service accounts in `crossplane-system` + to the policy: + + ```shell + vault write auth/kubernetes/role/crossplane \ + bound_service_account_names="*" \ + bound_service_account_namespaces="crossplane-system" \ + policies=crossplane-policy \ + ttl=1h + ``` + +## Install the Secret Store addon + +Apply the Secret Store addon to deploy the `secret-store-vault` component that +connects the Secrets Proxy to your Vault instance: + +```yaml +apiVersion: pkg.upbound.io/v1beta1 +kind: AddOn +metadata: + name: secret-store-vault +spec: + package: xpkg.upbound.io/upbound/secret-store-vault-addon:v0.1.0 + packagePullPolicy: Always +--- +apiVersion: pkg.upbound.io/v1beta1 +kind: AddOnRuntimeConfig +metadata: + name: default +spec: + helm: + values: + imagePullSecrets: + - name: ecr-pull-secret +``` + +```shell +kubectl apply -f secretstore.yaml +``` + +## Configure the Secrets Proxy webhook + +Apply the webhook configuration to inject the Secrets Proxy sidecar into +Crossplane and provider pods. The webhook restarts all matching pods on apply: + +```yaml +apiVersion: secretsproxy.upbound.io/v1alpha1 +kind: WebhookConfig +metadata: + name: crossplane-app +spec: + objectSelector: + matchLabels: + app: crossplane +--- +apiVersion: secretsproxy.upbound.io/v1alpha1 +kind: WebhookConfig +metadata: + name: crossplane-provider +spec: + objectSelector: + matchExpressions: + - key: pkg.crossplane.io/provider + operator: Exists +``` + +```shell +kubectl apply -f webhookconfig.yaml +``` + +## Install providers and functions + +1. Install the AWS provider family and IAM provider. Because the webhook matches + pods with the `pkg.crossplane.io/provider` label, provider pods start with + the Secrets Proxy sidecar already injected: + + ```yaml + apiVersion: pkg.crossplane.io/v1 + kind: Provider + metadata: + name: upbound-provider-aws-iam + spec: + package: xpkg.upbound.io/upbound/provider-aws-iam:v2.2.0 + ignoreCrossplaneConstraints: true + --- + apiVersion: pkg.crossplane.io/v1 + kind: Provider + metadata: + name: upbound-provider-family-aws + spec: + package: xpkg.upbound.io/upbound/provider-family-aws:v2.2.0 + ignoreCrossplaneConstraints: true + ``` + + ```shell + kubectl apply -f providers.yaml + ``` + +2. Install the pipeline functions: + + ```yaml + apiVersion: pkg.crossplane.io/v1 + kind: Function + metadata: + name: function-go-templating + spec: + package: xpkg.crossplane.io/crossplane-contrib/function-go-templating:v0.11.2 + --- + apiVersion: pkg.crossplane.io/v1 + kind: Function + metadata: + name: function-auto-ready + spec: + package: xpkg.crossplane.io/crossplane-contrib/function-auto-ready:v0.6.0 + ``` + + ```shell + kubectl apply -f functions.yaml + ``` + +3. Wait for providers and functions to become healthy, then create the provider + config. The `aws-official-creds` secret is stored in Vault — the Secrets + Proxy intercepts the Secret API call and serves it transparently: + + ```yaml + apiVersion: aws.m.upbound.io/v1beta1 + kind: ClusterProviderConfig + metadata: + name: default + namespace: crossplane-system + spec: + credentials: + secretRef: + key: credentials + name: aws-official-creds + namespace: crossplane-system + source: Secret + ``` + + ```shell + kubectl apply -f provider-config.yaml + ``` + +## Deploy the composition + +Apply the `UserAccessKey` XRD and composition. This composition creates an IAM +user and two access keys, writing connection details back to Vault through the +Secrets Proxy: + +```shell +kubectl apply -f comp.yaml +``` + +## Create the composite resource + +1. Edit `xr.yaml` and replace `` with your initials in both + the `metadata.name` and `spec.writeConnectionSecretToRef.name` fields: + + ```yaml + apiVersion: example.org/v1alpha1 + kind: UserAccessKey + metadata: + namespace: default + name: -keys + spec: + writeConnectionSecretToRef: + name: -keys-connection-details + ``` + +2. Apply the resource and verify reconciliation: + + ```shell + kubectl apply -f xr.yaml + kubectl get managed + kubectl get composite + ``` + + Connection details are stored in Vault, not in Kubernetes. Confirm no new + secrets were created: + + ```shell + kubectl get secret -n crossplane-system + ``` + +## Clean up + +Delete the composite resource. The garbage collector automatically removes the +associated secrets from Vault: + +```shell +kubectl delete -f xr.yaml +``` From 33887cb4bc29da0085785074e888bc2fd35af8db Mon Sep 17 00:00:00 2001 From: Rae Sharp Date: Thu, 12 Mar 2026 12:03:31 -0400 Subject: [PATCH 2/4] update vale --- docs/manuals/uxp/howtos/secrets-proxy.md | 26 ++++++++++++++++-------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/manuals/uxp/howtos/secrets-proxy.md b/docs/manuals/uxp/howtos/secrets-proxy.md index 62e61715..dc85289f 100644 --- a/docs/manuals/uxp/howtos/secrets-proxy.md +++ b/docs/manuals/uxp/howtos/secrets-proxy.md @@ -5,8 +5,10 @@ sidebar_position: 50 --- This guide explains how to configure the Secrets Proxy on an existing UXP -instance to retrieve secrets from an external HashiCorp Vault instance instead -of storing them as Kubernetes Secrets. +instance to retrieve secrets from an external HashiCorp Vault. + +The Secret Store add-on securely stores your secrets instead of storing them as +a Kubernetes secret in the cluster. The example composition creates IAM users and access keys. Rather than writing connection secrets to Kubernetes, the Secrets Proxy transparently reads and @@ -31,10 +33,12 @@ To enable the Secrets Proxy on an existing UXP installation, upgrade with If you don't have a Vault instance, you can deploy one in dev mode into your cluster using the official Helm chart. Dev mode starts with a pre-configured -root token and KV v2 secrets engine enabled at `secret/`. It is not persistent +root token and KV v2 secrets engine enabled at `secret/`. It's not persistent and is only suitable for testing. + 1. Add the HashiCorp Helm repo and install Vault in dev mode: + ```shell helm repo add hashicorp https://helm.releases.hashicorp.com @@ -60,9 +64,11 @@ and is only suitable for testing. The Secrets Proxy sidecar reaches Vault in-cluster at `http://vault.vault-system.svc.cluster.local:8200`. + :::warning Dev mode Vault is in-memory only. All data is lost when the pod restarts. ::: + ## Configure Vault @@ -149,10 +155,10 @@ Vault and read secrets on behalf of provider pods. policies=crossplane-policy \ ttl=1h ``` + +## Install the Secret Store add-on -## Install the Secret Store addon - -Apply the Secret Store addon to deploy the `secret-store-vault` component that +Apply the Secret Store add-on to deploy the `secret-store-vault` component that connects the Secrets Proxy to your Vault instance: ```yaml @@ -258,10 +264,11 @@ kubectl apply -f webhookconfig.yaml ```shell kubectl apply -f functions.yaml ``` - + 3. Wait for providers and functions to become healthy, then create the provider - config. The `aws-official-creds` secret is stored in Vault — the Secrets + config. The `aws-official-creds` secret is stored in Vault. The Secrets Proxy intercepts the Secret API call and serves it transparently: + ```yaml apiVersion: aws.m.upbound.io/v1beta1 @@ -295,7 +302,8 @@ kubectl apply -f comp.yaml ## Create the composite resource 1. Edit `xr.yaml` and replace `` with your initials in both - the `metadata.name` and `spec.writeConnectionSecretToRef.name` fields: + the `metadata.name` and `spec.writeConnectionSecretToRef.name` fields to + create a unique name: ```yaml apiVersion: example.org/v1alpha1 From f4c8741077553adaa80f7fac6805bf07960b31b4 Mon Sep 17 00:00:00 2001 From: Rae Sharp Date: Thu, 12 Mar 2026 12:28:31 -0400 Subject: [PATCH 3/4] update placement --- docs/manuals/uxp/howtos/secrets-proxy.md | 86 +++++++++++++++++++++--- 1 file changed, 76 insertions(+), 10 deletions(-) diff --git a/docs/manuals/uxp/howtos/secrets-proxy.md b/docs/manuals/uxp/howtos/secrets-proxy.md index dc85289f..9e8332d9 100644 --- a/docs/manuals/uxp/howtos/secrets-proxy.md +++ b/docs/manuals/uxp/howtos/secrets-proxy.md @@ -18,16 +18,36 @@ writes them to Vault. Before you begin, ensure you have: -* A running UXP instance with the Secrets Proxy feature enabled -* A running HashiCorp Vault instance -* [kubectl](https://kubernetes.io/docs/tasks/tools/) configured to point to your UXP cluster -* The [Vault CLI](https://developer.hashicorp.com/vault/docs/install) installed with `VAULT_ADDR` and `VAULT_TOKEN` set -* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) configured with credentials that can manage IAM resources - -:::note -To enable the Secrets Proxy on an existing UXP installation, upgrade with -`--set upbound.secretsProxy.enabled=true`. -::: +* [Kind](https://kind.sigs.k8s.io/docs/user/quick-start/#installation) installed +* [kubectl](https://kubernetes.io/docs/tasks/tools/) installed +* [Helm](https://helm.sh/docs/intro/install/) installed +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed +* The [Vault CLI](https://developer.hashicorp.com/vault/docs/install) installed +* The `up` CLI installed +* Docker running +* AWS credentials with access to the Upbound ECR registry +* A running HashiCorp Vault instance (or use the bootstrap section below) + +## Configure ECR access + +Set environment variables for pulling UXP images from the Upbound ECR registry: + +```shell +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_DEFAULT_REGION=us-west-1 + +ECR_REGISTRY="609897127049.dkr.ecr.us-west-1.amazonaws.com" +ECR_PASSWORD=$(aws ecr get-login-password --region us-west-1) +VERSION=2.2.0-up.1.rc.0.80.ga9163a05 +``` + +## Create a Kind cluster + +```shell +kind create cluster --name crossplane-secrets +kind export kubeconfig --name crossplane-secrets +``` ## Bootstrap a test Vault instance @@ -70,6 +90,52 @@ Dev mode Vault is in-memory only. All data is lost when the pod restarts. ::: +## Create an ECR pull secret + +```shell +kubectl create ns crossplane-system + +kubectl -n crossplane-system create secret docker-registry ecr-pull-secret \ + --docker-server="${ECR_REGISTRY}" \ + --docker-username=AWS \ + --docker-password="${ECR_PASSWORD}" +``` + +## Install UXP with Secrets Proxy + +The `--set upbound.secretsProxy.enabled=true` flag enables the Secrets Proxy +component. To enable it on an existing UXP installation, run `helm upgrade` +with the same flag against your existing release. + +```shell +helm upgrade --install crossplane \ + oci://${ECR_REGISTRY}/upbound/crossplane \ + --version "${VERSION#v}" \ + --namespace crossplane-system \ + --create-namespace \ + --set upbound.manager.image.repository="${ECR_REGISTRY}/upbound/controller-manager" \ + --set upbound.manager.image.tag="v${VERSION}" \ + --set upbound.manager.metering.image.repository="${ECR_REGISTRY}/upbound/controller-manager" \ + --set upbound.manager.metering.image.tag="v${VERSION}" \ + --set 'upbound.manager.imagePullSecrets[0].name=ecr-pull-secret' \ + --set 'upbound.manager.metering.imagePullSecrets[0].name=ecr-pull-secret' \ + --set 'imagePullSecrets[0]=ecr-pull-secret' \ + --set upbound.secretsProxy.enabled=true +``` + +Wait for all pods to be ready: + +```shell +kubectl get pods -n crossplane-system -w +``` + +## Apply a development license + +```shell +up uxp license apply --dev +up uxp license show +``` + ## Configure Vault Store the AWS credentials that Crossplane providers use to manage resources, From 75c8a1968bcf193f8bdd611e0529f90ba7bb67a2 Mon Sep 17 00:00:00 2001 From: Rae Sharp Date: Thu, 12 Mar 2026 12:41:39 -0400 Subject: [PATCH 4/4] update auth --- docs/manuals/uxp/howtos/secrets-proxy.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/manuals/uxp/howtos/secrets-proxy.md b/docs/manuals/uxp/howtos/secrets-proxy.md index 9e8332d9..12846a36 100644 --- a/docs/manuals/uxp/howtos/secrets-proxy.md +++ b/docs/manuals/uxp/howtos/secrets-proxy.md @@ -199,11 +199,11 @@ Vault and read secrets on behalf of provider pods. --output='jsonpath={.clusters[].cluster.server}') ``` -4. Enable the Kubernetes auth method in Vault and configure it with the cluster +4. Enable the JWT auth method in Vault and configure it with the cluster details: ```shell - vault auth enable kubernetes + vault auth enable jwt vault write auth/kubernetes/config \ kubernetes_host="${KUBE_HOST}" \