Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions operator/apis/platform/v1alpha1/central_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,21 @@ type CentralDBSpec struct {
DeploymentSpec `json:",inline"`
}

// GetPasswordSecret provides a way to retrieve the admin password that is safe to use on a nil receiver object.
func (c *CentralDBSpec) GetPasswordSecret() *LocalSecretReference {
if c == nil {
return nil
}
return c.PasswordSecret
}

func (c *CentralDBSpec) GetPasswordGenerationDisabled() bool {
if c == nil {
return false
}
return pointer.BoolPtrDerefOr(c.PasswordGenerationDisabled, false)
}

// IsExternal specifies that the database should not be managed by the Operator
func (c *CentralDBSpec) IsExternal() bool {
if c == nil {
Expand Down
81 changes: 59 additions & 22 deletions operator/pkg/central/extensions/reconcile_central_db_password.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package extensions

import (
"context"
"strings"

"github.com/go-logr/logr"
"github.com/operator-framework/helm-operator-plugins/pkg/extensions"
Expand All @@ -10,57 +11,93 @@ import (
commonExtensions "github.com/stackrox/rox/operator/pkg/common/extensions"
"github.com/stackrox/rox/operator/pkg/types"
"github.com/stackrox/rox/pkg/renderer"
coreV1 "k8s.io/api/core/v1"
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
)

const (
centralDBPasswordKey = `password`
centralDBPasswordResourceName = "central-db-password"
centralDBPasswordKey = `password`

defaultCentralDBPasswordSecretName = `central-db-password`
)

// ReconcileCentralDBPasswordExtension returns an extension that takes care of creating the central-db-password
// secret ahead of time.
// ReconcileCentralDBPasswordExtension returns an extension that takes care of reconciling the central-db-password secret.
func ReconcileCentralDBPasswordExtension(client ctrlClient.Client) extensions.ReconcileExtension {
return wrapExtension(reconcileCentralDBPassword, client)
}

func reconcileCentralDBPassword(ctx context.Context, c *platform.Central, client ctrlClient.Client, _ func(updateStatusFunc), _ logr.Logger) error {
if !c.Spec.Central.CentralDBEnabled() || c.Spec.Central.DB.IsExternal() {
return nil
}
func reconcileCentralDBPassword(ctx context.Context, c *platform.Central, client ctrlClient.Client, statusUpdater func(updateStatusFunc), log logr.Logger) error {
run := &reconcileCentralDBPasswordExtensionRun{
SecretReconciliator: commonExtensions.NewSecretReconciliator(client, c),
obj: c,
centralObj: c,
}
return run.Execute(ctx)
}

type reconcileCentralDBPasswordExtensionRun struct {
*commonExtensions.SecretReconciliator
obj *platform.Central
centralObj *platform.Central
password string
}

func (r *reconcileCentralDBPasswordExtensionRun) Execute(ctx context.Context) error {
// Delete any central-db password only if the CR is being deleted
shouldExist := r.obj.GetDeletionTimestamp() == nil
func (r *reconcileCentralDBPasswordExtensionRun) readPasswordFromReferencedSecret(ctx context.Context) error {
if r.centralObj.Spec.Central.DB.GetPasswordSecret() == nil {
return nil
}

if err := r.ReconcileSecret(ctx, centralDBPasswordResourceName, shouldExist, r.validateCentralDBPasswordData, r.generateCentralDBPasswordData, true); err != nil {
return errors.Wrapf(err, "reconciling %q secret", centralDBPasswordResourceName)
passwordSecretName := r.centralObj.Spec.Central.DB.PasswordSecret.Name

passwordSecret := &coreV1.Secret{}
key := ctrlClient.ObjectKey{Namespace: r.centralObj.GetNamespace(), Name: passwordSecretName}
if err := r.Client().Get(ctx, key, passwordSecret); err != nil {
return errors.Wrapf(err, "failed to retrieve central db password secret %q", passwordSecretName)
}

password := strings.TrimSpace(string(passwordSecret.Data[centralDBPasswordKey]))
if password == "" || strings.ContainsAny(password, "\r\n") {
return errors.Errorf("central db password secret %s must contain a non-empty, single-line %q entry", passwordSecretName, centralDBPasswordKey)
}

r.password = password
return nil
}

func (r *reconcileCentralDBPasswordExtensionRun) validateCentralDBPasswordData(data types.SecretDataMap, _ bool) error {
if len(data[centralDBPasswordKey]) == 0 {
return errors.Errorf("%s secret must contain a non-empty %q entry", centralDBPasswordResourceName, centralDBPasswordKey)
func (r *reconcileCentralDBPasswordExtensionRun) Execute(ctx context.Context) error {
if r.centralObj.DeletionTimestamp != nil {
return r.ReconcileSecret(ctx, defaultCentralDBPasswordSecretName, false, nil, nil, false)
}

if r.centralObj.Spec.Central.DB.GetPasswordGenerationDisabled() && r.centralObj.Spec.Central.DB.GetPasswordSecret() == nil {
err := r.ReconcileSecret(ctx, defaultCentralDBPasswordSecretName, false, nil, nil, false)
if err != nil {
return err
}
return nil
}

if err := r.readPasswordFromReferencedSecret(ctx); err != nil {
return err
}
// If the user puts the password into central-db-password secret then don't reconcile
if secret := r.centralObj.Spec.Central.DB.GetPasswordSecret(); secret != nil && secret.Name == defaultCentralDBPasswordSecretName {
return nil
}
if err := r.ReconcileSecret(ctx, defaultCentralDBPasswordSecretName, true, r.validateSecretData, r.generateDBPassword, true); err != nil {
return errors.Wrap(err, "reconciling central-db-password secret")
}
return nil
}

func (r *reconcileCentralDBPasswordExtensionRun) generateCentralDBPasswordData() (types.SecretDataMap, error) {
data := types.SecretDataMap{
centralDBPasswordKey: []byte(renderer.CreatePassword()),
func (r *reconcileCentralDBPasswordExtensionRun) validateSecretData(data types.SecretDataMap, controllerOwned bool) error {
return nil
}

func (r *reconcileCentralDBPasswordExtensionRun) generateDBPassword() (types.SecretDataMap, error) {
if r.password == "" {
r.password = renderer.CreatePassword()
}
return data, nil

return types.SecretDataMap{
centralDBPasswordKey: []byte(r.password),
}, nil
}
6 changes: 6 additions & 0 deletions operator/pkg/central/extensions/reconcile_pvc.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ func getPersistenceByClaimName(central *platform.Central, claim string) *platfor
case DefaultCentralPVCName:
return central.Spec.Central.GetPersistence()
case DefaultCentralDBPVCName:
if central.Spec.Central.DB.IsExternal() {
return nil
}
return convertDBPersistenceToPersistence(central.Spec.Central.DB.GetPersistence())
default:
panic("unknown default claim name")
Expand All @@ -82,6 +85,9 @@ func getPersistenceByClaimName(central *platform.Central, claim string) *platfor
func ReconcilePVCExtension(client ctrlClient.Client, target PVCTarget, defaultClaimName string) extensions.ReconcileExtension {
fn := func(ctx context.Context, central *platform.Central, client ctrlClient.Client, _ func(statusFunc updateStatusFunc), log logr.Logger) error {
persistence := getPersistenceByClaimName(central, defaultClaimName)
if persistence == nil {
return nil
}
return reconcilePVC(ctx, central, persistence, target, defaultClaimName, client, log)
}
return wrapExtension(fn, client)
Expand Down
2 changes: 2 additions & 0 deletions operator/pkg/central/values/translation/translation.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ func getCentralDBComponentValues(c *platform.CentralDBSpec) *translation.ValuesB
}

if c.ConnectionStringOverride != nil {
cv.SetBoolValue("external", true)

source := translation.NewValuesBuilder()
source.SetString("connectionString", c.ConnectionStringOverride)
cv.AddChild("source", &source)
Expand Down