From 0f2afad62eb292dcbf1332b0b17a72bfdfeb8a17 Mon Sep 17 00:00:00 2001 From: Zoran Regvart Date: Tue, 27 May 2025 11:00:24 +0200 Subject: [PATCH 1/4] feat(postgresql): add TLS related options This adds `sslCert`, `sslKey` and `sslRootCert` options to the PostgreSQL `ProviderConfig` needed to establish a connection to a database that either uses non-public certificates or requires client certificate authentication. I've opted to create the `Options` struct to hold these and the existing `sslMode` parameter, this will allow adding other options[1] easier in the future. [1] https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS Signed-off-by: Zoran Regvart --- apis/postgresql/v1alpha1/provider_types.go | 20 +++++++ .../v1alpha1/zz_generated.deepcopy.go | 15 +++++ ...sql.sql.crossplane.io_providerconfigs.yaml | 10 ++++ pkg/clients/postgresql/postgresql.go | 57 +++++++++++++++---- pkg/clients/postgresql/postgresql_test.go | 38 ++++++++++++- .../postgresql/database/reconciler.go | 5 +- .../postgresql/database/reconciler_test.go | 3 +- .../postgresql/extension/reconciler.go | 9 +-- .../postgresql/extension/reconciler_test.go | 3 +- pkg/controller/postgresql/grant/reconciler.go | 5 +- .../postgresql/grant/reconciler_test.go | 3 +- pkg/controller/postgresql/role/reconciler.go | 5 +- .../postgresql/role/reconciler_test.go | 3 +- .../postgresql/schema/reconciler.go | 5 +- .../postgresql/schema/reconciler_test.go | 3 +- 15 files changed, 151 insertions(+), 33 deletions(-) diff --git a/apis/postgresql/v1alpha1/provider_types.go b/apis/postgresql/v1alpha1/provider_types.go index 1a0b3eba..2936b72b 100644 --- a/apis/postgresql/v1alpha1/provider_types.go +++ b/apis/postgresql/v1alpha1/provider_types.go @@ -19,6 +19,8 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/crossplane-contrib/provider-sql/pkg/clients" + "github.com/crossplane-contrib/provider-sql/pkg/clients/postgresql" xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" ) @@ -36,6 +38,24 @@ type ProviderConfigSpec struct { // +kubebuilder:default=verify-full // +kubebuilder:validation:Optional SSLMode *string `json:"sslMode,omitempty"` + // Path to the certificate used for client authentication + // +kubebuilder:validation:Optional + SSLCert *string `json:"sslCert,omitempty"` + // Path to the key used for client authentication + // +kubebuilder:validation:Optional + SSLKey *string `json:"sslKey,omitempty"` + // Path to the CA certificate(s) used for verifying the server certificate + // +kubebuilder:validation:Optional + SSLRootCert *string `json:"sslRootCert,omitempty"` +} + +func (s ProviderConfigSpec) Options() postgresql.Options { + return postgresql.Options{ + SSLMode: clients.ToString(s.SSLMode), + SSLCert: clients.ToString(s.SSLCert), + SSLKey: clients.ToString(s.SSLKey), + SSLRootCert: clients.ToString(s.SSLRootCert), + } } const ( diff --git a/apis/postgresql/v1alpha1/zz_generated.deepcopy.go b/apis/postgresql/v1alpha1/zz_generated.deepcopy.go index 70d15187..bc2fcbaf 100644 --- a/apis/postgresql/v1alpha1/zz_generated.deepcopy.go +++ b/apis/postgresql/v1alpha1/zz_generated.deepcopy.go @@ -563,6 +563,21 @@ func (in *ProviderConfigSpec) DeepCopyInto(out *ProviderConfigSpec) { *out = new(string) **out = **in } + if in.SSLCert != nil { + in, out := &in.SSLCert, &out.SSLCert + *out = new(string) + **out = **in + } + if in.SSLKey != nil { + in, out := &in.SSLKey, &out.SSLKey + *out = new(string) + **out = **in + } + if in.SSLRootCert != nil { + in, out := &in.SSLRootCert, &out.SSLRootCert + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderConfigSpec. diff --git a/package/crds/postgresql.sql.crossplane.io_providerconfigs.yaml b/package/crds/postgresql.sql.crossplane.io_providerconfigs.yaml index ea431526..28deb2ea 100644 --- a/package/crds/postgresql.sql.crossplane.io_providerconfigs.yaml +++ b/package/crds/postgresql.sql.crossplane.io_providerconfigs.yaml @@ -84,6 +84,12 @@ spec: Defines the database name used to set up a connection to the provided PostgreSQL instance. Same as PGDATABASE environment variable. type: string + sslCert: + description: Path to the certificate used for client authentication + type: string + sslKey: + description: Path to the key used for client authentication + type: string sslMode: default: verify-full description: |- @@ -95,6 +101,10 @@ spec: - verify-ca - verify-full type: string + sslRootCert: + description: Path to the CA certificate(s) used for verifying the + server certificate + type: string required: - credentials type: object diff --git a/pkg/clients/postgresql/postgresql.go b/pkg/clients/postgresql/postgresql.go index 270bb92c..5a2419a9 100644 --- a/pkg/clients/postgresql/postgresql.go +++ b/pkg/clients/postgresql/postgresql.go @@ -23,31 +23,62 @@ type postgresDB struct { dsn string endpoint string port string - sslmode string + options Options +} + +type Options struct { + SSLMode string + SSLCert string + SSLKey string + SSLRootCert string +} + +func (o Options) queryString() string { + values := url.Values{} + + if o.SSLMode != "" { + values.Add("sslmode", o.SSLMode) + } + + if o.SSLCert != "" { + values.Add("sslcert", o.SSLCert) + } + + if o.SSLKey != "" { + values.Add("sslkey", o.SSLKey) + } + + if o.SSLRootCert != "" { + values.Add("sslrootcert", o.SSLRootCert) + } + + return values.Encode() } // New returns a new PostgreSQL database client. The default database name is // an empty string. The underlying pq library will default to either using the // value of PGDATABASE, or if unset, the hardcoded string 'postgres'. -// The sslmode defines the mode used to set up the connection for the provider. -func New(creds map[string][]byte, database, sslmode string) xsql.DB { +// The options provide additional settings to set up the connection for the +// provider. +func New(creds map[string][]byte, database string, options Options) xsql.DB { // TODO(negz): Support alternative connection secret formats? endpoint := string(creds[xpv1.ResourceCredentialsSecretEndpointKey]) port := string(creds[xpv1.ResourceCredentialsSecretPortKey]) username := string(creds[xpv1.ResourceCredentialsSecretUserKey]) password := string(creds[xpv1.ResourceCredentialsSecretPasswordKey]) - dsn := DSN(username, password, endpoint, port, database, sslmode) + + dsn := DSN(username, password, endpoint, port, database, options.queryString()) return postgresDB{ dsn: dsn, endpoint: endpoint, port: port, - sslmode: sslmode, + options: options, } } // DSN returns the DSN URL -func DSN(username, password, endpoint, port, database, sslmode string) string { +func DSN(username, password, endpoint, port, database, options string) string { // Use net/url UserPassword to encode the username and password // This will ensure that any special characters in the username or password // are percent-encoded for use in the user info portion of the DSN URL @@ -57,7 +88,8 @@ func DSN(username, password, endpoint, port, database, sslmode string) string { endpoint + ":" + port + "/" + database + - "?sslmode=" + sslmode + "?" + options + } // ExecTx executes an array of queries, committing if all are successful and @@ -130,10 +162,13 @@ func (c postgresDB) Scan(ctx context.Context, q xsql.Query, dest ...interface{}) // GetConnectionDetails returns the connection details for a user of this DB func (c postgresDB) GetConnectionDetails(username, password string) managed.ConnectionDetails { return managed.ConnectionDetails{ - xpv1.ResourceCredentialsSecretUserKey: []byte(username), - xpv1.ResourceCredentialsSecretPasswordKey: []byte(password), - xpv1.ResourceCredentialsSecretEndpointKey: []byte(c.endpoint), - xpv1.ResourceCredentialsSecretPortKey: []byte(c.port), + xpv1.ResourceCredentialsSecretUserKey: []byte(username), + xpv1.ResourceCredentialsSecretPasswordKey: []byte(password), + xpv1.ResourceCredentialsSecretEndpointKey: []byte(c.endpoint), + xpv1.ResourceCredentialsSecretPortKey: []byte(c.port), + xpv1.ResourceCredentialsSecretClientCertKey: []byte(c.options.SSLCert), + xpv1.ResourceCredentialsSecretClientKeyKey: []byte(c.options.SSLKey), + xpv1.ResourceCredentialsSecretCAKey: []byte(c.options.SSLRootCert), } } diff --git a/pkg/clients/postgresql/postgresql_test.go b/pkg/clients/postgresql/postgresql_test.go index a2056b91..d6cc6059 100644 --- a/pkg/clients/postgresql/postgresql_test.go +++ b/pkg/clients/postgresql/postgresql_test.go @@ -12,8 +12,44 @@ func TestDSNURLEscaping(t *testing.T) { rawPass := "password^" encPass := "password%5E" sslmode := "require" - dsn := DSN(user, rawPass, endpoint, port, db, sslmode) + options := Options{ + SSLMode: sslmode, + } + dsn := DSN(user, rawPass, endpoint, port, db, options.queryString()) if dsn != "postgres://"+user+":"+encPass+"@"+endpoint+":"+port+"/"+db+"?sslmode="+sslmode { t.Errorf("DSN string did not match expected output with userinfo URL encoded") } } + +func TestOptionsToQueryString(t *testing.T) { + cases := []struct { + name string + given Options + expected string + }{ + { + name: "empty", + given: Options{}, + expected: "", + }, + { + name: "everything", + given: Options{ + SSLMode: "require", + SSLCert: "/path/to/ssl.crt", + SSLKey: "/path/to/ssl.key", + SSLRootCert: "/path/to/ca.crt", + }, + expected: "sslcert=%2Fpath%2Fto%2Fssl.crt&sslkey=%2Fpath%2Fto%2Fssl.key&sslmode=require&sslrootcert=%2Fpath%2Fto%2Fca.crt", + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got := c.given.queryString() + if c.expected != got { + t.Errorf("Expected query string to be %q, but it was %q", c.expected, got) + } + }) + } +} diff --git a/pkg/controller/postgresql/database/reconciler.go b/pkg/controller/postgresql/database/reconciler.go index 9990a5f7..01e46924 100644 --- a/pkg/controller/postgresql/database/reconciler.go +++ b/pkg/controller/postgresql/database/reconciler.go @@ -40,7 +40,6 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane-contrib/provider-sql/apis/postgresql/v1alpha1" - "github.com/crossplane-contrib/provider-sql/pkg/clients" "github.com/crossplane-contrib/provider-sql/pkg/clients/postgresql" "github.com/crossplane-contrib/provider-sql/pkg/clients/xsql" ) @@ -93,7 +92,7 @@ func Setup(mgr ctrl.Manager, o xpcontroller.Options) error { type connector struct { kube client.Client usage resource.Tracker - newDB func(creds map[string][]byte, database string, sslmode string) xsql.DB + newDB func(creds map[string][]byte, database string, options postgresql.Options) xsql.DB } func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) { @@ -126,7 +125,7 @@ func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.E return nil, errors.Wrap(err, errGetSecret) } - return &external{db: c.newDB(s.Data, pc.Spec.DefaultDatabase, clients.ToString(pc.Spec.SSLMode))}, nil + return &external{db: c.newDB(s.Data, pc.Spec.DefaultDatabase, pc.Spec.Options())}, nil } type external struct{ db xsql.DB } diff --git a/pkg/controller/postgresql/database/reconciler_test.go b/pkg/controller/postgresql/database/reconciler_test.go index 98287ceb..723c6d08 100644 --- a/pkg/controller/postgresql/database/reconciler_test.go +++ b/pkg/controller/postgresql/database/reconciler_test.go @@ -32,6 +32,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/crossplane-runtime/pkg/test" + "github.com/crossplane-contrib/provider-sql/pkg/clients/postgresql" "github.com/crossplane-contrib/provider-sql/pkg/clients/xsql" ) @@ -64,7 +65,7 @@ func TestConnect(t *testing.T) { type fields struct { kube client.Client usage resource.Tracker - newDB func(creds map[string][]byte, database string, sslmode string) xsql.DB + newDB func(creds map[string][]byte, database string, options postgresql.Options) xsql.DB } type args struct { diff --git a/pkg/controller/postgresql/extension/reconciler.go b/pkg/controller/postgresql/extension/reconciler.go index ac2ae11d..f917232f 100644 --- a/pkg/controller/postgresql/extension/reconciler.go +++ b/pkg/controller/postgresql/extension/reconciler.go @@ -36,7 +36,6 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane-contrib/provider-sql/apis/postgresql/v1alpha1" - "github.com/crossplane-contrib/provider-sql/pkg/clients" "github.com/crossplane-contrib/provider-sql/pkg/clients/postgresql" "github.com/crossplane-contrib/provider-sql/pkg/clients/xsql" ) @@ -85,7 +84,7 @@ func Setup(mgr ctrl.Manager, o xpcontroller.Options) error { type connector struct { kube client.Client usage resource.Tracker - newDB func(creds map[string][]byte, database string, sslmode string) xsql.DB + newDB func(creds map[string][]byte, database string, options postgresql.Options) xsql.DB } func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) { @@ -118,13 +117,15 @@ func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.E return nil, errors.Wrap(err, errGetSecret) } + options := pc.Spec.Options() + // We do not want to create an extension on the default DB // if the user was expecting a database name to be resolved. if cr.Spec.ForProvider.Database != nil { - return &external{db: c.newDB(s.Data, *cr.Spec.ForProvider.Database, clients.ToString(pc.Spec.SSLMode))}, nil + return &external{db: c.newDB(s.Data, *cr.Spec.ForProvider.Database, options)}, nil } - return &external{db: c.newDB(s.Data, pc.Spec.DefaultDatabase, clients.ToString(pc.Spec.SSLMode))}, nil + return &external{db: c.newDB(s.Data, pc.Spec.DefaultDatabase, options)}, nil } type external struct{ db xsql.DB } diff --git a/pkg/controller/postgresql/extension/reconciler_test.go b/pkg/controller/postgresql/extension/reconciler_test.go index d675f016..cef02bdf 100644 --- a/pkg/controller/postgresql/extension/reconciler_test.go +++ b/pkg/controller/postgresql/extension/reconciler_test.go @@ -32,6 +32,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/crossplane-runtime/pkg/test" + "github.com/crossplane-contrib/provider-sql/pkg/clients/postgresql" "github.com/crossplane-contrib/provider-sql/pkg/clients/xsql" ) @@ -64,7 +65,7 @@ func TestConnect(t *testing.T) { type fields struct { kube client.Client usage resource.Tracker - newDB func(creds map[string][]byte, database string, sslmode string) xsql.DB + newDB func(creds map[string][]byte, database string, options postgresql.Options) xsql.DB } type args struct { diff --git a/pkg/controller/postgresql/grant/reconciler.go b/pkg/controller/postgresql/grant/reconciler.go index 7a236c09..ed95b909 100644 --- a/pkg/controller/postgresql/grant/reconciler.go +++ b/pkg/controller/postgresql/grant/reconciler.go @@ -37,7 +37,6 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane-contrib/provider-sql/apis/postgresql/v1alpha1" - "github.com/crossplane-contrib/provider-sql/pkg/clients" "github.com/crossplane-contrib/provider-sql/pkg/clients/postgresql" "github.com/crossplane-contrib/provider-sql/pkg/clients/xsql" ) @@ -94,7 +93,7 @@ func Setup(mgr ctrl.Manager, o xpcontroller.Options) error { type connector struct { kube client.Client usage resource.Tracker - newDB func(creds map[string][]byte, database string, sslmode string) xsql.DB + newDB func(creds map[string][]byte, database string, options postgresql.Options) xsql.DB } func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) { @@ -127,7 +126,7 @@ func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.E return nil, errors.Wrap(err, errGetSecret) } return &external{ - db: c.newDB(s.Data, pc.Spec.DefaultDatabase, clients.ToString(pc.Spec.SSLMode)), + db: c.newDB(s.Data, pc.Spec.DefaultDatabase, pc.Spec.Options()), kube: c.kube, }, nil } diff --git a/pkg/controller/postgresql/grant/reconciler_test.go b/pkg/controller/postgresql/grant/reconciler_test.go index 8ab046db..57816915 100644 --- a/pkg/controller/postgresql/grant/reconciler_test.go +++ b/pkg/controller/postgresql/grant/reconciler_test.go @@ -37,6 +37,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/crossplane-runtime/pkg/test" + "github.com/crossplane-contrib/provider-sql/pkg/clients/postgresql" "github.com/crossplane-contrib/provider-sql/pkg/clients/xsql" ) @@ -74,7 +75,7 @@ func TestConnect(t *testing.T) { type fields struct { kube client.Client usage resource.Tracker - newDB func(creds map[string][]byte, database string, sslmode string) xsql.DB + newDB func(creds map[string][]byte, database string, options postgresql.Options) xsql.DB } type args struct { diff --git a/pkg/controller/postgresql/role/reconciler.go b/pkg/controller/postgresql/role/reconciler.go index 055e4b77..b90a6524 100644 --- a/pkg/controller/postgresql/role/reconciler.go +++ b/pkg/controller/postgresql/role/reconciler.go @@ -42,7 +42,6 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane-contrib/provider-sql/apis/postgresql/v1alpha1" - "github.com/crossplane-contrib/provider-sql/pkg/clients" "github.com/crossplane-contrib/provider-sql/pkg/clients/postgresql" "github.com/crossplane-contrib/provider-sql/pkg/clients/xsql" ) @@ -95,7 +94,7 @@ func Setup(mgr ctrl.Manager, o xpcontroller.Options) error { type connector struct { kube client.Client usage resource.Tracker - newDB func(creds map[string][]byte, database string, sslmode string) xsql.DB + newDB func(creds map[string][]byte, database string, options postgresql.Options) xsql.DB } func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) { @@ -129,7 +128,7 @@ func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.E } return &external{ - db: c.newDB(s.Data, pc.Spec.DefaultDatabase, clients.ToString(pc.Spec.SSLMode)), + db: c.newDB(s.Data, pc.Spec.DefaultDatabase, pc.Spec.Options()), kube: c.kube, }, nil } diff --git a/pkg/controller/postgresql/role/reconciler_test.go b/pkg/controller/postgresql/role/reconciler_test.go index f151a4ee..e65e34a9 100644 --- a/pkg/controller/postgresql/role/reconciler_test.go +++ b/pkg/controller/postgresql/role/reconciler_test.go @@ -38,6 +38,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/crossplane-runtime/pkg/test" + "github.com/crossplane-contrib/provider-sql/pkg/clients/postgresql" "github.com/crossplane-contrib/provider-sql/pkg/clients/xsql" ) @@ -78,7 +79,7 @@ func TestConnect(t *testing.T) { type fields struct { kube client.Client usage resource.Tracker - newDB func(creds map[string][]byte, database string, sslmode string) xsql.DB + newDB func(creds map[string][]byte, database string, options postgresql.Options) xsql.DB } type args struct { diff --git a/pkg/controller/postgresql/schema/reconciler.go b/pkg/controller/postgresql/schema/reconciler.go index d60cbe13..3c5075cf 100644 --- a/pkg/controller/postgresql/schema/reconciler.go +++ b/pkg/controller/postgresql/schema/reconciler.go @@ -37,7 +37,6 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane-contrib/provider-sql/apis/postgresql/v1alpha1" - "github.com/crossplane-contrib/provider-sql/pkg/clients" "github.com/crossplane-contrib/provider-sql/pkg/clients/postgresql" "github.com/crossplane-contrib/provider-sql/pkg/clients/xsql" ) @@ -88,7 +87,7 @@ func Setup(mgr ctrl.Manager, o xpcontroller.Options) error { type connector struct { kube client.Client usage resource.Tracker - newDB func(creds map[string][]byte, database string, sslmode string) xsql.DB + newDB func(creds map[string][]byte, database string, options postgresql.Options) xsql.DB } func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) { @@ -125,7 +124,7 @@ func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.E return nil, errors.New(errNoDatabase) } - return &external{db: c.newDB(s.Data, *cr.Spec.ForProvider.Database, clients.ToString(pc.Spec.SSLMode))}, nil + return &external{db: c.newDB(s.Data, *cr.Spec.ForProvider.Database, pc.Spec.Options())}, nil } type external struct{ db xsql.DB } diff --git a/pkg/controller/postgresql/schema/reconciler_test.go b/pkg/controller/postgresql/schema/reconciler_test.go index d2c493e5..38be3207 100644 --- a/pkg/controller/postgresql/schema/reconciler_test.go +++ b/pkg/controller/postgresql/schema/reconciler_test.go @@ -34,6 +34,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/crossplane-runtime/pkg/test" + "github.com/crossplane-contrib/provider-sql/pkg/clients/postgresql" "github.com/crossplane-contrib/provider-sql/pkg/clients/xsql" ) @@ -70,7 +71,7 @@ func TestConnect(t *testing.T) { type fields struct { kube client.Client usage resource.Tracker - newDB func(creds map[string][]byte, database string, sslmode string) xsql.DB + newDB func(creds map[string][]byte, database string, options postgresql.Options) xsql.DB } type args struct { From 3ff79528f5e539e3b0b2b86b95842a6e8fa73977 Mon Sep 17 00:00:00 2001 From: Zoran Regvart Date: Wed, 28 May 2025 15:05:17 +0000 Subject: [PATCH 2/4] test(postgresql): e2e for TLS options Adds e2e tests for the functionality of providing the TLS options by testing with TLS turned on on the server side using the `tls.enabled=true` and `tls.autoGenerated=true` PostgreSQL Chart parameters. To make the tests as robust as possible: port forwarding is not used, but rather exposing the server port via NodePort; timeouts are increased on kubectl wait's, from 2 min to 3 min. The test iself reuses parts of the existing tests, and adds one additional check that asserts if TLS is used. Signed-off-by: Zoran Regvart --- cluster/local/integration_tests.sh | 9 ++ cluster/local/postgresdb_functions.sh | 145 ++++++++++++++++++++++++-- 2 files changed, 147 insertions(+), 7 deletions(-) diff --git a/cluster/local/integration_tests.sh b/cluster/local/integration_tests.sh index e8669b78..3c6a62d0 100755 --- a/cluster/local/integration_tests.sh +++ b/cluster/local/integration_tests.sh @@ -96,6 +96,15 @@ nodes: extraMounts: - hostPath: "${cache_path}/" containerPath: /cache + extraPortMappings: + - containerPort: 5432 + hostPort: 5432 + kubeadmConfigPatches: + - | + kind: ClusterConfiguration + apiServer: + extraArgs: + "service-node-port-range": "1-65535" EOF )" echo "${config}" | "${KIND}" create cluster --name="${K8S_CLUSTER}" --wait=5m --image="${node_image}" --config=- diff --git a/cluster/local/postgresdb_functions.sh b/cluster/local/postgresdb_functions.sh index 4a64c7e2..e1d4fc8e 100644 --- a/cluster/local/postgresdb_functions.sh +++ b/cluster/local/postgresdb_functions.sh @@ -21,6 +21,54 @@ setup_postgresdb_no_tls() { PORT_FORWARD_PID=$! } +setup_postgresdb_tls() { + echo_step "Installing PostgresDB Helm chart into default namespace" + postgres_root_pw=$(LC_ALL=C tr -cd "A-Za-z0-9" Date: Mon, 2 Jun 2025 17:05:25 +0200 Subject: [PATCH 3/4] chore(example): how to use custom TLS certificates An example on how to use custom TLS certificates with PostgreSQL. Signed-off-by: Zoran Regvart --- examples/postgresql/custom-certificates.yaml | 51 ++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 examples/postgresql/custom-certificates.yaml diff --git a/examples/postgresql/custom-certificates.yaml b/examples/postgresql/custom-certificates.yaml new file mode 100644 index 00000000..d6b9ab76 --- /dev/null +++ b/examples/postgresql/custom-certificates.yaml @@ -0,0 +1,51 @@ +--- +# This DeploymentRuntimeConfig will mount files embedded in a Secret to the +# provider Pod, this allows accessing those files as paths on in the options, +# e.g. when using custom TLS CA certificates or keys +apiVersion: pkg.crossplane.io/v1beta1 +kind: DeploymentRuntimeConfig +metadata: + name: postgres-custom-tls +spec: + deploymentTemplate: + spec: + selector: {} + template: + spec: + containers: + - name: package-runtime + volumeMounts: + - mountPath: /certs/postgres + name: postgresql-tls + readOnly: true + volumes: + - name: postgresql-tls + secret: + # Name of the secret containing the files + secretName: postgresdb-postgresql-crt + defaultMode: 420 +--- +# The DeploymentRuntimeConfig must be referenced in the Provider configuration +# for it to be effective +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-sql +spec: + runtimeConfigRef: + name: postgres-custom-tls + package: xpkg.upbound.io/crossplane-contrib/provider-sql:v0.13.0 +--- +# The configuration can now point to the /certs/postgres/ca.crt, ca.crt being +# the key in the postgresdb-postgresql-crt Secret referenced above +apiVersion: postgresql.sql.crossplane.io/v1alpha1 +kind: ProviderConfig +metadata: + name: default +spec: + sslRootCert: /certs/postgres/ca.crt + credentials: + source: PostgreSQLConnectionSecret + connectionSecretRef: + namespace: default + name: postgresdb-creds From 6014f47ba6a99cc1c46e07c3789b3091b59e8662 Mon Sep 17 00:00:00 2001 From: Zoran Regvart Date: Tue, 3 Jun 2025 12:09:57 +0200 Subject: [PATCH 4/4] feat(postgresql): TLS options via Secret Adds support for configuring TLS options using the credentials Secret. Very similar to how other providers are configured, this now also reads the `clusterCA`, `clientCert` and `clientKey` keys from the Secret. Integration tests are added, though they could be optimized to setup the PostgreSQL database only once and modify the configuration as needed to reduce the lenght of the tests, left as is for now. Signed-off-by: Zoran Regvart --- cluster/local/postgresdb_functions.sh | 43 +++++++++++++++ pkg/clients/postgresql/postgresql.go | 55 +++++++++++++++++-- pkg/clients/postgresql/postgresql_test.go | 30 ++++++++++ .../postgresql/database/reconciler.go | 18 ++++-- .../postgresql/database/reconciler_test.go | 2 +- .../postgresql/extension/reconciler.go | 14 ++++- .../postgresql/extension/reconciler_test.go | 2 +- pkg/controller/postgresql/grant/reconciler.go | 19 +++++-- .../postgresql/grant/reconciler_test.go | 2 +- pkg/controller/postgresql/role/reconciler.go | 18 ++++-- .../postgresql/role/reconciler_test.go | 2 +- .../postgresql/schema/reconciler.go | 18 ++++-- .../postgresql/schema/reconciler_test.go | 2 +- 13 files changed, 187 insertions(+), 38 deletions(-) diff --git a/cluster/local/postgresdb_functions.sh b/cluster/local/postgresdb_functions.sh index e1d4fc8e..e027dd86 100644 --- a/cluster/local/postgresdb_functions.sh +++ b/cluster/local/postgresdb_functions.sh @@ -137,6 +137,33 @@ EOF "${KUBECTL}" patch providers.pkg.crossplane.io/provider-sql --type=json --patch='[{"op":"add","path":"/spec/runtimeConfigRef","value":{"name":"postgres-tls"}}]' } +setup_provider_config_postgres_inline_tls() { + echo_step "creating ProviderConfig for PostgresDb with inline TLS" + local yaml="$(cat <