blob: 04f94a8544a5e00d15a0363d36e741fbc860625f [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package configutil
import (
"fmt"
"time"
"github.com/hashicorp/go-secure-stdlib/parseutil"
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/hcl/hcl/token"
)
// SharedConfig contains some shared values
type SharedConfig struct {
FoundKeys []string `hcl:",decodedFields"`
UnusedKeys UnusedKeyMap `hcl:",unusedKeyPositions"`
Sections map[string][]token.Pos
EntSharedConfig
Listeners []*Listener `hcl:"-"`
UserLockouts []*UserLockout `hcl:"-"`
Seals []*KMS `hcl:"-"`
Entropy *Entropy `hcl:"-"`
DisableMlock bool `hcl:"-"`
DisableMlockRaw interface{} `hcl:"disable_mlock"`
Telemetry *Telemetry `hcl:"telemetry"`
HCPLinkConf *HCPLinkConfig `hcl:"cloud"`
DefaultMaxRequestDuration time.Duration `hcl:"-"`
DefaultMaxRequestDurationRaw interface{} `hcl:"default_max_request_duration"`
// LogFormat specifies the log format. Valid values are "standard" and
// "json". The values are case-insenstive. If no log format is specified,
// then standard format will be used.
LogFile string `hcl:"log_file"`
LogFormat string `hcl:"log_format"`
LogLevel string `hcl:"log_level"`
LogRotateBytes int `hcl:"log_rotate_bytes"`
LogRotateBytesRaw interface{} `hcl:"log_rotate_bytes"`
LogRotateDuration string `hcl:"log_rotate_duration"`
LogRotateMaxFiles int `hcl:"log_rotate_max_files"`
LogRotateMaxFilesRaw interface{} `hcl:"log_rotate_max_files"`
PidFile string `hcl:"pid_file"`
ClusterName string `hcl:"cluster_name"`
AdministrativeNamespacePath string `hcl:"administrative_namespace_path"`
}
func ParseConfig(d string) (*SharedConfig, error) {
// Parse!
obj, err := hcl.Parse(d)
if err != nil {
return nil, err
}
// Start building the result
var result SharedConfig
if err := hcl.DecodeObject(&result, obj); err != nil {
return nil, err
}
if result.DefaultMaxRequestDurationRaw != nil {
if result.DefaultMaxRequestDuration, err = parseutil.ParseDurationSecond(result.DefaultMaxRequestDurationRaw); err != nil {
return nil, err
}
result.FoundKeys = append(result.FoundKeys, "DefaultMaxRequestDuration")
result.DefaultMaxRequestDurationRaw = nil
}
if result.DisableMlockRaw != nil {
if result.DisableMlock, err = parseutil.ParseBool(result.DisableMlockRaw); err != nil {
return nil, err
}
result.FoundKeys = append(result.FoundKeys, "DisableMlock")
result.DisableMlockRaw = nil
}
list, ok := obj.Node.(*ast.ObjectList)
if !ok {
return nil, fmt.Errorf("error parsing: file doesn't contain a root object")
}
if o := list.Filter("hsm"); len(o.Items) > 0 {
result.found("hsm", "hsm")
if err := parseKMS(&result.Seals, o, "hsm", 2); err != nil {
return nil, fmt.Errorf("error parsing 'hsm': %w", err)
}
}
if o := list.Filter("seal"); len(o.Items) > 0 {
result.found("seal", "Seal")
if err := parseKMS(&result.Seals, o, "seal", 3); err != nil {
return nil, fmt.Errorf("error parsing 'seal': %w", err)
}
}
if o := list.Filter("kms"); len(o.Items) > 0 {
result.found("kms", "Seal")
if err := parseKMS(&result.Seals, o, "kms", 3); err != nil {
return nil, fmt.Errorf("error parsing 'kms': %w", err)
}
}
if o := list.Filter("entropy"); len(o.Items) > 0 {
result.found("entropy", "Entropy")
if err := ParseEntropy(&result, o, "entropy"); err != nil {
return nil, fmt.Errorf("error parsing 'entropy': %w", err)
}
}
if o := list.Filter("listener"); len(o.Items) > 0 {
result.found("listener", "Listener")
if err := ParseListeners(&result, o); err != nil {
return nil, fmt.Errorf("error parsing 'listener': %w", err)
}
}
if o := list.Filter("user_lockout"); len(o.Items) > 0 {
result.found("user_lockout", "UserLockout")
if err := ParseUserLockouts(&result, o); err != nil {
return nil, fmt.Errorf("error parsing 'user_lockout': %w", err)
}
}
if o := list.Filter("telemetry"); len(o.Items) > 0 {
result.found("telemetry", "Telemetry")
if err := parseTelemetry(&result, o); err != nil {
return nil, fmt.Errorf("error parsing 'telemetry': %w", err)
}
}
if o := list.Filter("cloud"); len(o.Items) > 0 {
result.found("cloud", "Cloud")
if err := parseCloud(&result, o); err != nil {
return nil, fmt.Errorf("error parsing 'cloud': %w", err)
}
}
entConfig := &(result.EntSharedConfig)
if err := entConfig.ParseConfig(list); err != nil {
return nil, fmt.Errorf("error parsing enterprise config: %w", err)
}
return &result, nil
}
// Sanitized returns a copy of the config with all values that are considered
// sensitive stripped. It also strips all `*Raw` values that are mainly
// used for parsing.
//
// Specifically, the fields that this method strips are:
// - KMS.Config
// - Telemetry.CirconusAPIToken
func (c *SharedConfig) Sanitized() map[string]interface{} {
if c == nil {
return nil
}
result := map[string]interface{}{
"default_max_request_duration": c.DefaultMaxRequestDuration,
"disable_mlock": c.DisableMlock,
"log_level": c.LogLevel,
"log_format": c.LogFormat,
"pid_file": c.PidFile,
"cluster_name": c.ClusterName,
"administrative_namespace_path": c.AdministrativeNamespacePath,
}
// Optional log related settings
if c.LogFile != "" {
result["log_file"] = c.LogFile
}
if c.LogRotateBytes != 0 {
result["log_rotate_bytes"] = c.LogRotateBytes
}
if c.LogRotateDuration != "" {
result["log_rotate_duration"] = c.LogRotateDuration
}
if c.LogRotateMaxFiles != 0 {
result["log_rotate_max_files"] = c.LogRotateMaxFiles
}
// Sanitize listeners
if len(c.Listeners) != 0 {
var sanitizedListeners []interface{}
for _, ln := range c.Listeners {
cleanLn := map[string]interface{}{
"type": ln.Type,
"config": ln.RawConfig,
}
sanitizedListeners = append(sanitizedListeners, cleanLn)
}
result["listeners"] = sanitizedListeners
}
// Sanitize user lockout stanza
if len(c.UserLockouts) != 0 {
var sanitizedUserLockouts []interface{}
for _, userlockout := range c.UserLockouts {
cleanUserLockout := map[string]interface{}{
"type": userlockout.Type,
"lockout_threshold": userlockout.LockoutThreshold,
"lockout_duration": userlockout.LockoutDuration,
"lockout_counter_reset": userlockout.LockoutCounterReset,
"disable_lockout": userlockout.DisableLockout,
}
sanitizedUserLockouts = append(sanitizedUserLockouts, cleanUserLockout)
}
result["user_lockout_configs"] = sanitizedUserLockouts
}
// Sanitize seals stanza
if len(c.Seals) != 0 {
var sanitizedSeals []interface{}
for _, s := range c.Seals {
cleanSeal := map[string]interface{}{
"type": s.Type,
"disabled": s.Disabled,
}
sanitizedSeals = append(sanitizedSeals, cleanSeal)
}
result["seals"] = sanitizedSeals
}
// Sanitize telemetry stanza
if c.Telemetry != nil {
sanitizedTelemetry := map[string]interface{}{
"statsite_address": c.Telemetry.StatsiteAddr,
"statsd_address": c.Telemetry.StatsdAddr,
"disable_hostname": c.Telemetry.DisableHostname,
"metrics_prefix": c.Telemetry.MetricsPrefix,
"usage_gauge_period": c.Telemetry.UsageGaugePeriod,
"maximum_gauge_cardinality": c.Telemetry.MaximumGaugeCardinality,
"circonus_api_token": "",
"circonus_api_app": c.Telemetry.CirconusAPIApp,
"circonus_api_url": c.Telemetry.CirconusAPIURL,
"circonus_submission_interval": c.Telemetry.CirconusSubmissionInterval,
"circonus_submission_url": c.Telemetry.CirconusCheckSubmissionURL,
"circonus_check_id": c.Telemetry.CirconusCheckID,
"circonus_check_force_metric_activation": c.Telemetry.CirconusCheckForceMetricActivation,
"circonus_check_instance_id": c.Telemetry.CirconusCheckInstanceID,
"circonus_check_search_tag": c.Telemetry.CirconusCheckSearchTag,
"circonus_check_tags": c.Telemetry.CirconusCheckTags,
"circonus_check_display_name": c.Telemetry.CirconusCheckDisplayName,
"circonus_broker_id": c.Telemetry.CirconusBrokerID,
"circonus_broker_select_tag": c.Telemetry.CirconusBrokerSelectTag,
"dogstatsd_addr": c.Telemetry.DogStatsDAddr,
"dogstatsd_tags": c.Telemetry.DogStatsDTags,
"prometheus_retention_time": c.Telemetry.PrometheusRetentionTime,
"stackdriver_project_id": c.Telemetry.StackdriverProjectID,
"stackdriver_location": c.Telemetry.StackdriverLocation,
"stackdriver_namespace": c.Telemetry.StackdriverNamespace,
"stackdriver_debug_logs": c.Telemetry.StackdriverDebugLogs,
"lease_metrics_epsilon": c.Telemetry.LeaseMetricsEpsilon,
"num_lease_metrics_buckets": c.Telemetry.NumLeaseMetricsTimeBuckets,
"add_lease_metrics_namespace_labels": c.Telemetry.LeaseMetricsNameSpaceLabels,
}
result["telemetry"] = sanitizedTelemetry
}
return result
}
func (c *SharedConfig) found(s, k string) {
delete(c.UnusedKeys, s)
c.FoundKeys = append(c.FoundKeys, k)
}