mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
config: add TTL to agent identity config (#18457)
Add support for identity token TTL in agent configuration fields such as Consul `service_identity` and `template_identity`. Co-authored-by: Michael Schurter <mschurter@hashicorp.com>
This commit is contained in:
@@ -72,6 +72,29 @@ func ParseConfigFile(path string) (*Config, error) {
|
||||
return nil, fmt.Errorf("failed to decode HCL file %s: %w", path, 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)
|
||||
}
|
||||
}
|
||||
matches = list.Filter("consul")
|
||||
if len(matches.Items) > 0 {
|
||||
if err := parseConsuls(c, matches); err != nil {
|
||||
return nil, fmt.Errorf("error parsing 'consul': %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// convert strings to time.Durations
|
||||
tds := []durationConversionMap{
|
||||
{"gc_interval", &c.Client.GCInterval, &c.Client.GCIntervalHCL, nil},
|
||||
@@ -152,6 +175,30 @@ func ParseConfigFile(path string) (*Config, error) {
|
||||
},
|
||||
}
|
||||
|
||||
// Parse durations for Consul and Vault config blocks if provided.
|
||||
//
|
||||
// Since the map of multiple cluster configuration contains a pointer to
|
||||
// the default block we don't need to parse it directly.
|
||||
for name, consulConfig := range c.Consuls {
|
||||
if consulConfig.ServiceIdentity != nil {
|
||||
tds = append(tds, durationConversionMap{
|
||||
fmt.Sprintf("consuls.%s.service_identity.ttl", name), nil, &consulConfig.ServiceIdentity.TTLHCL,
|
||||
func(d *time.Duration) {
|
||||
consulConfig.ServiceIdentity.TTL = d
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if consulConfig.TemplateIdentity != nil {
|
||||
tds = append(tds, durationConversionMap{
|
||||
fmt.Sprintf("consuls.%s.template_identity.ttl", name), nil, &consulConfig.TemplateIdentity.TTLHCL,
|
||||
func(d *time.Duration) {
|
||||
consulConfig.TemplateIdentity.TTL = d
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Add enterprise audit sinks for time.Duration parsing
|
||||
for i, sink := range c.Audit.Sinks {
|
||||
tds = append(tds, durationConversionMap{
|
||||
@@ -164,28 +211,6 @@ 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)
|
||||
}
|
||||
}
|
||||
matches = list.Filter("consul")
|
||||
if len(matches.Items) > 0 {
|
||||
if err := parseConsuls(c, matches); err != nil {
|
||||
return nil, fmt.Errorf("error parsing 'consul': %w", err)
|
||||
}
|
||||
}
|
||||
// report unexpected keys
|
||||
err = extraKeys(c)
|
||||
if err != nil {
|
||||
|
||||
@@ -240,11 +240,15 @@ var basicConfig = &Config{
|
||||
Audience: []string{"consul.io", "nomad.dev"},
|
||||
Env: pointer.Of(false),
|
||||
File: pointer.Of(true),
|
||||
TTL: pointer.Of(1 * time.Hour),
|
||||
TTLHCL: "1h",
|
||||
},
|
||||
TemplateIdentity: &config.WorkloadIdentityConfig{
|
||||
Audience: []string{"consul.io"},
|
||||
Env: pointer.Of(true),
|
||||
File: pointer.Of(false),
|
||||
TTL: pointer.Of(2 * time.Hour),
|
||||
TTLHCL: "2h",
|
||||
},
|
||||
},
|
||||
Consuls: map[string]*config.ConsulConfig{
|
||||
@@ -276,11 +280,15 @@ var basicConfig = &Config{
|
||||
Audience: []string{"consul.io", "nomad.dev"},
|
||||
Env: pointer.Of(false),
|
||||
File: pointer.Of(true),
|
||||
TTL: pointer.Of(1 * time.Hour),
|
||||
TTLHCL: "1h",
|
||||
},
|
||||
TemplateIdentity: &config.WorkloadIdentityConfig{
|
||||
Audience: []string{"consul.io"},
|
||||
Env: pointer.Of(true),
|
||||
File: pointer.Of(false),
|
||||
TTL: pointer.Of(2 * time.Hour),
|
||||
TTLHCL: "2h",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
2
command/agent/testdata/basic.hcl
vendored
2
command/agent/testdata/basic.hcl
vendored
@@ -248,11 +248,13 @@ consul {
|
||||
aud = ["consul.io", "nomad.dev"]
|
||||
env = false
|
||||
file = true
|
||||
ttl = "1h"
|
||||
}
|
||||
template_identity {
|
||||
aud = ["consul.io"]
|
||||
env = true
|
||||
file = false
|
||||
ttl = "2h"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
6
command/agent/testdata/basic.json
vendored
6
command/agent/testdata/basic.json
vendored
@@ -173,7 +173,8 @@
|
||||
"nomad.dev"
|
||||
],
|
||||
"env": false,
|
||||
"file": true
|
||||
"file": true,
|
||||
"ttl": "1h"
|
||||
},
|
||||
"ssl": true,
|
||||
"template_identity": {
|
||||
@@ -181,7 +182,8 @@
|
||||
"consul.io"
|
||||
],
|
||||
"env": true,
|
||||
"file": false
|
||||
"file": false,
|
||||
"ttl": "2h"
|
||||
},
|
||||
"timeout": "5s",
|
||||
"token": "token1",
|
||||
|
||||
@@ -508,6 +508,9 @@ func workloadIdentityFromConfig(widConfig *config.WorkloadIdentityConfig) *struc
|
||||
if widConfig.File != nil {
|
||||
wid.File = *widConfig.File
|
||||
}
|
||||
if widConfig.TTL != nil {
|
||||
wid.TTL = *widConfig.TTL
|
||||
}
|
||||
|
||||
return wid
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@ func TestConsulConfig_Merge(t *testing.T) {
|
||||
Audience: []string{"consul.io", "nomad.dev"},
|
||||
Env: pointer.Of(false),
|
||||
File: pointer.Of(true),
|
||||
TTL: pointer.Of(2 * time.Hour),
|
||||
},
|
||||
ExtraKeysHCL: []string{"b", "2"},
|
||||
}
|
||||
@@ -134,6 +135,7 @@ func TestConsulConfig_Merge(t *testing.T) {
|
||||
Audience: []string{"consul.io", "nomad.dev"},
|
||||
Env: pointer.Of(false),
|
||||
File: pointer.Of(true),
|
||||
TTL: pointer.Of(2 * time.Hour),
|
||||
},
|
||||
ExtraKeysHCL: []string{"a", "1"}, // not merged
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package config
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-set"
|
||||
"github.com/hashicorp/nomad/helper/pointer"
|
||||
@@ -31,6 +32,11 @@ type WorkloadIdentityConfig struct {
|
||||
// File writes the Workload Identity into the Task's secrets directory
|
||||
// if set.
|
||||
File *bool `mapstructure:"file"`
|
||||
|
||||
// TTL is used to determine the expiration of the credentials created for
|
||||
// this identity (eg the JWT "exp" claim).
|
||||
TTL *time.Duration `mapstructure:"-"`
|
||||
TTLHCL string `mapstructure:"ttl" json:"-"`
|
||||
}
|
||||
|
||||
func (wi *WorkloadIdentityConfig) Copy() *WorkloadIdentityConfig {
|
||||
@@ -68,6 +74,12 @@ func (wi *WorkloadIdentityConfig) Equal(other *WorkloadIdentityConfig) bool {
|
||||
if !pointer.Eq(wi.File, other.File) {
|
||||
return false
|
||||
}
|
||||
if !pointer.Eq(wi.TTL, other.TTL) {
|
||||
return false
|
||||
}
|
||||
if wi.TTLHCL != other.TTLHCL {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -94,6 +106,10 @@ func (wi *WorkloadIdentityConfig) Merge(other *WorkloadIdentityConfig) *Workload
|
||||
|
||||
result.Env = pointer.Merge(result.Env, other.Env)
|
||||
result.File = pointer.Merge(result.File, other.File)
|
||||
result.TTL = pointer.Merge(result.TTL, other.TTL)
|
||||
if other.TTLHCL != "" {
|
||||
result.TTLHCL = other.TTLHCL
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/nomad/ci"
|
||||
"github.com/hashicorp/nomad/helper/pointer"
|
||||
@@ -19,6 +20,7 @@ func TestWorkloadIdentityConfig_Copy(t *testing.T) {
|
||||
Audience: []string{"aud"},
|
||||
Env: pointer.Of(true),
|
||||
File: pointer.Of(false),
|
||||
TTL: pointer.Of(time.Hour),
|
||||
}
|
||||
|
||||
// Verify Copy() returns the same values but different pointer.
|
||||
@@ -31,6 +33,7 @@ func TestWorkloadIdentityConfig_Copy(t *testing.T) {
|
||||
clone.Audience = []string{"aud", "clone"}
|
||||
clone.Env = pointer.Of(false)
|
||||
clone.File = pointer.Of(true)
|
||||
clone.TTL = pointer.Of(time.Second)
|
||||
|
||||
must.NotEq(t, original, clone)
|
||||
must.NotEqOp(t, original, clone)
|
||||
@@ -52,12 +55,14 @@ func TestWorkloadIdentityConfig_Equal(t *testing.T) {
|
||||
Audience: []string{"aud"},
|
||||
Env: pointer.Of(true),
|
||||
File: pointer.Of(false),
|
||||
TTL: pointer.Of(time.Hour),
|
||||
},
|
||||
b: &WorkloadIdentityConfig{
|
||||
Name: "test",
|
||||
Audience: []string{"aud"},
|
||||
Env: pointer.Of(true),
|
||||
File: pointer.Of(false),
|
||||
TTL: pointer.Of(time.Hour),
|
||||
},
|
||||
expectEq: true,
|
||||
},
|
||||
@@ -121,6 +126,16 @@ func TestWorkloadIdentityConfig_Equal(t *testing.T) {
|
||||
},
|
||||
expectEq: false,
|
||||
},
|
||||
{
|
||||
name: "different ttl",
|
||||
a: &WorkloadIdentityConfig{
|
||||
TTL: pointer.Of(time.Hour),
|
||||
},
|
||||
b: &WorkloadIdentityConfig{
|
||||
TTL: pointer.Of(time.Minute),
|
||||
},
|
||||
expectEq: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
@@ -152,6 +167,7 @@ func TestWorkloadIdentityConfig_Merge(t *testing.T) {
|
||||
Audience: []string{"aud"},
|
||||
Env: pointer.Of(true),
|
||||
File: pointer.Of(false),
|
||||
TTL: pointer.Of(time.Hour),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -164,6 +180,7 @@ func TestWorkloadIdentityConfig_Merge(t *testing.T) {
|
||||
Audience: []string{"aud", "other"},
|
||||
Env: pointer.Of(true),
|
||||
File: pointer.Of(false),
|
||||
TTL: pointer.Of(time.Hour),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -176,6 +193,7 @@ func TestWorkloadIdentityConfig_Merge(t *testing.T) {
|
||||
Audience: []string{"aud"},
|
||||
Env: pointer.Of(false),
|
||||
File: pointer.Of(false),
|
||||
TTL: pointer.Of(time.Hour),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -188,6 +206,20 @@ func TestWorkloadIdentityConfig_Merge(t *testing.T) {
|
||||
Audience: []string{"aud"},
|
||||
Env: pointer.Of(true),
|
||||
File: pointer.Of(true),
|
||||
TTL: pointer.Of(time.Hour),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "merge ttl",
|
||||
other: &WorkloadIdentityConfig{
|
||||
TTL: pointer.Of(time.Second),
|
||||
},
|
||||
expected: &WorkloadIdentityConfig{
|
||||
Name: "test",
|
||||
Audience: []string{"aud"},
|
||||
Env: pointer.Of(true),
|
||||
File: pointer.Of(false),
|
||||
TTL: pointer.Of(time.Second),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -199,6 +231,7 @@ func TestWorkloadIdentityConfig_Merge(t *testing.T) {
|
||||
Audience: []string{"aud"},
|
||||
Env: pointer.Of(true),
|
||||
File: pointer.Of(false),
|
||||
TTL: pointer.Of(time.Hour),
|
||||
}
|
||||
got := original.Merge(tc.other)
|
||||
must.Eq(t, tc.expected, got)
|
||||
|
||||
Reference in New Issue
Block a user