Kubernetes secrets are objects that contain some sensitive data. For Crossplane, that could be:
- credentials for a provider
- connection details to a resource
- inputs to managed resources at creation time
The baseline recommendation is to just use Kubernetes secrets. If you have business requirements to integrate Crossplane with a centralized secret management solution, the framework also documents below how to do that.
Reading secrets
To read secrets from an external source into your Crossplane cluster, it’s recommended to install the External Secrets Operator into your cluster. The External Secrets Operator is an open source tool that implements a custom resource called ExternalSecret
that defines where secrets live.
To use the external secrets operator, you need to register a SecretStore. This could be AWS Key Secrets Manager, Vault, or other central secret management services.
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
namespace: default
spec:
provider:
vault:
server: your-vault-server-address
path: secret
namespace: admin
version: v2
auth:
# points to a secret that contains a vault token
# https://www.vaultproject.io/docs/auth/token
tokenSecretRef:
name: vault-secret
key: vault-token
Once you have a secret store configured, you can pull external secrets into your control plane by creating new ExternalSecrets
. As an example, this allows you to store ProviderConfig credentials in a central secret management service and pull them into different control planes.
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: providerconfig-gcp-secret
namespace: default
spec:
refreshInterval: 15s
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
creationPolicy: Owner
data:
- secretKey: creds
remoteRef:
key: providerconfigs
property: providerconf-gcp
Writing secrets
When Crossplane creates a secret, the default mode is to write the secret as a Kubernetes secret in its cluster. One common example of this is when you create a resource that generates connection details. The connection details get written to a local Kubernetes secret in the cluster.
If you need to write secrets to a centralized secret management solution outside of the cluster, remember that a secret is “just another Crossplane resource.” Create secrets in the desired secret store by composing the set of managed resources which generate the secret. Then, pull data from the Kubernetes secret that Crossplane creates.
The example below demonstrates a composition that stores the connection details for a GCP CloudSQL database into a secret in GCP Secret Manager.
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: xsuperdatabases.test.org
spec:
writeConnectionSecretsToNamespace: upbound-system
compositeTypeRef:
apiVersion: test.org/v1alpha1
kind: XSuperDatabase
resources:
- name: the-db
base:
apiVersion: sql.gcp.upbound.io/v1beta1
kind: DatabaseInstance
metadata:
name: my-database
spec:
forProvider:
databaseVersion: MYSQL_5_7
deletionProtection: false
region: us-central1
settings:
- diskSize: 20
tier: db-f1-micro
writeConnectionSecretToRef:
name: my-db-connection
namespace: default
patches:
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.name
toFieldPath: metadata.name
- name: the-secret
base:
apiVersion: secretmanager.gcp.upbound.io/v1beta1
kind: Secret
metadata:
name: my-db-secret
spec:
forProvider:
labels:
environment: dev
replication:
- automatic: true
patches:
- type: CombineFromComposite
combine:
variables:
- fromFieldPath: spec.parameters.name
strategy: string
string:
fmt: "%s-secret"
toFieldPath: metadata.name
policy:
fromFieldPath: Required
- name: the-secret-data
base:
apiVersion: secretmanager.gcp.upbound.io/v1beta1
kind: SecretVersion
metadata:
name: my-db-secret-version
spec:
forProvider:
secretDataSecretRef:
key: publicIP
name: my-db-connection
namespace: default
secretSelector:
matchControllerRef: true
When a new composite resource gets created which uses this composition, a kind: DatabaseInstance
managed resource gets created. it stores its connection details in a local secret in the cluster. A kind: Secret
resource gets created, representing a new secret in GCP Secret Manager. Then a kind: SecretVersion
resource gets created, which transposes the data from a local Kubernetes secret to the secret contained in GCP.
This example is for GCP, but you would follow a similar pattern for other popular secret stores (AWS, Azure, Vault, etc), too.