Skip to content

Commit

Permalink
Merge pull request #81 from vshn/fix/vshnpostgresql_connection_secrets
Browse files Browse the repository at this point in the history
Fix connection secrets for VshnPostgreSQL
  • Loading branch information
TheBigLee authored Jan 27, 2023
2 parents ed17d35 + 8aaecce commit 94fe509
Show file tree
Hide file tree
Showing 6 changed files with 260 additions and 54 deletions.
35 changes: 14 additions & 21 deletions component/vshn_postgres.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,7 @@ local composition =
spec+: {
forProvider+: {
manifest+: {
metadata: {
name: '',
namespace: '',
},
metadata: {},
spec: {
cpu: '',
memory: '',
Expand Down Expand Up @@ -101,10 +98,7 @@ local composition =
spec+: {
forProvider+: {
manifest+: {
metadata: {
name: '',
namespace: '',
},
metadata: {},
spec: {
postgresVersion: '',
'postgresql.conf': {},
Expand All @@ -119,10 +113,7 @@ local composition =
spec+: {
forProvider+: {
manifest+: {
metadata: {
name: '',
namespace: '',
},
metadata: {},
spec: {
instances: 1,
sgInstanceProfile: '',
Expand All @@ -148,10 +139,7 @@ local composition =
spec+: {
forProvider+: {
manifest+: {
metadata: {
name: '',
namespace: '',
},
metadata: {},
stringData: {
POSTGRESQL_USER: defaultUser,
POSTGRESQL_PORT: defaultPort,
Expand All @@ -172,6 +160,11 @@ local composition =
toFieldPath: 'data.POSTGRESQL_PASSWORD',
},
],
// Make crossplane aware of the connection secret we are creating in this object
writeConnectionSecretToRef: {
name: '',
namespace: '',
},
},
};

Expand All @@ -180,10 +173,7 @@ local composition =
spec+: {
forProvider+: {
manifest+: {
metadata: {
name: '',
namespace: '',
},
metadata: {},
spec: {
policyTypes: [
'Ingress',
Expand Down Expand Up @@ -283,16 +273,19 @@ local composition =
},
{
base: secret,
connectionDetails: comp.conn.AllFromSecretKeys(connectionSecretKeys),
patches: [
comp.PatchSetRef('annotations'),
comp.PatchSetRef('labels'),
comp.FromCompositeFieldPathWithTransformSuffix('metadata.labels[crossplane.io/composite]', 'metadata.name', 'connection'),
comp.FromCompositeFieldPath('metadata.labels[crossplane.io/claim-namespace]', 'spec.forProvider.manifest.metadata.namespace'),
comp.FromCompositeFieldPathWithTransformPrefix('metadata.labels[crossplane.io/composite]', 'spec.forProvider.manifest.metadata.namespace', 'vshn-postgresql'),
comp.FromCompositeFieldPathWithTransformSuffix('metadata.labels[crossplane.io/claim-name]', 'spec.forProvider.manifest.metadata.name', 'connection'),

comp.CombineCompositeFromTwoFieldPaths('metadata.labels[crossplane.io/composite]', 'metadata.labels[crossplane.io/composite]', 'spec.forProvider.manifest.stringData.POSTGRESQL_HOST', '%s.vshn-postgresql-%s.svc.cluster.local'),
comp.FromCompositeFieldPathWithTransformPrefix('metadata.labels[crossplane.io/composite]', 'spec.references[0].patchesFrom.namespace', 'vshn-postgresql'),
comp.FromCompositeFieldPath('metadata.labels[crossplane.io/composite]', 'spec.references[0].patchesFrom.name'),
comp.FromCompositeFieldPathWithTransformPrefix('metadata.labels[crossplane.io/composite]', 'spec.writeConnectionSecretToRef.namespace', 'vshn-postgresql'),
comp.FromCompositeFieldPathWithTransformSuffix('metadata.labels[crossplane.io/claim-name]', 'spec.writeConnectionSecretToRef.name', 'connection'),
],
},
] + if pgParams.enableNetworkPolicy == true then [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
= Manage secrets not coming from a provider

An operator or a helm chart can't create a crossplane connection secret. Therefore we need to create one.

To be able to manage the connection secrets via crossplane, we create a helper secret object via provider-kubernetes.
The helper secret object is populated via composition and is made aware to crossplane via `writeConnectionSecretToRef`.
Doing this we simulate the behaviour of connection secrets from a crossplane provider.

Once we've got a crossplane managed secret, we can use `writeConnectionSecretToRef` in the claim to reference the final secret (which is automatically created by crossplane)

Visualizing this process it looks like the following:

image::crossplane-secrets.png[]

== Example

Let's assume we've got an operator or a helm chart that creates a secret called `<composite_name>` in the namespace `vshn-<composite_name>` that contains a password in the key `superuser-password`.

We will then create a secret object in our composition like shown in the snippet below.

This will result in a regular temporary secret (our helper secret) which will also be placed in the `vshn-<composite_name>` namespace. The key `MY_PASSWORD` is patched in from the secret created by the operator or helm chart. (1)

The name and namespace of that secret is also patched in accordingly. (2)

By using the `writeConnectionSecretToRef` key, we make crossplane aware of the connection secret. (3)

We again patch the name and namespace of that helper secret object accordingly. (4)

Finally we list the propagation secret keys from that object to the composition instance connection secret. (5)


[source,yaml]
----
- base:
apiVersion: kubernetes.crossplane.io/v1alpha1
kind: Object
metadata: {}
spec:
forProvider:
manifest:
apiVersion: v1
kind: Secret
metadata: {}
stringData:
MY_APP_PORT: 1234
providerConfigRef:
name: kubernetes
references:
- patchesFrom: # (1)
apiVersion: v1 # (1)
fieldPath: data.superuser-password # (1)
kind: Secret # (1)
name: "" # (1)
namespace: "" # (1)
toFieldPath: data.MY_PASSWORD # (1)
writeConnectionSecretToRef: # (3)
name: "" # (3)
namespace: "" # (3)
connectionDetails:
- fromConnectionSecretKey: MY_APP_PORT # (5)
name: MY_APP_PORT # (5)
type: FromConnectionSecretKey # (5)
- fromConnectionSecretKey: MY_PASSWORD # (5)
name: MY_PASSWORD # (5)
type: FromConnectionSecretKey # (5)
patches:
- patchSetName: annotations
type: PatchSet
- patchSetName: labels
type: PatchSet
- fromFieldPath: metadata.labels[crossplane.io/composite] # (4)
toFieldPath: metadata.name # (4)
transforms: # (4)
- string: # (4)
fmt: '%s-connection' # (4)
type: Format # (4)
type: string # (4)
type: FromCompositeFieldPath # (4)
- fromFieldPath: metadata.labels[crossplane.io/composite] # (4)
toFieldPath: spec.forProvider.manifest.metadata.namespace # (4)
transforms: # (4)
- string: # (4)
fmt: vshn-%s # (4)
type: Format # (4)
type: string # (4)
type: FromCompositeFieldPath # (4)
- fromFieldPath: metadata.labels[crossplane.io/claim-name] # (4)
toFieldPath: spec.forProvider.manifest.metadata.name # (4)
transforms: # (4)
- string: # (4)
fmt: '%s-connection' # (4)
type: Format # (4)
type: string # (4)
type: FromCompositeFieldPath # (4)
- fromFieldPath: metadata.labels[crossplane.io/composite] # (2)
toFieldPath: spec.references[0].patchesFrom.namespace # (2)
transforms: # (2)
- string: # (2)
fmt: vshn-%s # (2)
type: Format # (2)
type: string # (2)
type: FromCompositeFieldPath # (2)
- fromFieldPath: metadata.labels[crossplane.io/composite] # (2)
toFieldPath: spec.references[0].patchesFrom.name # (2)
type: FromCompositeFieldPath # (2)
- fromFieldPath: metadata.labels[crossplane.io/composite] # (4)
toFieldPath: spec.writeConnectionSecretToRef.namespace # (4)
transforms: # (4)
- string: # (4)
fmt: vshn-%s # (4)
type: Format # (4)
type: string # (4)
type: FromCompositeFieldPath # (4)
- fromFieldPath: metadata.labels[crossplane.io/claim-name] # (4)
toFieldPath: spec.writeConnectionSecretToRef.name # (4)
transforms: # (4)
- string: # (4)
fmt: '%s-connection' # (4)
type: Format # (4)
type: string # (4)
type: FromCompositeFieldPath # (4)
----


Assuming we've got a claim called `my-great-app` with the following content:

[source,yaml]
---
[...]
metadata:
name: my-app
namespace: my-app-namespace
spec:
writeConnectionSecretToRef:
name: my_connection_secret
---

The operator or helm chart will thus create a secret like this:

[source,yaml]
---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: my-app-c48ad
namespace: vshn-my-app-c48ad
data:
MY_PASSWORD: Rm9vYmFy
---

The composition will create the helper secret like this:
[source,yaml]
---
apiVersion: v1
kind: Secret
type: connection.crossplane.io/v1alpha1
metadata:
name: my-app-c48ad-connection
namespace: vshn-my-app-c48ad
ownerReferences:
- apiVersion: kubernetes.crossplane.io/v1alpha1
controller: yes
kind: Object
name: my-app-c48ad-connection
uid: f22e3083-6a89-4286-ba6d-f990ee91ccad
data:
MY_PASSWORD: Rm9vYmFy
MY_APP_PORT: 1234
---

Finally the claim secret will be populated like the following:
[source,yaml]
---
apiVersion: v1
kind: Secret
type: connection.crossplane.io/v1alpha1
metadata:
name: my_connection_secret
namespace: my-app-namespace
data:
MY_PASSWORD: Rm9vYmFy
MY_APP_PORT: 1234
---
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/modules/ROOT/partials/nav.adoc
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
* xref:index.adoc[Home]
.Explanations
* xref:explanations/crossplane-secrets-non-provider.adoc[Manage secrets not coming from a provider]
.Tutorials
* xref:tutorials/install-cloudscale.adoc[Install Cloudscale Stack]
* xref:tutorials/install-exoscale.adoc[Install Exoscale Stack]
Expand Down
4 changes: 1 addition & 3 deletions lib/appcat-compositions.libsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,7 @@ local connFromFieldPath(name, field) = {
local kubeObject(apiVersion, kind) = {
apiVersion: 'kubernetes.crossplane.io/v1alpha1',
kind: 'Object',
metadata: {
name: '',
},
metadata: {},
spec: {
providerConfigRef: {
name: 'kubernetes',
Expand Down
Loading

0 comments on commit 94fe509

Please sign in to comment.