Secrets Management

Upbound has built-in features to help you manage secrets for control planes running in a Space. Upbound offers:

  1. New SharedSecretStore and SharedExternalSecret resources to manage syncing external secrets into groups of control planes.
  2. Built-in support for External Secrets Operator (ESO) APIs. This allows you to synchronize secrets from external secret stores into a managed control plane.

Shared secrets in a Space

Spaces administrators can manage external secrets for multiple control planes with two new API types. SharedSecretStore and SharedExternalSecret allow admins to provision group-scoped secret stores and external secrets into their control planes.

Shared Secret Stores

SharedSecretStore is group-scoped and created in the namespace of a Space containing one or more ControlPlane instances. It provisions ClusterSecretStore resources into control planes with a group:

  • If the provided selector matches, all matching control planes in the group receive the corresponding ClusterSecretStore.
  • If the provided selector doesn’t match, the non-matched control planes in the namespace remove the corresponding ClusterSecretStore
  • You can use the ClusterSecretStore within a control plane context: ExternalSecret and ClusterExternalSecret can access the store as documented in the ESO documentation.

SharedSecretStore API type is an extension of ClusterSecretStore. Read the API reference docs for more information.

Shared External Secrets

SharedExternalSecret is group-scoped and created in the namespace of a Space containing one or more ControlPlane instances. It enables provisioning of ClusterExternalSecret resources into control planes within the group boundary:

  • If the provided selector matches, all matching control planes in the group receive the corresponding ClusterExternalSecret.
  • If the provided selector doesn’t match, the non-matched control planes in the namespace remove the corresponding ClusterExternalSecret
  • You can use the ClusterExternalSecret within a control plane context: ClusterSecretStore can access the secret as documented in the ESO documentation.

SharedExternalSecret API type is an extension of ClusterExternalSecret. Read the API reference docs for more information.

Usage

Create two managed control planes in acmeorg namespace.

cat <<EOF | kubectl apply -n acmeorg -f -
apiVersion: spaces.upbound.io/v1beta1
kind: ControlPlane
metadata:
  labels:
    # example label, to be matched in SharedSecretStore/SharedExternalSecret examples
    org: foo
  name: ctp
spec:
  writeConnectionSecretToRef:
    name: kubeconfig-ctp2
cat <<EOF | kubectl apply -n acmeorg -f -
apiVersion: spaces.upbound.io/v1beta1
kind: ControlPlane
metadata:
  labels:
    # example label, to be matched in SharedSecretStore/SharedExternalSecret examples
    org: foo
  name: ctp2
spec:
  writeConnectionSecretToRef:
    name: kubeconfig-ctp

Deploy SharedSecretStore in the same namespace. This example uses fake provider.

cat <<EOF | kubectl apply -n acmeorg -f -
apiVersion: spaces.upbound.io/v1alpha1
kind: SharedSecretStore
metadata:
  name: fake
spec:
  controlPlaneSelector:
    labelSelectors:
      - matchLabels:
          org: foo
  namespaceSelector:
    names:
      - default
  provider:
    fake:
      data:
        - key: "/foo/bar"
          value: "HELLO1"
          version: "v1"
        - key: "/foo/bar"
          value: "HELLO2"
          version: "v2"
        - key: "/foo/baz"
          value: '{"john": "doe"}'
          version: "v1"

Deploy SharedExternalSecret in the same namespace.

cat <<EOF | kubectl apply -n acmeorg -f -
apiVersion: spaces.upbound.io/v1alpha1
kind: SharedExternalSecret
metadata:
  name: fake-secret
spec:
  controlPlaneSelector:
    labelSelectors:
      - matchLabels:
          org: foo
  namespaceSelector:
    names:
      - default
  externalSecretSpec:
    refreshInterval: 1h
    secretStoreRef:
      # refer the projected store
      name: fake
      kind: ClusterSecretStore
    data:
      - secretKey: "foo"
        remoteRef:
          key: "/foo/bar"
          version: "v1"

Check if control planes are available:

$ kubectl get controlplanes
NAME   CROSSPLANE VERSION   SUPPORTED   READY   MESSAGE   AGE
ctp    1.13.2-up.3          True        True              21m
ctp2   1.13.2-up.3          True        True              22m

Connect to control plane ctp:

up ctp connect ctp

Check if Kubernetes secret fake-secret is available in default namespace:

$ kubectl get secret fake-secret
NAME          TYPE     DATA   AGE
fake-secret   Opaque   1      20s

Perform the same check on control plane ctp2.

Verify the projected ClusterSecretStore and ClusterExternalSecret.

$ kubectl get clustersecretstore
NAME   AGE     STATUS   CAPABILITIES   READY
fake   5m18s   Valid    ReadOnly       True

$ kubectl get clusterexternalsecret
NAME          STORE   REFRESH INTERVAL   READY
fake-secret   fake                       True

External secrets in a control plane

You can use ESO API types in a Spaces-managed control plane as you would in a standalone Crossplane instance or Kubernetes cluster. Below is an example of the AWS Secrets Manager configuration.

Usage

First, create a secret in the managed control plane which contains the auth credentials to access the external secret store.

kubectl create secret generic awssm-secret \
  --from-file=./access-key \
  --from-file=./secret-access-key

Create a SecretStore resource in your managed control plane, referencing the auth secret created in the previous step.

cat <<EOF | kubectl apply -f -
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secretsmanager
  namespace: default
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        secretRef:
          accessKeyIDSecretRef:
            name: awssm-secret
            key: access-key
          secretAccessKeySecretRef:
            name: awssm-secret
            key: secret-access-key
EOF

Once you have a secret store configured, you can pull external secrets into your control plane by creating new ExternalSecrets. As an example, you can store ProviderConfig credentials in a central secret management service and pull them into your managed control plane.

cat <<EOF | kubectl apply -f -
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: providerconfig-aws-secret
  namespace: default
spec:
  refreshInterval: 15s
  secretStoreRef:
    name: aws-secretsmanager
    kind: SecretStore
  target:
    creationPolicy: Owner
  data:
  - secretKey: aws_access_key_id
    remoteRef:
      key: providerconfigs
      property: aws_access_key_id
  - secretKey: aws_secret_access_key
    remoteRef:
      key: providerconfigs
      property: aws_secret_access_key
EOF

For a full guide on using ESO API types and how to connect it to various external secret stores, read the ESO documentation.