mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
config: parsing support for multiple Vault clusters in agent config (#18224)
Add the plumbing we need to accept multiple Vault clusters in Nomad agent configuration, to support upcoming Nomad Enterprise features. The `vault` blocks are differentiated by a new `name` field, and if the `name` is omitted it becomes the "default" Vault configuration. All blocks with the same name are merged together, as with the existing behavior. Unfortunately we're still using HCL1 for parsing configuration and the `Decode` method doesn't parse multiple blocks differentiated only by a field name without a label. So we've had to add an extra parsing pass, similar to what we've done for HCL1 jobspecs. For now, all existing consumers will use the "default" Vault configuration, so there's no user-facing behavior change in this changeset other than the contents of the agent self API. Ref: https://github.com/hashicorp/team-nomad/issues/404
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/hashicorp/nomad/client/lib/numalib"
|
||||
"github.com/hashicorp/nomad/client/state"
|
||||
"github.com/hashicorp/nomad/command/agent/host"
|
||||
"github.com/hashicorp/nomad/helper"
|
||||
"github.com/hashicorp/nomad/helper/bufconndialer"
|
||||
"github.com/hashicorp/nomad/helper/pluginutils/loader"
|
||||
"github.com/hashicorp/nomad/helper/pointer"
|
||||
@@ -171,9 +172,14 @@ type Config struct {
|
||||
// ConsulConfig is this Agent's Consul configuration
|
||||
ConsulConfig *structsc.ConsulConfig
|
||||
|
||||
// VaultConfig is this Agent's Vault configuration
|
||||
// VaultConfig is this Agent's default Vault configuration
|
||||
VaultConfig *structsc.VaultConfig
|
||||
|
||||
// VaultConfigs is a map of Vault configurations, here to support features
|
||||
// in Nomad Enterprise. The default Vault config pointer above will be found
|
||||
// in this map under the name "default"
|
||||
VaultConfigs map[string]*structsc.VaultConfig
|
||||
|
||||
// StatsCollectionInterval is the interval at which the Nomad client
|
||||
// collects resource usage stats
|
||||
StatsCollectionInterval time.Duration
|
||||
@@ -748,6 +754,7 @@ func (c *Config) Copy() *Config {
|
||||
nc.HostVolumes = structs.CopyMapStringClientHostVolumeConfig(nc.HostVolumes)
|
||||
nc.ConsulConfig = c.ConsulConfig.Copy()
|
||||
nc.VaultConfig = c.VaultConfig.Copy()
|
||||
nc.VaultConfigs = helper.DeepCopyMap(c.VaultConfigs)
|
||||
nc.TemplateConfig = c.TemplateConfig.Copy()
|
||||
nc.ReservableCores = slices.Clone(c.ReservableCores)
|
||||
nc.Artifact = c.Artifact.Copy()
|
||||
@@ -756,7 +763,7 @@ func (c *Config) Copy() *Config {
|
||||
|
||||
// DefaultConfig returns the default configuration
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
cfg := &Config{
|
||||
Version: version.GetVersion(),
|
||||
VaultConfig: structsc.DefaultVaultConfig(),
|
||||
ConsulConfig: structsc.DefaultConsulConfig(),
|
||||
@@ -798,6 +805,10 @@ func DefaultConfig() *Config {
|
||||
MaxDynamicPort: structs.DefaultMinDynamicPort,
|
||||
MinDynamicPort: structs.DefaultMaxDynamicPort,
|
||||
}
|
||||
|
||||
cfg.VaultConfigs = map[string]*structsc.VaultConfig{
|
||||
"default": cfg.VaultConfig}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Read returns the specified configuration value or "".
|
||||
|
||||
@@ -69,6 +69,7 @@ func TestClientConfig(t testing.T) (*Config, func()) {
|
||||
conf.CgroupParent = "testing.slice"
|
||||
|
||||
conf.VaultConfig.Enabled = pointer.Of(false)
|
||||
conf.VaultConfigs["default"].Enabled = pointer.Of(false)
|
||||
conf.DevMode = true
|
||||
|
||||
// Loosen GC threshold
|
||||
|
||||
@@ -507,7 +507,11 @@ func convertServerConfig(agentConfig *Config) (*nomad.Config, error) {
|
||||
|
||||
// Add the Consul and Vault configs
|
||||
conf.ConsulConfig = agentConfig.Consul
|
||||
|
||||
conf.VaultConfig = agentConfig.Vault
|
||||
for _, vaultConfig := range agentConfig.Vaults {
|
||||
conf.VaultConfigs[vaultConfig.Name] = vaultConfig
|
||||
}
|
||||
|
||||
// Set the TLS config
|
||||
conf.TLSConfig = agentConfig.TLSConfig
|
||||
@@ -799,7 +803,11 @@ func convertClientConfig(agentConfig *Config) (*clientconfig.Config, error) {
|
||||
}
|
||||
|
||||
conf.ConsulConfig = agentConfig.Consul
|
||||
|
||||
conf.VaultConfig = agentConfig.Vault
|
||||
for _, vaultConfig := range agentConfig.Vaults {
|
||||
conf.VaultConfigs[vaultConfig.Name] = vaultConfig
|
||||
}
|
||||
|
||||
// Set up Telemetry configuration
|
||||
conf.StatsCollectionInterval = agentConfig.Telemetry.collectionInterval
|
||||
|
||||
@@ -89,6 +89,11 @@ func (s *HTTPServer) AgentSelfRequest(resp http.ResponseWriter, req *http.Reques
|
||||
if self.Config != nil && self.Config.Vault != nil && self.Config.Vault.Token != "" {
|
||||
self.Config.Vault.Token = "<redacted>"
|
||||
}
|
||||
for _, vaultConfig := range self.Config.Vaults {
|
||||
if vaultConfig.Token != "" {
|
||||
vaultConfig.Token = "<redacted>"
|
||||
}
|
||||
}
|
||||
|
||||
if self.Config != nil && self.Config.ACL != nil && self.Config.ACL.ReplicationToken != "" {
|
||||
self.Config.ACL.ReplicationToken = "<redacted>"
|
||||
|
||||
@@ -77,6 +77,7 @@ func (c *Command) readConfig() *Config {
|
||||
ACL: &ACLConfig{},
|
||||
Audit: &config.AuditConfig{},
|
||||
}
|
||||
cmdConfig.Vaults = map[string]*config.VaultConfig{"default": cmdConfig.Vault}
|
||||
|
||||
flags := flag.NewFlagSet("agent", flag.ContinueOnError)
|
||||
flags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||
|
||||
@@ -136,9 +136,17 @@ type Config struct {
|
||||
// discover the current Nomad servers.
|
||||
Consul *config.ConsulConfig `hcl:"consul"`
|
||||
|
||||
// Vault contains the configuration for the Vault Agent and
|
||||
// Vault contains the configuration for the default Vault Agent and
|
||||
// parameters necessary to derive tokens.
|
||||
Vault *config.VaultConfig `hcl:"vault"`
|
||||
//
|
||||
// TODO(tgross): we'll probably want to remove this field once we've added a
|
||||
// selector so that we don't have to maintain it
|
||||
Vault *config.VaultConfig
|
||||
|
||||
// Vaults is a map derived from multiple `vault` blocks, here to support
|
||||
// features in Nomad Enterprise. The default Vault config pointer above will
|
||||
// be found in this map under the name "default"
|
||||
Vaults map[string]*config.VaultConfig
|
||||
|
||||
// UI is used to configure the web UI
|
||||
UI *config.UIConfig `hcl:"ui"`
|
||||
@@ -1264,7 +1272,7 @@ func DevConfig(mode *devModeConfig) *Config {
|
||||
|
||||
// DefaultConfig is the baseline configuration for Nomad.
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
cfg := &Config{
|
||||
LogLevel: "INFO",
|
||||
Region: "global",
|
||||
Datacenter: "dc1",
|
||||
@@ -1355,6 +1363,9 @@ func DefaultConfig() *Config {
|
||||
DisableUpdateCheck: pointer.Of(false),
|
||||
Limits: config.DefaultLimits(),
|
||||
}
|
||||
|
||||
cfg.Vaults = map[string]*config.VaultConfig{"default": cfg.Vault}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Listener can be used to get a new listener using a custom bind address.
|
||||
@@ -1522,13 +1533,9 @@ func (c *Config) Merge(b *Config) *Config {
|
||||
result.Consul = result.Consul.Merge(b.Consul)
|
||||
}
|
||||
|
||||
// Apply the Vault Configuration
|
||||
if result.Vault == nil && b.Vault != nil {
|
||||
vaultConfig := *b.Vault
|
||||
result.Vault = &vaultConfig
|
||||
} else if b.Vault != nil {
|
||||
result.Vault = result.Vault.Merge(b.Vault)
|
||||
}
|
||||
// Apply the Vault Configurations and overwrite the default Vault config
|
||||
result.Vaults = mergeVaultConfigs(result.Vaults, b.Vaults)
|
||||
result.Vault = result.Vaults["default"]
|
||||
|
||||
// Apply the UI Configuration
|
||||
if result.UI == nil && b.UI != nil {
|
||||
@@ -1579,6 +1586,21 @@ func (c *Config) Merge(b *Config) *Config {
|
||||
return &result
|
||||
}
|
||||
|
||||
func mergeVaultConfigs(left, right map[string]*config.VaultConfig) map[string]*config.VaultConfig {
|
||||
merged := helper.DeepCopyMap(left)
|
||||
if left == nil {
|
||||
merged = map[string]*config.VaultConfig{}
|
||||
}
|
||||
for name, rConfig := range right {
|
||||
if lConfig, ok := left[name]; ok {
|
||||
merged[name] = lConfig.Merge(rConfig)
|
||||
} else {
|
||||
merged[name] = rConfig.Copy()
|
||||
}
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
// Copy returns a deep copy safe for mutation.
|
||||
func (c *Config) Copy() *Config {
|
||||
if c == nil {
|
||||
@@ -1597,6 +1619,7 @@ func (c *Config) Copy() *Config {
|
||||
nc.DisableUpdateCheck = pointer.Copy(c.DisableUpdateCheck)
|
||||
nc.Consul = c.Consul.Copy()
|
||||
nc.Vault = c.Vault.Copy()
|
||||
nc.Vaults = helper.DeepCopyMap(c.Vaults)
|
||||
nc.UI = c.UI.Copy()
|
||||
|
||||
nc.NomadConfig = c.NomadConfig.Copy()
|
||||
|
||||
@@ -12,9 +12,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/hcl"
|
||||
"github.com/hashicorp/hcl/hcl/ast"
|
||||
client "github.com/hashicorp/nomad/client/config"
|
||||
"github.com/hashicorp/nomad/helper"
|
||||
"github.com/hashicorp/nomad/nomad/structs/config"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// ParseConfigFile returns an agent.Config from parsed from a file.
|
||||
@@ -57,6 +59,7 @@ func ParseConfigFile(path string) (*Config, error) {
|
||||
Autopilot: &config.AutopilotConfig{},
|
||||
Telemetry: &Telemetry{},
|
||||
Vault: &config.VaultConfig{},
|
||||
Vaults: map[string]*config.VaultConfig{},
|
||||
}
|
||||
|
||||
err = hcl.Decode(c, buf.String())
|
||||
@@ -156,6 +159,23 @@ func ParseConfigFile(path string) (*Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Re-parse the file to extract the multiple Vault configurations, which we
|
||||
// need to parse by hand because we don't have a label on the block
|
||||
root, err := hcl.Parse(buf.String())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse HCL file %s: %w", path, err)
|
||||
}
|
||||
list, ok := root.Node.(*ast.ObjectList)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("error parsing: root should be an object")
|
||||
}
|
||||
matches := list.Filter("vault")
|
||||
if len(matches.Items) > 0 {
|
||||
if err := parseVaults(c, matches); err != nil {
|
||||
return nil, fmt.Errorf("error parsing 'vault': %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// report unexpected keys
|
||||
err = extraKeys(c)
|
||||
if err != nil {
|
||||
@@ -261,6 +281,10 @@ func extraKeys(c *Config) error {
|
||||
helper.RemoveEqualFold(&c.ExtraKeysHCL, "telemetry")
|
||||
}
|
||||
|
||||
// The `vault` blocks are parsed separately from the Decode method, so it
|
||||
// will incorrectly report them as extra keys
|
||||
helper.RemoveEqualFold(&c.ExtraKeysHCL, "vault")
|
||||
|
||||
return helper.UnusedKeys(c)
|
||||
}
|
||||
|
||||
@@ -293,3 +317,35 @@ func finalizeClientTemplateConfig(config *Config) {
|
||||
config.Client.TemplateConfig = nil
|
||||
}
|
||||
}
|
||||
|
||||
// parseVaults decodes the `vault` blocks. The hcl.Decode method can't parse
|
||||
// these correctly as HCL1 because they don't have labels, which would result in
|
||||
// all the blocks getting merged regardless of name.
|
||||
func parseVaults(c *Config, list *ast.ObjectList) error {
|
||||
if len(list.Items) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, obj := range list.Items {
|
||||
var m map[string]interface{}
|
||||
if err := hcl.DecodeObject(&m, obj.Val); err != nil {
|
||||
return err
|
||||
}
|
||||
v := &config.VaultConfig{}
|
||||
err := mapstructure.WeakDecode(m, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if v.Name == "" {
|
||||
v.Name = "default"
|
||||
}
|
||||
if exist, ok := c.Vaults[v.Name]; ok {
|
||||
c.Vaults[v.Name] = exist.Merge(v)
|
||||
} else {
|
||||
c.Vaults[v.Name] = v
|
||||
}
|
||||
}
|
||||
|
||||
c.Vault = c.Vaults["default"]
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/hashicorp/nomad/helper/pointer"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
"github.com/hashicorp/nomad/nomad/structs/config"
|
||||
"github.com/shoenig/test/must"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -235,6 +236,7 @@ var basicConfig = &Config{
|
||||
TimeoutHCL: "5s",
|
||||
},
|
||||
Vault: &config.VaultConfig{
|
||||
Name: "default",
|
||||
Addr: "127.0.0.1:9500",
|
||||
AllowUnauthenticated: &trueValue,
|
||||
ConnectionRetryIntv: config.DefaultVaultConnectRetryIntv,
|
||||
@@ -249,6 +251,24 @@ var basicConfig = &Config{
|
||||
TaskTokenTTL: "1s",
|
||||
Token: "12345",
|
||||
},
|
||||
Vaults: map[string]*config.VaultConfig{
|
||||
"default": {
|
||||
Name: "default",
|
||||
Addr: "127.0.0.1:9500",
|
||||
AllowUnauthenticated: &trueValue,
|
||||
ConnectionRetryIntv: config.DefaultVaultConnectRetryIntv,
|
||||
Enabled: &falseValue,
|
||||
Role: "test_role",
|
||||
TLSCaFile: "/path/to/ca/file",
|
||||
TLSCaPath: "/path/to/ca",
|
||||
TLSCertFile: "/path/to/cert/file",
|
||||
TLSKeyFile: "/path/to/key/file",
|
||||
TLSServerName: "foobar",
|
||||
TLSSkipVerify: &trueValue,
|
||||
TaskTokenTTL: "1s",
|
||||
Token: "12345",
|
||||
},
|
||||
},
|
||||
TLSConfig: &config.TLSConfig{
|
||||
EnableHTTP: true,
|
||||
EnableRPC: true,
|
||||
@@ -360,6 +380,7 @@ var pluginConfig = &Config{
|
||||
DisableAnonymousSignature: false,
|
||||
Consul: nil,
|
||||
Vault: nil,
|
||||
Vaults: map[string]*config.VaultConfig{},
|
||||
TLSConfig: nil,
|
||||
HTTPAPIResponseHeaders: map[string]string{},
|
||||
Sentinel: nil,
|
||||
@@ -512,6 +533,7 @@ func TestConfig_Parse(t *testing.T) {
|
||||
oldDefault := &Config{
|
||||
Consul: config.DefaultConsulConfig(),
|
||||
Vault: config.DefaultVaultConfig(),
|
||||
Vaults: map[string]*config.VaultConfig{"default": config.DefaultVaultConfig()},
|
||||
Autopilot: config.DefaultAutopilotConfig(),
|
||||
}
|
||||
actual = oldDefault.Merge(actual)
|
||||
@@ -552,6 +574,7 @@ func (c *Config) addDefaults() {
|
||||
}
|
||||
if c.Vault == nil {
|
||||
c.Vault = config.DefaultVaultConfig()
|
||||
c.Vaults = map[string]*config.VaultConfig{"default": c.Vault}
|
||||
}
|
||||
if c.Telemetry == nil {
|
||||
c.Telemetry = &Telemetry{}
|
||||
@@ -694,10 +717,20 @@ var sample0 = &Config{
|
||||
ClientAutoJoin: pointer.Of(false),
|
||||
},
|
||||
Vault: &config.VaultConfig{
|
||||
Name: "default",
|
||||
Enabled: pointer.Of(true),
|
||||
Role: "nomad-cluster",
|
||||
Addr: "http://host.example.com:8200",
|
||||
},
|
||||
Vaults: map[string]*config.VaultConfig{
|
||||
"default": {
|
||||
Name: "default",
|
||||
Enabled: pointer.Of(true),
|
||||
Role: "nomad-cluster",
|
||||
Addr: "http://host.example.com:8200",
|
||||
},
|
||||
},
|
||||
|
||||
TLSConfig: &config.TLSConfig{
|
||||
EnableHTTP: true,
|
||||
EnableRPC: true,
|
||||
@@ -790,10 +823,19 @@ var sample1 = &Config{
|
||||
ClientAutoJoin: pointer.Of(false),
|
||||
},
|
||||
Vault: &config.VaultConfig{
|
||||
Name: "default",
|
||||
Enabled: pointer.Of(true),
|
||||
Role: "nomad-cluster",
|
||||
Addr: "http://host.example.com:8200",
|
||||
},
|
||||
Vaults: map[string]*config.VaultConfig{
|
||||
"default": {
|
||||
Name: "default",
|
||||
Enabled: pointer.Of(true),
|
||||
Role: "nomad-cluster",
|
||||
Addr: "http://host.example.com:8200",
|
||||
},
|
||||
},
|
||||
TLSConfig: &config.TLSConfig{
|
||||
EnableHTTP: true,
|
||||
EnableRPC: true,
|
||||
@@ -904,3 +946,51 @@ func permutations(arr []string) [][]string {
|
||||
helper(arr, len(arr))
|
||||
return res
|
||||
}
|
||||
|
||||
func TestConfig_MultipleVault(t *testing.T) {
|
||||
|
||||
// verify the default Vault config is set from the list
|
||||
cfg := DefaultConfig()
|
||||
must.Eq(t, "default", cfg.Vault.Name)
|
||||
must.Equal(t, config.DefaultVaultConfig(), cfg.Vault)
|
||||
must.Nil(t, cfg.Vault.Enabled) // unset
|
||||
must.Eq(t, "https://vault.service.consul:8200", cfg.Vault.Addr)
|
||||
must.Eq(t, "", cfg.Vault.Token)
|
||||
|
||||
must.MapLen(t, 1, cfg.Vaults)
|
||||
must.Equal(t, cfg.Vault, cfg.Vaults["default"])
|
||||
must.True(t, cfg.Vault == cfg.Vaults["default"]) // must be same pointer
|
||||
|
||||
// merge in the user's configuration
|
||||
fc, err := LoadConfig("testdata/basic.hcl")
|
||||
must.NoError(t, err)
|
||||
cfg = cfg.Merge(fc)
|
||||
|
||||
must.Eq(t, "default", cfg.Vault.Name)
|
||||
must.NotNil(t, cfg.Vault.Enabled, must.Sprint("override should set to non-nil"))
|
||||
must.False(t, *cfg.Vault.Enabled)
|
||||
must.Eq(t, "127.0.0.1:9500", cfg.Vault.Addr)
|
||||
must.Eq(t, "12345", cfg.Vault.Token)
|
||||
|
||||
must.MapLen(t, 1, cfg.Vaults)
|
||||
must.Equal(t, cfg.Vault, cfg.Vaults["default"])
|
||||
|
||||
// add an extra Vault config and override fields in the default
|
||||
fc, err = LoadConfig("testdata/extra-vault.hcl")
|
||||
must.NoError(t, err)
|
||||
|
||||
cfg = cfg.Merge(fc)
|
||||
|
||||
must.Eq(t, "default", cfg.Vault.Name)
|
||||
must.True(t, *cfg.Vault.Enabled)
|
||||
must.Eq(t, "127.0.0.1:9500", cfg.Vault.Addr)
|
||||
must.Eq(t, "abracadabra", cfg.Vault.Token)
|
||||
|
||||
must.MapLen(t, 2, cfg.Vaults)
|
||||
must.Equal(t, cfg.Vault, cfg.Vaults["default"])
|
||||
|
||||
must.Eq(t, "alternate", cfg.Vaults["alternate"].Name)
|
||||
must.True(t, *cfg.Vaults["alternate"].Enabled)
|
||||
must.Eq(t, "127.0.0.1:9501", cfg.Vaults["alternate"].Addr)
|
||||
must.Eq(t, "xyzzy", cfg.Vaults["alternate"].Token)
|
||||
}
|
||||
|
||||
@@ -184,6 +184,7 @@ func TestConfig_Merge(t *testing.T) {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
},
|
||||
Vault: &config.VaultConfig{
|
||||
Name: "default",
|
||||
Token: "1",
|
||||
AllowUnauthenticated: &falseValue,
|
||||
TaskTokenTTL: "1",
|
||||
@@ -195,6 +196,21 @@ func TestConfig_Merge(t *testing.T) {
|
||||
TLSSkipVerify: &falseValue,
|
||||
TLSServerName: "1",
|
||||
},
|
||||
Vaults: map[string]*config.VaultConfig{
|
||||
"default": {
|
||||
Name: "default",
|
||||
Token: "1",
|
||||
AllowUnauthenticated: &falseValue,
|
||||
TaskTokenTTL: "1",
|
||||
Addr: "1",
|
||||
TLSCaFile: "1",
|
||||
TLSCaPath: "1",
|
||||
TLSCertFile: "1",
|
||||
TLSKeyFile: "1",
|
||||
TLSSkipVerify: &falseValue,
|
||||
TLSServerName: "1",
|
||||
},
|
||||
},
|
||||
Consul: &config.ConsulConfig{
|
||||
ServerServiceName: "1",
|
||||
ClientServiceName: "1",
|
||||
@@ -393,6 +409,7 @@ func TestConfig_Merge(t *testing.T) {
|
||||
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
||||
},
|
||||
Vault: &config.VaultConfig{
|
||||
Name: "default",
|
||||
Token: "2",
|
||||
AllowUnauthenticated: &trueValue,
|
||||
TaskTokenTTL: "2",
|
||||
@@ -404,6 +421,21 @@ func TestConfig_Merge(t *testing.T) {
|
||||
TLSSkipVerify: &trueValue,
|
||||
TLSServerName: "2",
|
||||
},
|
||||
Vaults: map[string]*config.VaultConfig{
|
||||
"default": {
|
||||
Name: "default",
|
||||
Token: "2",
|
||||
AllowUnauthenticated: &trueValue,
|
||||
TaskTokenTTL: "2",
|
||||
Addr: "2",
|
||||
TLSCaFile: "2",
|
||||
TLSCaPath: "2",
|
||||
TLSCertFile: "2",
|
||||
TLSKeyFile: "2",
|
||||
TLSSkipVerify: &trueValue,
|
||||
TLSServerName: "2",
|
||||
},
|
||||
},
|
||||
Consul: &config.ConsulConfig{
|
||||
ServerServiceName: "2",
|
||||
ClientServiceName: "2",
|
||||
|
||||
25
command/agent/testdata/extra-vault.hcl
vendored
Normal file
25
command/agent/testdata/extra-vault.hcl
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# Copyright (c) HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
# this unnamed (default) config should merge cleanly onto the basic config
|
||||
vault {
|
||||
enabled = true
|
||||
token = "abracadabra"
|
||||
}
|
||||
|
||||
# this alternate config should be added as an extra vault config
|
||||
vault {
|
||||
name = "alternate"
|
||||
address = "127.0.0.1:9501"
|
||||
allow_unauthenticated = true
|
||||
task_token_ttl = "5s"
|
||||
enabled = true
|
||||
token = "xyzzy"
|
||||
ca_file = "/path/to/ca/file"
|
||||
ca_path = "/path/to/ca"
|
||||
cert_file = "/path/to/cert/file"
|
||||
key_file = "/path/to/key/file"
|
||||
tls_server_name = "barbaz"
|
||||
tls_skip_verify = true
|
||||
create_from_role = "test_role2"
|
||||
}
|
||||
1
command/agent/testdata/sample0.json
vendored
1
command/agent/testdata/sample0.json
vendored
@@ -82,6 +82,7 @@
|
||||
"verify_server_hostname": true
|
||||
},
|
||||
"vault": {
|
||||
"name": "default",
|
||||
"address": "http://host.example.com:8200",
|
||||
"create_from_role": "nomad-cluster",
|
||||
"enabled": true
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/hashicorp/memberlist"
|
||||
"github.com/hashicorp/nomad/helper"
|
||||
"github.com/hashicorp/nomad/helper/pointer"
|
||||
"github.com/hashicorp/nomad/helper/uuid"
|
||||
"github.com/hashicorp/nomad/nomad/deploymentwatcher"
|
||||
@@ -310,9 +311,14 @@ type Config struct {
|
||||
// ConsulConfig is this Agent's Consul configuration
|
||||
ConsulConfig *config.ConsulConfig
|
||||
|
||||
// VaultConfig is this Agent's Vault configuration
|
||||
// VaultConfig is this Agent's default Vault configuration
|
||||
VaultConfig *config.VaultConfig
|
||||
|
||||
// VaultConfigs is a map of Vault configurations, here to support features
|
||||
// in Nomad Enterprise. The default Vault config pointer above will be found
|
||||
// in this map under the name "default"
|
||||
VaultConfigs map[string]*config.VaultConfig
|
||||
|
||||
// RPCHoldTimeout is how long an RPC can be "held" before it is errored.
|
||||
// This is used to paper over a loss of leadership by instead holding RPCs,
|
||||
// so that the caller experiences a slow response rather than an error.
|
||||
@@ -442,6 +448,7 @@ func (c *Config) Copy() *Config {
|
||||
nc.EnabledSchedulers = slices.Clone(c.EnabledSchedulers)
|
||||
nc.ConsulConfig = c.ConsulConfig.Copy()
|
||||
nc.VaultConfig = c.VaultConfig.Copy()
|
||||
nc.VaultConfigs = helper.DeepCopyMap(c.VaultConfigs)
|
||||
nc.TLSConfig = c.TLSConfig.Copy()
|
||||
nc.SentinelConfig = c.SentinelConfig.Copy()
|
||||
nc.AutopilotConfig = c.AutopilotConfig.Copy()
|
||||
@@ -541,6 +548,8 @@ func DefaultConfig() *Config {
|
||||
JobTrackedVersions: structs.JobDefaultTrackedVersions,
|
||||
}
|
||||
|
||||
c.VaultConfigs = map[string]*config.VaultConfig{"default": c.VaultConfig}
|
||||
|
||||
// Enable all known schedulers by default
|
||||
c.EnabledSchedulers = make([]string, 0, len(scheduler.BuiltinSchedulers))
|
||||
for name := range scheduler.BuiltinSchedulers {
|
||||
|
||||
@@ -25,21 +25,22 @@ const (
|
||||
//
|
||||
// - Create child tokens with policy subsets of the Server's token.
|
||||
type VaultConfig struct {
|
||||
Name string `mapstructure:"name"`
|
||||
|
||||
// Enabled enables or disables Vault support.
|
||||
Enabled *bool `hcl:"enabled"`
|
||||
Enabled *bool `mapstructure:"enabled"`
|
||||
|
||||
// Token is the Vault token given to Nomad such that it can
|
||||
// derive child tokens. Nomad will renew this token at half its lease
|
||||
// lifetime.
|
||||
Token string `hcl:"token"`
|
||||
Token string `mapstructure:"token"`
|
||||
|
||||
// Role sets the role in which to create tokens from. The Token given to
|
||||
// Nomad does not have to be created from this role but must have "update"
|
||||
// capability on "auth/token/create/<create_from_role>". If this value is
|
||||
// unset and the token is created from a role, the value is defaulted to the
|
||||
// role the token is from.
|
||||
Role string `hcl:"create_from_role"`
|
||||
Role string `mapstructure:"create_from_role"`
|
||||
|
||||
// Namespace sets the Vault namespace used for all calls against the
|
||||
// Vault API. If this is unset, then Nomad does not use Vault namespaces.
|
||||
@@ -48,16 +49,16 @@ type VaultConfig struct {
|
||||
// AllowUnauthenticated allows users to submit jobs requiring Vault tokens
|
||||
// without providing a Vault token proving they have access to these
|
||||
// policies.
|
||||
AllowUnauthenticated *bool `hcl:"allow_unauthenticated"`
|
||||
AllowUnauthenticated *bool `mapstructure:"allow_unauthenticated"`
|
||||
|
||||
// TaskTokenTTL is the TTL of the tokens created by Nomad Servers and used
|
||||
// by the client. There should be a minimum time value such that the client
|
||||
// does not have to renew with Vault at a very high frequency
|
||||
TaskTokenTTL string `hcl:"task_token_ttl"`
|
||||
TaskTokenTTL string `mapstructure:"task_token_ttl"`
|
||||
|
||||
// Addr is the address of the local Vault agent. This should be a complete
|
||||
// URL such as "http://vault.example.com"
|
||||
Addr string `hcl:"address"`
|
||||
Addr string `mapstructure:"address"`
|
||||
|
||||
// ConnectionRetryIntv is the interval to wait before re-attempting to
|
||||
// connect to Vault.
|
||||
@@ -65,29 +66,30 @@ type VaultConfig struct {
|
||||
|
||||
// TLSCaFile is the path to a PEM-encoded CA cert file to use to verify the
|
||||
// Vault server SSL certificate.
|
||||
TLSCaFile string `hcl:"ca_file"`
|
||||
TLSCaFile string `mapstructure:"ca_file"`
|
||||
|
||||
// TLSCaFile is the path to a directory of PEM-encoded CA cert files to
|
||||
// verify the Vault server SSL certificate.
|
||||
TLSCaPath string `hcl:"ca_path"`
|
||||
TLSCaPath string `mapstructure:"ca_path"`
|
||||
|
||||
// TLSCertFile is the path to the certificate for Vault communication
|
||||
TLSCertFile string `hcl:"cert_file"`
|
||||
TLSCertFile string `mapstructure:"cert_file"`
|
||||
|
||||
// TLSKeyFile is the path to the private key for Vault communication
|
||||
TLSKeyFile string `hcl:"key_file"`
|
||||
TLSKeyFile string `mapstructure:"key_file"`
|
||||
|
||||
// TLSSkipVerify enables or disables SSL verification
|
||||
TLSSkipVerify *bool `hcl:"tls_skip_verify"`
|
||||
TLSSkipVerify *bool `mapstructure:"tls_skip_verify"`
|
||||
|
||||
// TLSServerName, if set, is used to set the SNI host when connecting via TLS.
|
||||
TLSServerName string `hcl:"tls_server_name"`
|
||||
TLSServerName string `mapstructure:"tls_server_name"`
|
||||
}
|
||||
|
||||
// DefaultVaultConfig returns the canonical defaults for the Nomad
|
||||
// `vault` configuration.
|
||||
func DefaultVaultConfig() *VaultConfig {
|
||||
return &VaultConfig{
|
||||
Name: "default",
|
||||
Addr: "https://vault.service.consul:8200",
|
||||
ConnectionRetryIntv: DefaultVaultConnectRetryIntv,
|
||||
AllowUnauthenticated: pointer.Of(true),
|
||||
@@ -109,6 +111,9 @@ func (c *VaultConfig) AllowsUnauthenticated() bool {
|
||||
func (c *VaultConfig) Merge(b *VaultConfig) *VaultConfig {
|
||||
result := *c
|
||||
|
||||
if b.Name != "" {
|
||||
c.Name = b.Name
|
||||
}
|
||||
if b.Enabled != nil {
|
||||
result.Enabled = b.Enabled
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user