From 8c4657970aac846f7ceb2b26c0dbd641ae1a1946 Mon Sep 17 00:00:00 2001 From: Austin Cunningham Date: Fri, 10 Jan 2025 12:22:26 +0000 Subject: [PATCH] THREESCALE-11156 add Database TLS doc --- doc/apimanager-reference.md | 15 +++++++ doc/operator-user-guide.md | 42 +++++++++++++++++++ pkg/3scale/amp/component/zync.go | 30 +++++++++++++ .../amp/operator/zync_options_provider.go | 8 ---- pkg/helper/envvarutils.go | 16 +++++-- pkg/reconcilers/deployment.go | 7 ++++ 6 files changed, 107 insertions(+), 11 deletions(-) diff --git a/doc/apimanager-reference.md b/doc/apimanager-reference.md index f6dd9aca6..9de032a64 100644 --- a/doc/apimanager-reference.md +++ b/doc/apimanager-reference.md @@ -738,6 +738,11 @@ For Mysql: | URL | URL of the Porta database. Format: `mysql2://:@/`, where `` must be an already existing user in the external database with full permissions on the specified `` logical database and `` must be an already existing logical database in the external database.| Mandatory when the instance is managed externally. A default is only set when database is managed internally.
When managed internally:
`mysql2://root:@system-mysql/mysql`.| | DB_USER | Not used by 3scale components. Only used when the database is managed internally to create a new user granted with superuser permissions for the database specified in the `URL` field. | `mysql` | | DB_PASSWORD | Not used by 3scale components. Only used when the database is managed internally to create a new user granted with superuser permissions for the database specified in the `URL` field. | Autogenerated value | +| DATABASE_SSL_MODE | [Database SSL Mode](https://github.com/brianmario/mysql2?tab=readme-ov-file#ssltls-options) | Required to set TLS Database connection. Only for TLS | +| DB_SSL_CA | SSL CA certificate | Required to set TLS Database connection. Only for TLS | +| DB_SSL_CERT | SSL CERT certificate | Required to set TLS Database connection. Only for TLS | +DB_SSL_KEY | SSL Key | Required to set TLS Database connection. Only for TLS | + For Postgresql: @@ -746,6 +751,11 @@ For Postgresql: | URL | URL of the Porta database. Format: `postgresql://:@/`, where `` must be an already existing user in the external database with full permissions on the specified `` logical database and `` must be an already existing logical database in the external database.| Mandatory when the instance is managed externally. A default is only set when database is managed internally.
When managed internally:
`postgresql://system:@system-postgresql/system`.| | DB_USER | Not used by 3scale components. Only used when the database is managed internally to create a user with superuser power. | `system` | | DB_PASSWORD | Not used by 3scale components. Only used when the database is managed internally to create a user with superuser power. | Autogenerated value | +| DATABASE_SSL_MODE | [Database SSL Mode](https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-PROTECTION) | Required to set TLS Database connection. Only for TLS | +| DB_SSL_CA | SSL CA certificate | Required to set TLS Database connection. Only for TLS | +| DB_SSL_CERT | SSL CERT certificate | Required to set TLS Database connection. Only for TLS | +DB_SSL_KEY | SSL Key | Required to set TLS Database connection. Only for TLS | + For Oracle: @@ -812,6 +822,11 @@ For Oracle: | ZYNC_DATABASE_PASSWORD | Database password associated to the user specified in the `DATABASE_URL` parameter | When the database is managed externally, this parameter is mandatory and must have the same value as the password part of the `DATABASE_URL` parameter in this secret. Otherwise the default value is an autogenerated value if not defined | | SECRET_KEY_BASE | Zync's application key generator to encrypt communications | Autogenerated value | | ZYNC_AUTHENTICATION_TOKEN | Authentication token used to authenticate System when calling Zync | Autogenerated value | +| DATABASE_SSL_MODE | [Database SSL Mode](https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-PROTECTION) | Required to set TLS Database connection. Only for TLS | +| DB_SSL_CA | SSL CA certificate | Required to set TLS Database connection. Only for TLS | +| DB_SSL_CERT | SSL CERT certificate | Required to set TLS Database connection. Only for TLS | +DB_SSL_KEY | SSL Key | Required to set TLS Database connection. Only for TLS | + ### fileStorage-S3-credentials-secret diff --git a/doc/operator-user-guide.md b/doc/operator-user-guide.md index 8d62a304f..aae32af85 100644 --- a/doc/operator-user-guide.md +++ b/doc/operator-user-guide.md @@ -278,6 +278,48 @@ Secret name must be `zync`. See [Zync secret](apimanager-reference.md#zync) for reference. +#### TLS database configuration #### + +It is possible to connect to both the system-database and zync database via TLS provided these databases have TLS enabled. To enable TLS communication to these databases you will need to configure the ApiManager and the database secret. + +In ApiManager CR we set the boolean to enable TLS configuration for the respictive databases +- `spec.zyncDatabaseTLSEnabled: true` +- `spec.systemDatabaseTLSEnabled: true` + +We pass the cert files in via the respective secret i.e. system-database & zync + +You set the following values in the secret to connect to the database via TLS + +| Secret Key | Secret Value | +| --- | --- | +| DATABASE_SSL_MODE | string of the SSL mode for database connection | +| DB_SSL_CA | actual ca cert | +| DB_SSL_CERT | actual client cert | +| DB_SSL_KEY | actual client key | + +e.g. for system-database +```bash +oc create secret generic system-database \ + --from-literal=DATABASE_SSL_MODE=verify-ca \ + --from-literal=DATABASE_URL=postgresql://postgres:postgres@postgres-zync.postgres.svc.cluster.local/zync_production \ + --from-literal=ZYNC_DATABASE_PASSWORD=password \ + --from-file=DB_SSL_CA=rootCA.crt \ + --from-file=DB_SSL_CERT=client.crt \ + --from-file=DB_SSL_KEY=client.key +``` +e.g. for zync +```bash +oc create secret generic zync \ + --from-literal=DATABASE_SSL_MODE=verify-ca \ + --from-literal=DATABASE_URL=postgresql://postgres:postgres@postgres-zync.postgres.svc.cluster.local/zync_production \ + --from-literal=ZYNC_DATABASE_PASSWORD=password \ + --from-file=DB_SSL_CA=rootCA.crt \ + --from-file=DB_SSL_CERT=client.crt \ + --from-file=DB_SSL_KEY=client.key +``` + +Once these values have been set and are correct the operator will proceed to mount the certs into the related pods to enable client TLS communication. + #### S3 Filestorage Installation 3scale’s FileStorage being in a S3 service instead of in a PVC. diff --git a/pkg/3scale/amp/component/zync.go b/pkg/3scale/amp/component/zync.go index dc78eeafd..41c9cbc58 100644 --- a/pkg/3scale/amp/component/zync.go +++ b/pkg/3scale/amp/component/zync.go @@ -44,6 +44,36 @@ func NewZync(options *ZyncOptions) *Zync { } func (zync *Zync) Secret() *v1.Secret { + secret := &v1.Secret{} + if secret.Labels == nil { + secret.Labels = make(map[string]string) + } + labels := secret.Labels + labels["apimanager.apps.3scale.net/watched-by"] = "zync" + + if zync.Options.ZyncDbTLSEnabled { + return &v1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: ZyncSecretName, + Labels: labels, + }, + StringData: map[string]string{ + ZyncSecretKeyBaseFieldName: zync.Options.SecretKeyBase, + ZyncSecretDatabaseURLFieldName: zync.Options.DatabaseURL, + ZyncSecretDatabasePasswordFieldName: zync.Options.DatabasePassword, + ZyncSecretAuthenticationTokenFieldName: zync.Options.AuthenticationToken, + ZyncSecretDatabaseSslMode: zync.Options.DatabaseSslMode, + ZyncSecretSslCa: zync.Options.DatabaseSslCa, + ZyncSecretSslCert: zync.Options.DatabaseSslCert, + ZyncSecretSslKey: zync.Options.DatabaseSslKey, + }, + Type: v1.SecretTypeOpaque, + } + } return &v1.Secret{ TypeMeta: metav1.TypeMeta{ APIVersion: "v1", diff --git a/pkg/3scale/amp/operator/zync_options_provider.go b/pkg/3scale/amp/operator/zync_options_provider.go index 4da482597..6414699c1 100644 --- a/pkg/3scale/amp/operator/zync_options_provider.go +++ b/pkg/3scale/amp/operator/zync_options_provider.go @@ -48,9 +48,6 @@ func (z *ZyncOptionsProvider) GetZyncOptions() (*component.ZyncOptions, error) { z.setTopologySpreadConstraints() z.zyncOptions.CommonLabels = z.commonLabels() - if z.apimanager.IsZyncDatabaseTLSEnabled() { - z.zyncOptions.CommonZyncSecretLabels = z.commonZyncSecretLabels() - } z.zyncOptions.CommonZyncLabels = z.commonZyncLabels() z.zyncOptions.CommonZyncQueLabels = z.commonZyncQueLabels() z.zyncOptions.CommonZyncDatabaseLabels = z.commonZyncDatabaseLabels() @@ -245,11 +242,6 @@ func (z *ZyncOptionsProvider) commonLabels() map[string]string { "threescale_component": "zync", } } -func (z *ZyncOptionsProvider) commonZyncSecretLabels() map[string]string { - labels := z.commonLabels() - labels["apimanager.apps.3scale.net/watched-by"] = "zync" - return labels -} func (z *ZyncOptionsProvider) commonZyncLabels() map[string]string { labels := z.commonLabels() diff --git a/pkg/helper/envvarutils.go b/pkg/helper/envvarutils.go index efdd88e3e..25dee11f3 100644 --- a/pkg/helper/envvarutils.go +++ b/pkg/helper/envvarutils.go @@ -2,6 +2,7 @@ package helper import ( "context" + "fmt" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" "reflect" @@ -164,8 +165,16 @@ func EnvVarReconciler(desired []v1.EnvVar, existing *[]v1.EnvVar, envVar string) // check if the secret ssl certs are populated and sets the path if they are // system-app and zync use this function func TlsCertPresent(pathSslEnvVar string, secretName string, databaseTLSEnabled bool) string { - cfg, _ := config.GetConfig() - client, _ := client.New(cfg, client.Options{}) + cfg, err := config.GetConfig() + if err != nil { + fmt.Printf("clientTLS error, get config : %v", err) + return "" + } + clientTLS, err := client.New(cfg, client.Options{}) + if err != nil { + fmt.Printf("clientTLS error, client create : %v", err) + return "" + } namespace, _ := GetOperatorNamespace() var path string var sslEnvVar string @@ -191,8 +200,9 @@ func TlsCertPresent(pathSslEnvVar string, secretName string, databaseTLSEnabled Name: secretName, Namespace: namespace, } - err := client.Get(context.TODO(), nn, &secret) + err = clientTLS.Get(context.TODO(), nn, &secret) if err != nil { + fmt.Printf("clientTLS error, get secret : %v", err) return "" } diff --git a/pkg/reconcilers/deployment.go b/pkg/reconcilers/deployment.go index c7b041c94..f7adbc42b 100644 --- a/pkg/reconcilers/deployment.go +++ b/pkg/reconcilers/deployment.go @@ -327,6 +327,13 @@ func DeploymentPodInitContainerImageMutator(desired, existing *k8sappsv1.Deploym updated := false for i, desiredContainer := range desired.Spec.Template.Spec.InitContainers { + if i >= len(existing.Spec.Template.Spec.InitContainers) { + // Add missing containers from desired to existing + existing.Spec.Template.Spec.InitContainers = append(existing.Spec.Template.Spec.InitContainers, desiredContainer) + fmt.Printf("Added missing container: %s\n", desiredContainer.Name) + updated = true + continue + } existingContainer := &existing.Spec.Template.Spec.InitContainers[i] if !reflect.DeepEqual(existingContainer.Image, desiredContainer.Image) {