Skip to content

Commit 2e575f3

Browse files
eberlepGerrit91
andauthored
Cert manager provided tls certs (#571)
* Make use of gardener provided TLS certificates * ... * Generate shorter commonName * * remove commonName annotation * shorten DNS name * Try different annotations * Add subdomain to commonName * use wildcard in commonName * Use proper dns name instead of wildcard * Naming * Set spilo_fsgroup to fix tls permission issue * Add additional annotation * Move annotations to shared LB when both are available * Reset annotations if neccessary * Update annotations * Centralize logic * ... * Use certificate CR instead of gardener annotations * Set temporariy default value for testing * Add log * AddToScheme * Switch secret name to static string from docs * * Make TLS subdomain configurable too * Use (obfuscated) commonName in cert when tls subdomain is configured (and possibly publicly announced) * Remove (test) default value * Disable certificate creation when not configured --------- Co-authored-by: Gerrit <[email protected]>
1 parent 4393d6d commit 2e575f3

File tree

9 files changed

+175
-30
lines changed

9 files changed

+175
-30
lines changed

api/v1/postgres_types.go

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,26 @@ func (p *Postgres) ToSharedSvcLBNamespacedName() *types.NamespacedName {
420420
}
421421
}
422422

423-
func (p *Postgres) ToDedicatedSvcLB(lbIP string, lbPort int32, standbyClustersSourceRanges []string) *corev1.Service {
423+
func (p *Postgres) EnableSharedSVCLB(enableForceSharedIP bool) bool {
424+
if enableForceSharedIP {
425+
// shared IP is forced, so force it. No more questions asked.
426+
return true
427+
}
428+
429+
if p.Spec.DedicatedLoadBalancerIP == nil {
430+
// No dedicated ip set at all, enabled shared lb
431+
return true
432+
}
433+
434+
if *p.Spec.DedicatedLoadBalancerIP == "" {
435+
// Empty IP set, enable shared lb
436+
return true
437+
}
438+
439+
return false
440+
}
441+
442+
func (p *Postgres) ToDedicatedSvcLB(lbIP string, lbPort int32, standbyClustersSourceRanges []string, sharedSvcLbAlsoEnabled bool) *corev1.Service {
424443
lb := &corev1.Service{}
425444
lb.Spec.Type = "LoadBalancer"
426445

@@ -430,6 +449,8 @@ func (p *Postgres) ToDedicatedSvcLB(lbIP string, lbPort int32, standbyClustersSo
430449
lb.Name = p.ToDedicatedSvcLBName()
431450
lb.SetLabels(SvcLoadBalancerLabel)
432451

452+
lb.Annotations = map[string]string{}
453+
433454
lbsr := []string{}
434455
if p.HasSourceRanges() {
435456
for _, src := range p.Spec.AccessList.SourceRanges {
@@ -485,6 +506,20 @@ func (p *Postgres) ToDedicatedSvcLBNamespacedName() *types.NamespacedName {
485506
}
486507
}
487508

509+
func (p *Postgres) EnableDedicatedSVCLB() bool {
510+
if p.Spec.DedicatedLoadBalancerIP == nil {
511+
// No dedicated ip set at all, disable dedicated lb
512+
return false
513+
}
514+
515+
if *p.Spec.DedicatedLoadBalancerIP == "" {
516+
// Empty IP set, disable dedicated lb
517+
return false
518+
}
519+
520+
return true
521+
}
522+
488523
func (p *Postgres) ToPeripheralResourceName() string {
489524

490525
return p.generateTeamID() + "-" + p.generateDatabaseName()
@@ -600,14 +635,29 @@ func (p *Postgres) ToPeripheralResourceNamespace() string {
600635
return projectID + "-" + name
601636
}
602637

638+
func (p *Postgres) ToDNSName(tlsSubDomain string) string {
639+
// We only want letters and numbers
640+
name := alphaNumericRegExp.ReplaceAllString(string(p.Name), "")
641+
// Limit size
642+
maxLen := 12
643+
if len(name) > maxLen {
644+
name = name[:maxLen]
645+
}
646+
return name + "." + tlsSubDomain
647+
}
648+
649+
func (p *Postgres) ToTLSSecretName() string {
650+
return "pg-tls"
651+
}
652+
603653
func (p *Postgres) ToPeripheralResourceLookupKey() types.NamespacedName {
604654
return types.NamespacedName{
605655
Namespace: p.ToPeripheralResourceNamespace(),
606656
Name: p.ToPeripheralResourceName(),
607657
}
608658
}
609659

610-
func (p *Postgres) ToUnstructuredZalandoPostgresql(z *zalando.Postgresql, c *corev1.ConfigMap, sc string, pgParamBlockList map[string]bool, rbs *BackupConfig, srcDB *Postgres, patroniTTL, patroniLoopWait, patroniRetryTimeout uint32, dboIsSuperuser bool) (*unstructured.Unstructured, error) {
660+
func (p *Postgres) ToUnstructuredZalandoPostgresql(z *zalando.Postgresql, c *corev1.ConfigMap, sc string, pgParamBlockList map[string]bool, rbs *BackupConfig, srcDB *Postgres, patroniTTL, patroniLoopWait, patroniRetryTimeout uint32, dboIsSuperuser bool, enableTlsCert bool) (*unstructured.Unstructured, error) {
611661
if z == nil {
612662
z = &zalando.Postgresql{}
613663
}
@@ -734,6 +784,12 @@ func (p *Postgres) ToUnstructuredZalandoPostgresql(z *zalando.Postgresql, c *cor
734784
}
735785
}
736786

787+
if enableTlsCert {
788+
z.Spec.TLS = &zalando.TLSDescription{
789+
SecretName: p.ToTLSSecretName(),
790+
}
791+
}
792+
737793
jsonZ, err := runtime.DefaultUnstructuredConverter.ToUnstructured(z)
738794
if err != nil {
739795
return nil, fmt.Errorf("failed to convert to unstructured zalando postgresql: %w", err)

api/v1/postgres_types_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ func TestPostgresRestoreTimestamp_ToUnstructuredZalandoPostgresql(t *testing.T)
349349
p := &Postgres{
350350
Spec: tt.spec,
351351
}
352-
got, _ := p.ToUnstructuredZalandoPostgresql(nil, tt.c, tt.sc, tt.pgParamBlockList, tt.rbs, tt.srcDB, 130, 10, 60, false)
352+
got, _ := p.ToUnstructuredZalandoPostgresql(nil, tt.c, tt.sc, tt.pgParamBlockList, tt.rbs, tt.srcDB, 130, 10, 60, false, false)
353353

354354
jsonZ, err := runtime.DefaultUnstructuredConverter.ToUnstructured(got)
355355
if err != nil {

controllers/postgres_controller.go

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ import (
4646
pg "github.com/fi-ts/postgreslet/api/v1"
4747
"github.com/fi-ts/postgreslet/pkg/lbmanager"
4848
"github.com/fi-ts/postgreslet/pkg/operatormanager"
49+
50+
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
51+
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
4952
)
5053

5154
const (
@@ -93,6 +96,9 @@ type PostgresReconciler struct {
9396
InitDBJobConfigMapName string
9497
EnableBootstrapStandbyFromS3 bool
9598
EnableSuperUserForDBO bool
99+
EnableCustomTLSCert bool
100+
TLSClusterIssuer string
101+
TLSSubDomain string
96102
}
97103

98104
// Reconcile is the entry point for postgres reconciliation.
@@ -237,6 +243,12 @@ func (r *PostgresReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
237243
}
238244
}
239245

246+
// Request certificate, if neccessary
247+
if err := r.createOrUpdateCertificate(log, ctx, instance); err != nil {
248+
r.recorder.Eventf(instance, "Warning", "Error", "failed to create certificate request: %v", err)
249+
return ctrl.Result{}, fmt.Errorf("error while creating certificate request: %w", err)
250+
}
251+
240252
// Make sure the postgres secrets exist, if necessary
241253
if err := r.ensurePostgresSecrets(log, ctx, instance); err != nil {
242254
r.recorder.Eventf(instance, "Warning", "Error", "failed to create postgres secrets: %v", err)
@@ -373,7 +385,7 @@ func (r *PostgresReconciler) createOrUpdateZalandoPostgresql(ctx context.Context
373385
return fmt.Errorf("failed to fetch zalando postgresql: %w", err)
374386
}
375387

376-
u, err := instance.ToUnstructuredZalandoPostgresql(nil, sidecarsCM, r.StorageClass, r.PgParamBlockList, restoreBackupConfig, restoreSourceInstance, patroniTTL, patroniLoopWait, patroniRetryTimeout, r.EnableSuperUserForDBO)
388+
u, err := instance.ToUnstructuredZalandoPostgresql(nil, sidecarsCM, r.StorageClass, r.PgParamBlockList, restoreBackupConfig, restoreSourceInstance, patroniTTL, patroniLoopWait, patroniRetryTimeout, r.EnableSuperUserForDBO, r.EnableCustomTLSCert)
377389
if err != nil {
378390
return fmt.Errorf("failed to convert to unstructured zalando postgresql: %w", err)
379391
}
@@ -389,7 +401,7 @@ func (r *PostgresReconciler) createOrUpdateZalandoPostgresql(ctx context.Context
389401
// Update zalando postgresql
390402
mergeFrom := client.MergeFrom(rawZ.DeepCopy())
391403

392-
u, err := instance.ToUnstructuredZalandoPostgresql(rawZ, sidecarsCM, r.StorageClass, r.PgParamBlockList, restoreBackupConfig, restoreSourceInstance, patroniTTL, patroniLoopWait, patroniRetryTimeout, r.EnableSuperUserForDBO)
404+
u, err := instance.ToUnstructuredZalandoPostgresql(rawZ, sidecarsCM, r.StorageClass, r.PgParamBlockList, restoreBackupConfig, restoreSourceInstance, patroniTTL, patroniLoopWait, patroniRetryTimeout, r.EnableSuperUserForDBO, r.EnableCustomTLSCert)
393405
if err != nil {
394406
return fmt.Errorf("failed to convert to unstructured zalando postgresql: %w", err)
395407
}
@@ -1710,3 +1722,34 @@ func (r *PostgresReconciler) ensureInitDBJob(log logr.Logger, ctx context.Contex
17101722

17111723
return nil
17121724
}
1725+
1726+
func (r *PostgresReconciler) createOrUpdateCertificate(log logr.Logger, ctx context.Context, instance *pg.Postgres) error {
1727+
if r.TLSClusterIssuer == "" {
1728+
log.V(debugLogLevel).Info("certificate skipped")
1729+
return nil
1730+
}
1731+
1732+
commonName := instance.ToPeripheralResourceName()
1733+
if r.TLSSubDomain != "" {
1734+
commonName = instance.ToDNSName(r.TLSSubDomain)
1735+
}
1736+
1737+
c := &cmapi.Certificate{ObjectMeta: metav1.ObjectMeta{Name: instance.ToPeripheralResourceName(), Namespace: instance.ToPeripheralResourceNamespace()}}
1738+
if _, err := controllerutil.CreateOrUpdate(ctx, r.SvcClient, c, func() error {
1739+
c.Spec = cmapi.CertificateSpec{
1740+
CommonName: commonName,
1741+
SecretName: instance.ToTLSSecretName(),
1742+
IssuerRef: cmmeta.ObjectReference{
1743+
Group: "cert-manager.io",
1744+
Kind: "ClusterIssuer",
1745+
Name: r.TLSClusterIssuer,
1746+
},
1747+
}
1748+
return nil
1749+
}); err != nil {
1750+
return fmt.Errorf("unable to create or update certificate: %w", err)
1751+
}
1752+
1753+
log.V(debugLogLevel).Info("certificate created or updated")
1754+
return nil
1755+
}

controllers/status_controller.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,14 @@ func (r *StatusReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
9797
}
9898

9999
log.V(debugLogLevel).Info("updating socket")
100-
if owner.Spec.DedicatedLoadBalancerIP == nil {
100+
if !owner.EnableDedicatedSVCLB() {
101101
// no dedicated load balancer configured, use the shared one
102102
shared := &corev1.Service{}
103103
if err := r.SvcClient.Get(ctx, *owner.ToSharedSvcLBNamespacedName(), shared); err == nil {
104104
// update IP and port
105105
owner.Status.Socket.IP = shared.Spec.LoadBalancerIP
106106
owner.Status.Socket.Port = shared.Spec.Ports[0].Port
107+
owner.Status.AdditionalSockets = []pg.Socket{} // reset additional sockets
107108

108109
} else {
109110
// Todo: Handle errors other than `NotFound`

go.mod

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/fi-ts/postgreslet
33
go 1.22
44

55
require (
6+
github.com/cert-manager/cert-manager v1.10.0
67
github.com/go-logr/logr v1.2.4
78
github.com/google/uuid v1.3.0
89
github.com/metal-stack/firewall-controller v1.3.0
@@ -12,10 +13,10 @@ require (
1213
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.56.2
1314
github.com/spf13/viper v1.12.0
1415
github.com/zalando/postgres-operator v1.7.0
15-
k8s.io/api v0.25.0
16-
k8s.io/apiextensions-apiserver v0.25.0
17-
k8s.io/apimachinery v0.25.0
18-
k8s.io/client-go v0.25.0
16+
k8s.io/api v0.25.2
17+
k8s.io/apiextensions-apiserver v0.25.2
18+
k8s.io/apimachinery v0.25.2
19+
k8s.io/client-go v0.25.2
1920
k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2
2021
sigs.k8s.io/controller-runtime v0.13.1
2122
)
@@ -73,7 +74,7 @@ require (
7374
go.uber.org/atomic v1.10.0 // indirect
7475
go.uber.org/multierr v1.8.0 // indirect
7576
go.uber.org/zap v1.23.0 // indirect
76-
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
77+
golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7 // indirect
7778
golang.org/x/mod v0.10.0 // indirect
7879
golang.org/x/net v0.10.0 // indirect
7980
golang.org/x/oauth2 v0.2.0 // indirect
@@ -90,9 +91,10 @@ require (
9091
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
9192
gopkg.in/yaml.v2 v2.4.0 // indirect
9293
gopkg.in/yaml.v3 v3.0.1 // indirect
93-
k8s.io/component-base v0.25.0 // indirect
94+
k8s.io/component-base v0.25.2 // indirect
9495
k8s.io/klog/v2 v2.80.1 // indirect
95-
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
96+
k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea // indirect
97+
sigs.k8s.io/gateway-api v0.5.0 // indirect
9698
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
9799
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
98100
sigs.k8s.io/yaml v1.3.0 // indirect

0 commit comments

Comments
 (0)