Skip to content
Open
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
45 changes: 45 additions & 0 deletions cli/cmd/install_openbao.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
package cmd

import (
"bufio"
"context"
"errors"
"fmt"
"io"
"os"
"os/exec"
"os/signal"
"path/filepath"
"strings"
"syscall"
"time"

Expand All @@ -29,19 +33,30 @@ type InstallOpenBaoCmd struct {
// InstallOpenBaoOpts holds the CLI flags for the OpenBao installer.
type InstallOpenBaoOpts struct {
*GlobalOptions
Namespace string
SecretsEngineName string
BaoUsername string
DRBackupPath string
Replicas int
StorageSize string
Timeout time.Duration
AgeKeyFile string
Yes bool
}

func (c *InstallOpenBaoCmd) RunE(_ *cobra.Command, _ []string) error {
if err := validateOpenBaoPrereqs(); err != nil {
return err
}

// If --age-key-file is provided, set SOPS_AGE_KEY_FILE so ResolveAgeKey
// picks it up. Otherwise, fall back to the normal auto-discovery chain.
if c.Opts.AgeKeyFile != "" {
if err := os.Setenv("SOPS_AGE_KEY_FILE", c.Opts.AgeKeyFile); err != nil {
return fmt.Errorf("setting SOPS_AGE_KEY_FILE: %w", err)
Comment thread
Jcing95 marked this conversation as resolved.
Comment on lines +54 to +56

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

changes global process state permanently.

We should pass the key file path through the OpenBaoInstallerConfig

}
}

configDir, err := os.UserConfigDir()
if err != nil {
return fmt.Errorf("determining user config directory: %w", err)
Expand All @@ -54,6 +69,7 @@ func (c *InstallOpenBaoCmd) RunE(_ *cobra.Command, _ []string) error {
}

cfg := installer.OpenBaoInstallerConfig{
Namespace: c.Opts.Namespace,
SecretsEngineName: c.Opts.SecretsEngineName,
Username: c.Opts.BaoUsername,
DRBackupPath: c.Opts.DRBackupPath,
Expand All @@ -69,6 +85,32 @@ func (c *InstallOpenBaoCmd) RunE(_ *cobra.Command, _ []string) error {
return fmt.Errorf("initializing openbao installer: %w", err)
}

inst.ConfirmFunc = func() error {
if c.Opts.Yes {
return nil
}

fmt.Printf("\nWARNING: No DR backup found at: %s\n", c.Opts.DRBackupPath)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We use log. Can we change all the fmt.Print stuff to log.Print

fmt.Println("This will perform a FRESH OpenBao initialization:")
fmt.Println(" - Existing Vault CR will be deleted")
fmt.Println(" - All OpenBao pods will be terminated")
fmt.Println(" - Persistent volume claims (data) will be deleted")
fmt.Println(" - Existing unseal keys will be removed")
fmt.Println("")
fmt.Println("If you intended to restore from a backup, verify --dr-backup-path is correct.")
fmt.Print("\nType 'yes' to continue: ")

reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
if err != nil && !errors.Is(err, io.EOF) {
return fmt.Errorf("failed to read confirmation: %w", err)
}
if strings.TrimSpace(strings.ToLower(input)) != "yes" {
return fmt.Errorf("aborted: type 'yes' to continue or pass --yes")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Confusing message. If it is aborted, how can we continue. Aborted sounds like a big error, but it is just a typo / failed confirm

}
return nil
}

ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()

Expand Down Expand Up @@ -100,12 +142,15 @@ func AddInstallOpenBaoCmd(install *cobra.Command, opts *GlobalOptions) {
},
Opts: &InstallOpenBaoOpts{GlobalOptions: opts},
}
openbao.cmd.Flags().StringVarP(&openbao.Opts.Namespace, "namespace", "n", installer.DefaultOpenBaoNamespace, "Kubernetes namespace for OpenBao deployment")
openbao.cmd.Flags().StringVar(&openbao.Opts.SecretsEngineName, "secrets-engine", "cs-secrets-engine", "Name of the KV-v2 secrets engine to provision")
openbao.cmd.Flags().StringVar(&openbao.Opts.BaoUsername, "bao-user", "admin", "Username for the userpass auth method (ignored on restore, uses DR backup value)")
openbao.cmd.Flags().StringVar(&openbao.Opts.DRBackupPath, "dr-backup-path", "", "Path for SOPS-encrypted DR backup file (required)")
openbao.cmd.Flags().IntVar(&openbao.Opts.Replicas, "replicas", 1, "Number of OpenBao replicas (1 for single-node, odd number >= 3 for HA)")
openbao.cmd.Flags().StringVar(&openbao.Opts.StorageSize, "storage-size", "10Gi", "PVC storage size for each OpenBao replica")
openbao.cmd.Flags().DurationVar(&openbao.Opts.Timeout, "timeout", 5*time.Minute, "Timeout for waiting on initialization")
openbao.cmd.Flags().StringVarP(&openbao.Opts.AgeKeyFile, "age-key-file", "k", "", "Path to age private key file for SOPS encryption/decryption (auto-detected if not set)")
openbao.cmd.Flags().BoolVarP(&openbao.Opts.Yes, "yes", "y", false, "Auto-approve re-initialization of an existing deployment when no DR backup is found")

util.MarkFlagRequired(openbao.cmd, "dr-backup-path")

Expand Down
3 changes: 3 additions & 0 deletions docs/oms_install_openbao.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,16 @@ $ oms install openbao --dr-backup-path ./backups/cluster-1.enc.json --timeout 10
### Options

```
-k, --age-key-file string Path to age private key file for SOPS encryption/decryption (auto-detected if not set)
--bao-user string Username for the userpass auth method (ignored on restore, uses DR backup value) (default "admin")
--dr-backup-path string Path for SOPS-encrypted DR backup file (required)
-h, --help help for openbao
-n, --namespace string Kubernetes namespace for OpenBao deployment (default "vault")
--replicas int Number of OpenBao replicas (1 for single-node, odd number >= 3 for HA) (default 1)
--secrets-engine string Name of the KV-v2 secrets engine to provision (default "cs-secrets-engine")
--storage-size string PVC storage size for each OpenBao replica (default "10Gi")
--timeout duration Timeout for waiting on initialization (default 5m0s)
-y, --yes Auto-approve re-initialization of an existing deployment when no DR backup is found
```

### SEE ALSO
Expand Down
37 changes: 37 additions & 0 deletions internal/installer/export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) Codesphere Inc.
// SPDX-License-Identifier: Apache-2.0

package installer

import (
"context"

corev1 "k8s.io/api/core/v1"
)

// Test helpers — exported only during `go test` so external test packages

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks like at least backupUnsealKeys is missing and not tested

// (package installer_test) can access unexported fields and methods.

func (o *OpenBaoInstaller) SetCtx(ctx context.Context) {
o.ctx = ctx
}

func (o *OpenBaoInstaller) SetUnsealSecret(secret *corev1.Secret) {
o.unsealSecret = secret
}

func (o *OpenBaoInstaller) SetPassword(password string) {
o.password = password
}

func (o *OpenBaoInstaller) GetDRBackupExists() bool {
return o.drBackupExists
}

func (o *OpenBaoInstaller) GetUnsealSecret() *corev1.Secret {
return o.unsealSecret
}

func (o *OpenBaoInstaller) HasExistingDeployment() (bool, error) {
return o.hasExistingDeployment()
}
6 changes: 3 additions & 3 deletions internal/installer/manifests/openbao/vault-cr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,12 @@ spec:
fieldRef:
fieldPath: metadata.name
- name: BAO_CLUSTER_ADDR
value: "http://$(POD_NAME).{{ .Namespace }}.svc.cluster.local:8201"
value: "http://$(POD_NAME).openbao.{{ .Namespace }}.svc.cluster.local:8201"
- name: BAO_API_ADDR
value: "http://$(POD_NAME).{{ .Namespace }}.svc.cluster.local:8200"
value: "http://$(POD_NAME).openbao.{{ .Namespace }}.svc.cluster.local:8200"
unsealConfig:
options:
preFlightChecks: false
preFlightChecks: true
storeRootToken: false
kubernetes:
secretNamespace: {{ .Namespace }}
Expand Down
Loading
Loading