Skip to content

Cert manager provided tls certs #571

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3c18c59
Make use of gardener provided TLS certificates
eberlep May 16, 2024
abc05fb
...
eberlep May 16, 2024
3e94f75
Generate shorter commonName
eberlep May 16, 2024
1a295a5
* remove commonName annotation
eberlep May 16, 2024
e51dc96
Try different annotations
eberlep May 16, 2024
2dba281
Add subdomain to commonName
eberlep May 16, 2024
e500fc4
use wildcard in commonName
eberlep May 16, 2024
bd25ebc
Use proper dns name instead of wildcard
eberlep May 16, 2024
7623905
Naming
eberlep May 16, 2024
097582b
Set spilo_fsgroup to fix tls permission issue
eberlep May 16, 2024
4f140fb
Add additional annotation
eberlep May 16, 2024
1fa6a20
Move annotations to shared LB when both are available
eberlep May 21, 2024
16ab026
Reset annotations if neccessary
eberlep May 21, 2024
9f76aa0
Update annotations
eberlep May 21, 2024
5822ae4
Centralize logic
eberlep May 24, 2024
d12dd1c
...
eberlep May 24, 2024
252d6dd
Merge branch 'main' into gardener-provided-tls-certs
Gerrit91 May 29, 2024
bc1133d
Merge remote-tracking branch 'origin' into gardener-provided-tls-certs
eberlep May 31, 2024
d97a353
Use certificate CR instead of gardener annotations
eberlep Jun 7, 2024
986c2f8
Set temporariy default value for testing
eberlep Jun 7, 2024
3913ff4
Add log
eberlep Jun 7, 2024
81aa41d
AddToScheme
eberlep Jun 7, 2024
3d1123f
Switch secret name to static string from docs
eberlep Jun 11, 2024
472d04a
* Make TLS subdomain configurable too
eberlep Jun 11, 2024
8168e5c
Remove (test) default value
eberlep Jun 12, 2024
4c678e6
Disable certificate creation when not configured
eberlep Jun 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 58 additions & 2 deletions api/v1/postgres_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,26 @@ func (p *Postgres) ToSharedSvcLBNamespacedName() *types.NamespacedName {
}
}

func (p *Postgres) ToDedicatedSvcLB(lbIP string, lbPort int32, standbyClustersSourceRanges []string) *corev1.Service {
func (p *Postgres) EnableSharedSVCLB(enableForceSharedIP bool) bool {
if enableForceSharedIP {
// shared IP is forced, so force it. No more questions asked.
return true
}

if p.Spec.DedicatedLoadBalancerIP == nil {
// No dedicated ip set at all, enabled shared lb
return true
}

if *p.Spec.DedicatedLoadBalancerIP == "" {
// Empty IP set, enable shared lb
return true
}

return false
}

func (p *Postgres) ToDedicatedSvcLB(lbIP string, lbPort int32, standbyClustersSourceRanges []string, sharedSvcLbAlsoEnabled bool) *corev1.Service {
lb := &corev1.Service{}
lb.Spec.Type = "LoadBalancer"

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

lb.Annotations = map[string]string{}

lbsr := []string{}
if p.HasSourceRanges() {
for _, src := range p.Spec.AccessList.SourceRanges {
Expand Down Expand Up @@ -485,6 +506,20 @@ func (p *Postgres) ToDedicatedSvcLBNamespacedName() *types.NamespacedName {
}
}

func (p *Postgres) EnableDedicatedSVCLB() bool {
if p.Spec.DedicatedLoadBalancerIP == nil {
// No dedicated ip set at all, disable dedicated lb
return false
}

if *p.Spec.DedicatedLoadBalancerIP == "" {
// Empty IP set, disable dedicated lb
return false
}

return true
}

func (p *Postgres) ToPeripheralResourceName() string {

return p.generateTeamID() + "-" + p.generateDatabaseName()
Expand Down Expand Up @@ -600,14 +635,29 @@ func (p *Postgres) ToPeripheralResourceNamespace() string {
return projectID + "-" + name
}

func (p *Postgres) ToDNSName(tlsSubDomain string) string {
// We only want letters and numbers
name := alphaNumericRegExp.ReplaceAllString(string(p.Name), "")
// Limit size
maxLen := 12
if len(name) > maxLen {
name = name[:maxLen]
}
return name + "." + tlsSubDomain
}

func (p *Postgres) ToTLSSecretName() string {
return "pg-tls"
}

func (p *Postgres) ToPeripheralResourceLookupKey() types.NamespacedName {
return types.NamespacedName{
Namespace: p.ToPeripheralResourceNamespace(),
Name: p.ToPeripheralResourceName(),
}
}

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) {
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) {
if z == nil {
z = &zalando.Postgresql{}
}
Expand Down Expand Up @@ -734,6 +784,12 @@ func (p *Postgres) ToUnstructuredZalandoPostgresql(z *zalando.Postgresql, c *cor
}
}

if enableTlsCert {
z.Spec.TLS = &zalando.TLSDescription{
SecretName: p.ToTLSSecretName(),
}
}

jsonZ, err := runtime.DefaultUnstructuredConverter.ToUnstructured(z)
if err != nil {
return nil, fmt.Errorf("failed to convert to unstructured zalando postgresql: %w", err)
Expand Down
2 changes: 1 addition & 1 deletion api/v1/postgres_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ func TestPostgresRestoreTimestamp_ToUnstructuredZalandoPostgresql(t *testing.T)
p := &Postgres{
Spec: tt.spec,
}
got, _ := p.ToUnstructuredZalandoPostgresql(nil, tt.c, tt.sc, tt.pgParamBlockList, tt.rbs, tt.srcDB, 130, 10, 60, false)
got, _ := p.ToUnstructuredZalandoPostgresql(nil, tt.c, tt.sc, tt.pgParamBlockList, tt.rbs, tt.srcDB, 130, 10, 60, false, false)

jsonZ, err := runtime.DefaultUnstructuredConverter.ToUnstructured(got)
if err != nil {
Expand Down
47 changes: 45 additions & 2 deletions controllers/postgres_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ import (
pg "github.com/fi-ts/postgreslet/api/v1"
"github.com/fi-ts/postgreslet/pkg/lbmanager"
"github.com/fi-ts/postgreslet/pkg/operatormanager"

cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
)

const (
Expand Down Expand Up @@ -93,6 +96,9 @@ type PostgresReconciler struct {
InitDBJobConfigMapName string
EnableBootstrapStandbyFromS3 bool
EnableSuperUserForDBO bool
EnableCustomTLSCert bool
TLSClusterIssuer string
TLSSubDomain string
}

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

// Request certificate, if neccessary
if err := r.createOrUpdateCertificate(log, ctx, instance); err != nil {
r.recorder.Eventf(instance, "Warning", "Error", "failed to create certificate request: %v", err)
return ctrl.Result{}, fmt.Errorf("error while creating certificate request: %w", err)
}

// Make sure the postgres secrets exist, if necessary
if err := r.ensurePostgresSecrets(log, ctx, instance); err != nil {
r.recorder.Eventf(instance, "Warning", "Error", "failed to create postgres secrets: %v", err)
Expand Down Expand Up @@ -373,7 +385,7 @@ func (r *PostgresReconciler) createOrUpdateZalandoPostgresql(ctx context.Context
return fmt.Errorf("failed to fetch zalando postgresql: %w", err)
}

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

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

return nil
}

func (r *PostgresReconciler) createOrUpdateCertificate(log logr.Logger, ctx context.Context, instance *pg.Postgres) error {
if r.TLSClusterIssuer == "" {
log.V(debugLogLevel).Info("certificate skipped")
return nil
}

commonName := instance.ToPeripheralResourceName()
if r.TLSSubDomain != "" {
commonName = instance.ToDNSName(r.TLSSubDomain)
}

c := &cmapi.Certificate{ObjectMeta: metav1.ObjectMeta{Name: instance.ToPeripheralResourceName(), Namespace: instance.ToPeripheralResourceNamespace()}}
if _, err := controllerutil.CreateOrUpdate(ctx, r.SvcClient, c, func() error {
c.Spec = cmapi.CertificateSpec{
CommonName: commonName,
SecretName: instance.ToTLSSecretName(),
IssuerRef: cmmeta.ObjectReference{
Group: "cert-manager.io",
Kind: "ClusterIssuer",
Name: r.TLSClusterIssuer,
},
}
return nil
}); err != nil {
return fmt.Errorf("unable to create or update certificate: %w", err)
}

log.V(debugLogLevel).Info("certificate created or updated")
return nil
}
3 changes: 2 additions & 1 deletion controllers/status_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,14 @@ func (r *StatusReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
}

log.V(debugLogLevel).Info("updating socket")
if owner.Spec.DedicatedLoadBalancerIP == nil {
if !owner.EnableDedicatedSVCLB() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the old code you executed the else block when the value of the IP was an empty string (aka "") now you enter the if block . this one looks more correct. i hope this is compatible with existing specs.

// no dedicated load balancer configured, use the shared one
shared := &corev1.Service{}
if err := r.SvcClient.Get(ctx, *owner.ToSharedSvcLBNamespacedName(), shared); err == nil {
// update IP and port
owner.Status.Socket.IP = shared.Spec.LoadBalancerIP
owner.Status.Socket.Port = shared.Spec.Ports[0].Port
owner.Status.AdditionalSockets = []pg.Socket{} // reset additional sockets

} else {
// Todo: Handle errors other than `NotFound`
Expand Down
16 changes: 9 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/fi-ts/postgreslet
go 1.22

require (
github.com/cert-manager/cert-manager v1.10.0
github.com/go-logr/logr v1.2.4
github.com/google/uuid v1.3.0
github.com/metal-stack/firewall-controller v1.3.0
Expand All @@ -12,10 +13,10 @@ require (
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.56.2
github.com/spf13/viper v1.12.0
github.com/zalando/postgres-operator v1.7.0
k8s.io/api v0.25.0
k8s.io/apiextensions-apiserver v0.25.0
k8s.io/apimachinery v0.25.0
k8s.io/client-go v0.25.0
k8s.io/api v0.25.2
k8s.io/apiextensions-apiserver v0.25.2
k8s.io/apimachinery v0.25.2
k8s.io/client-go v0.25.2
k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2
sigs.k8s.io/controller-runtime v0.13.1
)
Expand Down Expand Up @@ -73,7 +74,7 @@ require (
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.23.0 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
golang.org/x/crypto v0.0.0-20220924013350-4ba4fb4dd9e7 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/oauth2 v0.2.0 // indirect
Expand All @@ -90,9 +91,10 @@ require (
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/component-base v0.25.0 // indirect
k8s.io/component-base v0.25.2 // indirect
k8s.io/klog/v2 v2.80.1 // indirect
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea // indirect
sigs.k8s.io/gateway-api v0.5.0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
Expand Down
Loading