
The helm chart uses numeric values representing the following log levels: DEBUG, INFO, WARNING, ERROR and FATAL Update the validation to accept numeric levels, and interpet 5 (FATAL) as ERROR. Test Plan: PASS test valid/invalid loglevels, numeric bounds Change-Id: Ifbebe30e487f30ba11ada398056252e80a07ba94 Signed-off-by: Michel Thebeau <Michel.Thebeau@windriver.com>
321 lines
9.8 KiB
Go
321 lines
9.8 KiB
Go
//
|
|
// Copyright (c) 2025 Wind River Systems, Inc.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
package baoConfig
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log/slog"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/go-yaml/yaml"
|
|
clientapi "github.com/openbao/openbao/api/v2"
|
|
)
|
|
|
|
type ServerAddress struct {
|
|
Host string `yaml:"host"`
|
|
Port int `yaml:"port"`
|
|
}
|
|
|
|
type Token struct {
|
|
Duration int `yaml:"duration"`
|
|
Key string `yaml:"key"`
|
|
}
|
|
|
|
type KeyShards struct {
|
|
Key string `yaml:"key"`
|
|
KeyBase64 string `yaml:"key_base64"`
|
|
}
|
|
|
|
type MonitorConfig struct {
|
|
// A map value listing all DNS names
|
|
// Key: Domain name
|
|
// Value: ServerAddress. consisting of host address and port number
|
|
ServerAddresses map[string]ServerAddress `yaml:"ServerAddresses"`
|
|
|
|
// A map value listing all authentication tokens
|
|
// Key: release id
|
|
// Value: Token. consisting of lease duration and the token key
|
|
Tokens map[string]Token `yaml:"Tokens"`
|
|
|
|
// A map value listing all key shards for unseal
|
|
// Key: shard name
|
|
// Value: The shard key and the base64 encoded version of that key
|
|
UnsealKeyShards map[string]KeyShards `yaml:"UnsealKeyShards"`
|
|
|
|
// A string of path to the PEM-encoded CA cert file to use to verify
|
|
// The server's SSL certificate
|
|
// Leave this empty if using the default CA cert file location
|
|
CACert string `yaml:"CACert"`
|
|
|
|
// ClientCert is the path to the certificate for Vault communication
|
|
ClientCert string `yaml:"ClientCert"`
|
|
|
|
// ClientKey is the path to the private key for Vault communication
|
|
ClientKey string `yaml:"ClientKey"`
|
|
|
|
// The path of the log file
|
|
LogPath string `yaml:"logPath"`
|
|
|
|
// The default log level
|
|
// Available log levels: DEBUG, INFO, WARN and ERROR
|
|
// Accepts numeric values matching helm chart log levels (4-5 are interpreted as ERROR)
|
|
LogLevel string `yaml:"logLevel"`
|
|
|
|
// The time in seconds waited between each unseal check in the run command.
|
|
// If this is unset or set to 0, the command option can be used to supply the time.
|
|
// If neither is supplied, then default time of 5 seconds will be used.
|
|
WaitInterval int `yaml:"WaitInterval"`
|
|
|
|
// Time, in seconds, the client will wait for each request before
|
|
// returning timeout exceeded error.
|
|
// Set this value in negative to use the default value of 60 seconds.
|
|
Timeout int `yaml:"Timeout"`
|
|
|
|
// Namespace used for the k8s application.
|
|
Namespace string `yaml:"Namespace"`
|
|
|
|
// Default port for all addresses.
|
|
// If the port number was not specified in the config file, it will use this port number.
|
|
// This port number will also be used for all generated addresses from Kubernetes pods
|
|
// Default value is always 8200
|
|
DefaultPort int `yaml:"DefaultPort"`
|
|
|
|
// Prefix string used to find all server pods
|
|
PodPrefix string `yaml:"PodPrefix"`
|
|
|
|
// Suffix string for all generated pod addresses
|
|
// Default is "pod.cluster.local"
|
|
PodAddressSuffix string `yaml:"PodAddressSuffix"`
|
|
|
|
// Prefix string used to find root token and unseal key shards
|
|
// Default is "cluster-key"
|
|
SecretPrefix string `yaml:"SecretPrefix"`
|
|
}
|
|
|
|
func (configInstance *MonitorConfig) ReadYAMLMonitorConfig(in io.Reader) error {
|
|
data, err := io.ReadAll(in)
|
|
if err != nil {
|
|
return fmt.Errorf(
|
|
"unable to read Host DNS config data from input. Error message: %v", err)
|
|
}
|
|
|
|
err = yaml.Unmarshal(data, configInstance)
|
|
if err != nil {
|
|
return fmt.Errorf(
|
|
"unable to unmarshal Host DNS config YAML data. Error message: %v", err)
|
|
}
|
|
|
|
// Use default port value of 8200, if no default port was specified.
|
|
if configInstance.DefaultPort == 0 {
|
|
configInstance.DefaultPort = 8200
|
|
}
|
|
|
|
// Fill in empty ports
|
|
for dnsname, addr := range configInstance.ServerAddresses {
|
|
if addr.Port == 0 {
|
|
addr.Port = configInstance.DefaultPort
|
|
configInstance.ServerAddresses[dnsname] = addr
|
|
}
|
|
}
|
|
|
|
// Validate YAML input for ServerAddresses
|
|
err = configInstance.validateDNS()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Validate YAML input for Tokens
|
|
err = configInstance.validateTokens()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Validate YAML input for unseal key shards
|
|
err = configInstance.validateKeyShards()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Validate YAML input for CACert
|
|
err = configInstance.validateCACert()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Validate YAML input for log configs
|
|
err = configInstance.validateLogConfig()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (configInstance MonitorConfig) WriteYAMLMonitorConfig(out io.Writer) error {
|
|
data, err := yaml.Marshal(configInstance)
|
|
if err != nil {
|
|
return fmt.Errorf(
|
|
"unable to marshal Host DNS config data to YAML. Error message: %v", err)
|
|
}
|
|
|
|
_, err = out.Write(data)
|
|
if err != nil {
|
|
return fmt.Errorf(
|
|
"unable to write marshaled Host DNS config YAML data. Error message: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Create a new config based on the monitor config
|
|
func (configInstance MonitorConfig) NewConfig(dnshost string) (*clientapi.Config, error) {
|
|
slog.Debug(fmt.Sprintf("Setting up api access config for host %v", dnshost))
|
|
defConfig := clientapi.DefaultConfig()
|
|
|
|
// Check if DefaultConfig has issues
|
|
if defConfig.Error != nil {
|
|
return defConfig, fmt.Errorf("issue found in default config: %v", defConfig.Error)
|
|
}
|
|
slog.Debug("No issues found in retrieving default config.")
|
|
|
|
// Check if there is a domain name listed under ServerAddresses
|
|
dnsAddr, ok := configInstance.ServerAddresses[dnshost]
|
|
if !ok {
|
|
return defConfig, fmt.Errorf("unable to find %v under the list of available DNS names", dnshost)
|
|
}
|
|
|
|
// Set the DNS address as the configured address for the server
|
|
defConfig.Address = strings.Join([]string{"https://", dnsAddr.Host, ":", strconv.Itoa(dnsAddr.Port)}, "")
|
|
|
|
slog.Debug(fmt.Sprintf("Server address set to %v", defConfig.Address))
|
|
|
|
// Apply CACert entry to the config
|
|
var newTLSconfig clientapi.TLSConfig
|
|
slog.Debug("Applying the following cert configs:")
|
|
slog.Debug(fmt.Sprintf("CACert: %v", configInstance.CACert))
|
|
slog.Debug(fmt.Sprintf("ClientCert: %v", configInstance.ClientCert))
|
|
slog.Debug(fmt.Sprintf("ClientKey: %v", configInstance.ClientKey))
|
|
|
|
newTLSconfig.CACert = configInstance.CACert
|
|
newTLSconfig.ClientCert = configInstance.ClientCert
|
|
newTLSconfig.ClientKey = configInstance.ClientKey
|
|
|
|
// This does nothing if newTLSconfig is empty
|
|
err := defConfig.ConfigureTLS(&newTLSconfig)
|
|
if err != nil {
|
|
return defConfig, fmt.Errorf("error with configuring TLS: %v", err)
|
|
}
|
|
|
|
slog.Debug("Configuring TLS successful")
|
|
|
|
// Set the timeout value. Do not set the value if it is negative.
|
|
if configInstance.Timeout >= 0 {
|
|
defConfig.Timeout = time.Duration(configInstance.Timeout) * time.Second
|
|
}
|
|
|
|
slog.Debug("API access config setup complete.")
|
|
// Config creation complete.
|
|
return defConfig, nil
|
|
}
|
|
|
|
func (configInstance MonitorConfig) SetupClient(dnshost string) (*clientapi.Client, error) {
|
|
slog.Debug(fmt.Sprintf("Setting up client for host %v", dnshost))
|
|
newConfig, err := configInstance.NewConfig(dnshost)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error in creating new config: %v", err)
|
|
}
|
|
|
|
slog.Debug("Creating client for API access...")
|
|
newClient, err := clientapi.NewClient(newConfig)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error in creating new client: %v", err)
|
|
}
|
|
|
|
slog.Debug("Client setup complete.")
|
|
return newClient, nil
|
|
}
|
|
|
|
// Parse the new keys from the init responce into the monitor config
|
|
func (configInstance *MonitorConfig) ParseInitResponse(dnshost string, responce *clientapi.InitResponse) error {
|
|
slog.Debug("Parsing response from /sys/init to monitor configs")
|
|
|
|
keyShardheader := strings.Join([]string{"key", "shard", dnshost}, "-")
|
|
|
|
slog.Debug("Parsing the root token...")
|
|
// Parse in the root token
|
|
if _, ok := configInstance.Tokens["root_token"]; ok {
|
|
return fmt.Errorf("an entry of the root token was already found")
|
|
}
|
|
if configInstance.Tokens == nil {
|
|
configInstance.Tokens = make(map[string]Token)
|
|
}
|
|
configInstance.Tokens["root_token"] = Token{
|
|
Duration: 0,
|
|
Key: responce.RootToken,
|
|
}
|
|
|
|
slog.Debug("Parsing the unseal key shards...")
|
|
// Parse in the key shards for unseal
|
|
for i := range len(responce.Keys) {
|
|
keyShardName := strings.Join([]string{keyShardheader, strconv.Itoa(i)}, "-")
|
|
if _, ok := configInstance.UnsealKeyShards[keyShardName]; ok {
|
|
return fmt.Errorf("an entry of %v was already found under UnsealKeyShards", keyShardName)
|
|
}
|
|
if configInstance.UnsealKeyShards == nil {
|
|
configInstance.UnsealKeyShards = make(map[string]KeyShards)
|
|
}
|
|
configInstance.UnsealKeyShards[keyShardName] = KeyShards{
|
|
Key: responce.Keys[i],
|
|
KeyBase64: responce.KeysB64[i],
|
|
}
|
|
}
|
|
|
|
slog.Debug("Parsing the recovery key shards...")
|
|
// Parse in the recovery key shards
|
|
for i := range len(responce.RecoveryKeys) {
|
|
keyShardName := strings.Join([]string{keyShardheader, "recovery", strconv.Itoa(i)}, "-")
|
|
if _, ok := configInstance.UnsealKeyShards[keyShardName]; ok {
|
|
return fmt.Errorf("an entry of %v was already found under UnsealKeyShards", keyShardName)
|
|
}
|
|
if configInstance.UnsealKeyShards == nil {
|
|
configInstance.UnsealKeyShards = make(map[string]KeyShards)
|
|
}
|
|
configInstance.UnsealKeyShards[keyShardName] = KeyShards{
|
|
Key: responce.RecoveryKeys[i],
|
|
KeyBase64: responce.RecoveryKeysB64[i],
|
|
}
|
|
}
|
|
|
|
slog.Debug("Parsing init response complete")
|
|
return nil
|
|
}
|
|
|
|
// Interpret numeric or text log level
|
|
// Always returns a log level in string format
|
|
func (configInstance *MonitorConfig) InterpretLogLevel() string {
|
|
// Check if the log level is a number
|
|
if converted, err := strconv.Atoi(configInstance.LogLevel); err == nil {
|
|
if level, exists := availableLogLevels[converted]; exists {
|
|
return level
|
|
}
|
|
|
|
// error, but this code should not be reached if validateLogConfig works
|
|
fmt.Errorf("the numeric LogLevel %v is not a valid log level", configInstance.LogLevel)
|
|
return "INFO" // Default to INFO if the numeric level is invalid
|
|
}
|
|
|
|
// Default to INFO if no log level was set
|
|
if configInstance.LogLevel == "" {
|
|
return "INFO"
|
|
}
|
|
|
|
// validateLogConfig already validated the LogLevel
|
|
return configInstance.LogLevel
|
|
} |