Let's say you are setting up an application which also requires PostgreSQL database. Database cluster is create using CloudNativePG operator which automatically creates a secret for the application with connection string:
$ kubectl get secret -oyaml -n partdb partdb-cluster-app | yq .data.uri | base64 -d
postgresql://partdb:...@partdb-cluster-rw.partdb:5432/partdb
And then you discover that application actually expects some extra parameters in the end of connection string. Of course, you could take this secret, create a modified copy of it and manually add it to the cluster, but no.. this is not a gitops way of working. Besides CNPG also supports credential rotation, which might cause fun to debug issues.
Our automated gitops solution relies on using External Secrets operator.
Firstly, lets gather and map our requirements:
SecretStore using kubernetes provider targeting single namespace (in our case it's partdb)ServiceAccount with proper RBAC permissions (Role + RoleBinding) to retrieve the existing secret from this namespace.So lets start by setting up the service account with proper RBAC:
Role with permissions to read Secret and create SelfSubjectRulesReview resources:
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: eso-store-role namespace: partdb rules: - apiGroups: [""] resources: - secrets verbs: - get - list - watch - apiGroups: - authorization.k8s.io resources: - selfsubjectrulesreviews verbs: - create
ServiceAccount:
apiVersion: v1 kind: ServiceAccount metadata: name: eso-user namespace: partdb
And RoleBinding assigning Role to ServiceAccount:
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: eso-user namespace: partdb roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: eso-store-role subjects: - kind: ServiceAccount name: eso-user namespace: partdb
At this point we have created enough "plumbing" to roll out SecretStore with kubernetes provider targeting our own cluster:
apiVersion: external-secrets.io/v1beta1 kind: SecretStore metadata: name: secrets namespace: partdb spec: provider: kubernetes: auth: serviceAccount: name: eso-user remoteNamespace: partdb server: caProvider: key: ca.crt name: kube-root-ca.crt type: ConfigMap url: kubernetes.default
And finally we can create the ExternalSecret which utilizes template to rewrite the values:
apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: partdb-fix-secret namespace: partdb spec: refreshInterval: 24h secretStoreRef: kind: SecretStore name: secrets target: name: partdb-cluster-app-fixed creationPolicy: Owner template: engineVersion: v2 data: uri: "{{ .uri }}/?serverVersion=16.4" data: - secretKey: uri remoteRef: key: partdb-cluster-app property: uri
Let's check it out:
$ kubectl get secret -oyaml -n partdb partdb-cluster-app-fixed | yq .data.uri | base64 -d
postgresql://partdb:...@partdb-cluster-rw.partdb:5432/partdb/?serverVersion=16.4