Allow auto_auth with templates without specifying a sink (#8812)
For situations where you want the Vault agent to handle one or more templates but do not require the acquired credentials elsewhere. Modify the logic in SyncServer so that if there are no sinks, ignore any new credentials. Since SyncServer is responsible for shutting down the agent, make sure it still properly shuts down in this new situation. Solves #7988
This commit is contained in:
parent
1dd2113755
commit
3ce9615992
|
@ -156,8 +156,10 @@ func LoadConfig(path string) (*Config, error) {
|
|||
}
|
||||
|
||||
if result.AutoAuth != nil {
|
||||
if len(result.AutoAuth.Sinks) == 0 && (result.Cache == nil || !result.Cache.UseAutoAuthToken) {
|
||||
return nil, fmt.Errorf("auto_auth requires at least one sink or cache.use_auto_auth_token=true ")
|
||||
if len(result.AutoAuth.Sinks) == 0 &&
|
||||
(result.Cache == nil || !result.Cache.UseAutoAuthToken) &&
|
||||
len(result.Templates) == 0 {
|
||||
return nil, fmt.Errorf("auto_auth requires at least one sink or at least one template or cache.use_auto_auth_token=true")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -254,6 +254,13 @@ func TestLoadConfigFile_Bad_AutoAuth_Wrapped_Multiple_Sinks(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigFile_Bad_AutoAuth_Nosinks_Nocache_Notemplates(t *testing.T) {
|
||||
_, err := LoadConfig("./test-fixtures/bad-config-auto_auth-nosinks-nocache-notemplates.hcl")
|
||||
if err == nil {
|
||||
t.Fatal("LoadConfig should return an error when auto_auth configured and there are no sinks, caches or templates")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigFile_Bad_AutoAuth_Both_Wrapping_Types(t *testing.T) {
|
||||
_, err := LoadConfig("./test-fixtures/bad-config-method-wrapping-and-sink-wrapping.hcl")
|
||||
if err == nil {
|
||||
|
@ -539,3 +546,98 @@ func TestLoadConfigFile_Template(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestLoadConfigFile_Template_NoSinks tests template definitions without sinks in Vault Agent
|
||||
func TestLoadConfigFile_Template_NoSinks(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
fixturePath string
|
||||
expectedTemplates []*ctconfig.TemplateConfig
|
||||
}{
|
||||
"min": {
|
||||
fixturePath: "./test-fixtures/config-template-min-nosink.hcl",
|
||||
expectedTemplates: []*ctconfig.TemplateConfig{
|
||||
&ctconfig.TemplateConfig{
|
||||
Source: pointerutil.StringPtr("/path/on/disk/to/template.ctmpl"),
|
||||
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render.txt"),
|
||||
},
|
||||
},
|
||||
},
|
||||
"full": {
|
||||
fixturePath: "./test-fixtures/config-template-full-nosink.hcl",
|
||||
expectedTemplates: []*ctconfig.TemplateConfig{
|
||||
&ctconfig.TemplateConfig{
|
||||
Backup: pointerutil.BoolPtr(true),
|
||||
Command: pointerutil.StringPtr("restart service foo"),
|
||||
CommandTimeout: pointerutil.TimeDurationPtr("60s"),
|
||||
Contents: pointerutil.StringPtr("{{ keyOrDefault \"service/redis/maxconns@east-aws\" \"5\" }}"),
|
||||
CreateDestDirs: pointerutil.BoolPtr(true),
|
||||
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render.txt"),
|
||||
ErrMissingKey: pointerutil.BoolPtr(true),
|
||||
LeftDelim: pointerutil.StringPtr("<<"),
|
||||
Perms: pointerutil.FileModePtr(0655),
|
||||
RightDelim: pointerutil.StringPtr(">>"),
|
||||
SandboxPath: pointerutil.StringPtr("/path/on/disk/where"),
|
||||
|
||||
Wait: &ctconfig.WaitConfig{
|
||||
Min: pointerutil.TimeDurationPtr("10s"),
|
||||
Max: pointerutil.TimeDurationPtr("40s"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"many": {
|
||||
fixturePath: "./test-fixtures/config-template-many-nosink.hcl",
|
||||
expectedTemplates: []*ctconfig.TemplateConfig{
|
||||
&ctconfig.TemplateConfig{
|
||||
Source: pointerutil.StringPtr("/path/on/disk/to/template.ctmpl"),
|
||||
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render.txt"),
|
||||
ErrMissingKey: pointerutil.BoolPtr(false),
|
||||
CreateDestDirs: pointerutil.BoolPtr(true),
|
||||
Command: pointerutil.StringPtr("restart service foo"),
|
||||
Perms: pointerutil.FileModePtr(0600),
|
||||
},
|
||||
&ctconfig.TemplateConfig{
|
||||
Source: pointerutil.StringPtr("/path/on/disk/to/template2.ctmpl"),
|
||||
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render2.txt"),
|
||||
Backup: pointerutil.BoolPtr(true),
|
||||
Perms: pointerutil.FileModePtr(0755),
|
||||
Wait: &ctconfig.WaitConfig{
|
||||
Min: pointerutil.TimeDurationPtr("2s"),
|
||||
Max: pointerutil.TimeDurationPtr("10s"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
config, err := LoadConfig(tc.fixturePath)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
expected := &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
PidFile: "./pidfile",
|
||||
},
|
||||
AutoAuth: &AutoAuth{
|
||||
Method: &Method{
|
||||
Type: "aws",
|
||||
MountPath: "auth/aws",
|
||||
Namespace: "my-namespace/",
|
||||
Config: map[string]interface{}{
|
||||
"role": "foobar",
|
||||
},
|
||||
},
|
||||
Sinks: nil,
|
||||
},
|
||||
Templates: tc.expectedTemplates,
|
||||
}
|
||||
|
||||
if diff := deep.Equal(config, expected); diff != nil {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
pid_file = "./pidfile"
|
||||
|
||||
auto_auth {
|
||||
method "aws" {
|
||||
mount_path = "auth/aws"
|
||||
config = {
|
||||
role = "foobar"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
pid_file = "./pidfile"
|
||||
|
||||
auto_auth {
|
||||
method {
|
||||
type = "aws"
|
||||
namespace = "/my-namespace"
|
||||
|
||||
config = {
|
||||
role = "foobar"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template {
|
||||
destination = "/path/on/disk/where/template/will/render.txt"
|
||||
create_dest_dirs = true
|
||||
contents = "{{ keyOrDefault \"service/redis/maxconns@east-aws\" \"5\" }}"
|
||||
|
||||
command = "restart service foo"
|
||||
command_timeout = "60s"
|
||||
|
||||
error_on_missing_key = true
|
||||
perms = 0655
|
||||
backup = true
|
||||
left_delimiter = "<<"
|
||||
right_delimiter = ">>"
|
||||
|
||||
sandbox_path = "/path/on/disk/where"
|
||||
wait {
|
||||
min = "5s"
|
||||
max = "30s"
|
||||
}
|
||||
wait {
|
||||
min = "10s"
|
||||
max = "40s"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
pid_file = "./pidfile"
|
||||
|
||||
auto_auth {
|
||||
method {
|
||||
type = "aws"
|
||||
namespace = "/my-namespace"
|
||||
|
||||
config = {
|
||||
role = "foobar"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template {
|
||||
source = "/path/on/disk/to/template.ctmpl"
|
||||
destination = "/path/on/disk/where/template/will/render.txt"
|
||||
|
||||
create_dest_dirs = true
|
||||
|
||||
command = "restart service foo"
|
||||
|
||||
error_on_missing_key = false
|
||||
perms = 0600
|
||||
}
|
||||
|
||||
template {
|
||||
source = "/path/on/disk/to/template2.ctmpl"
|
||||
destination = "/path/on/disk/where/template/will/render2.txt"
|
||||
|
||||
perms = 0755
|
||||
|
||||
backup = true
|
||||
|
||||
wait {
|
||||
min = "2s"
|
||||
max = "10s"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
pid_file = "./pidfile"
|
||||
|
||||
auto_auth {
|
||||
method {
|
||||
type = "aws"
|
||||
namespace = "/my-namespace"
|
||||
|
||||
config = {
|
||||
role = "foobar"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template {
|
||||
source = "/path/on/disk/to/template.ctmpl"
|
||||
destination = "/path/on/disk/where/template/will/render.txt"
|
||||
}
|
|
@ -114,27 +114,34 @@ func (ss *SinkServer) Run(ctx context.Context, incoming chan string, sinks []*Si
|
|||
return
|
||||
|
||||
case token := <-incoming:
|
||||
if token != *latestToken {
|
||||
if len(sinks) > 0 {
|
||||
if token != *latestToken {
|
||||
|
||||
// Drain the existing funcs
|
||||
drainLoop:
|
||||
for {
|
||||
select {
|
||||
case <-sinkCh:
|
||||
atomic.AddInt32(ss.remaining, -1)
|
||||
default:
|
||||
break drainLoop
|
||||
// Drain the existing funcs
|
||||
drainLoop:
|
||||
for {
|
||||
select {
|
||||
case <-sinkCh:
|
||||
atomic.AddInt32(ss.remaining, -1)
|
||||
default:
|
||||
break drainLoop
|
||||
}
|
||||
}
|
||||
|
||||
*latestToken = token
|
||||
|
||||
for _, s := range sinks {
|
||||
atomic.AddInt32(ss.remaining, 1)
|
||||
sinkCh <- sinkToken{s, token}
|
||||
}
|
||||
}
|
||||
|
||||
*latestToken = token
|
||||
|
||||
for _, s := range sinks {
|
||||
atomic.AddInt32(ss.remaining, 1)
|
||||
sinkCh <- sinkToken{s, token}
|
||||
} else {
|
||||
ss.logger.Trace("no sinks, ignoring new token")
|
||||
if ss.exitAfterAuth {
|
||||
ss.logger.Trace("no sinks, exitAfterAuth, bye")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
case st := <-sinkCh:
|
||||
atomic.AddInt32(ss.remaining, -1)
|
||||
select {
|
||||
|
|
|
@ -489,10 +489,6 @@ func TestAgent_RequireRequestHeader(t *testing.T) {
|
|||
defer os.Remove(roleIDPath)
|
||||
defer os.Remove(secretIDPath)
|
||||
|
||||
// Get a temp file path we can use for the sink
|
||||
sinkPath := makeTempFile(t, "sink.txt", "")
|
||||
defer os.Remove(sinkPath)
|
||||
|
||||
// Create a config file
|
||||
config := `
|
||||
auto_auth {
|
||||
|
@ -530,7 +526,7 @@ listener "tcp" {
|
|||
require_request_header = true
|
||||
}
|
||||
`
|
||||
config = fmt.Sprintf(config, roleIDPath, secretIDPath, sinkPath)
|
||||
config = fmt.Sprintf(config, roleIDPath, secretIDPath)
|
||||
configPath := makeTempFile(t, "config.hcl", config)
|
||||
defer os.Remove(configPath)
|
||||
|
||||
|
@ -743,10 +739,6 @@ func TestAgent_Template_Basic(t *testing.T) {
|
|||
}`)
|
||||
request(t, serverClient, req, 200)
|
||||
|
||||
// Get a temp file path we can use for the sink
|
||||
sinkPath := makeTempFile(t, "sink.txt", "")
|
||||
defer os.Remove(sinkPath)
|
||||
|
||||
// make a temp directory to hold renders. Each test will create a temp dir
|
||||
// inside this one
|
||||
tmpDirRoot, err := ioutil.TempDir("", "agent-test-renders")
|
||||
|
@ -760,10 +752,6 @@ func TestAgent_Template_Basic(t *testing.T) {
|
|||
templateCount int
|
||||
exitAfterAuth bool
|
||||
}{
|
||||
"zero": {},
|
||||
"zero-with-exit": {
|
||||
exitAfterAuth: true,
|
||||
},
|
||||
"one": {
|
||||
templateCount: 1,
|
||||
},
|
||||
|
@ -819,12 +807,6 @@ auto_auth {
|
|||
remove_secret_id_file_after_reading = false
|
||||
}
|
||||
}
|
||||
|
||||
sink "file" {
|
||||
config = {
|
||||
path = "%s"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
%s
|
||||
|
@ -841,7 +823,7 @@ auto_auth {
|
|||
// flatten the template configs
|
||||
templateConfig := strings.Join(templateConfigStrings, " ")
|
||||
|
||||
config = fmt.Sprintf(config, serverClient.Address(), roleIDPath, secretIDPath, sinkPath, templateConfig, exitAfterAuth)
|
||||
config = fmt.Sprintf(config, serverClient.Address(), roleIDPath, secretIDPath, templateConfig, exitAfterAuth)
|
||||
configPath := makeTempFile(t, "config.hcl", config)
|
||||
defer os.Remove(configPath)
|
||||
|
||||
|
@ -1032,10 +1014,6 @@ func TestAgent_Template_ExitCounter(t *testing.T) {
|
|||
}`)
|
||||
request(t, serverClient, req, 200)
|
||||
|
||||
// Get a temp file path we can use for the sink
|
||||
sinkPath := makeTempFile(t, "sink.txt", "")
|
||||
defer os.Remove(sinkPath)
|
||||
|
||||
// make a temp directory to hold renders. Each test will create a temp dir
|
||||
// inside this one
|
||||
tmpDirRoot, err := ioutil.TempDir("", "agent-test-renders")
|
||||
|
@ -1066,12 +1044,6 @@ auto_auth {
|
|||
remove_secret_id_file_after_reading = false
|
||||
}
|
||||
}
|
||||
|
||||
sink "file" {
|
||||
config = {
|
||||
path = "%s"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template {
|
||||
|
@ -1100,7 +1072,7 @@ EOF
|
|||
exit_after_auth = true
|
||||
`
|
||||
|
||||
config = fmt.Sprintf(config, serverClient.Address(), roleIDPath, secretIDPath, sinkPath, tmpDir, tmpDir, tmpDir)
|
||||
config = fmt.Sprintf(config, serverClient.Address(), roleIDPath, secretIDPath, tmpDir, tmpDir, tmpDir)
|
||||
configPath := makeTempFile(t, "config.hcl", config)
|
||||
defer os.Remove(configPath)
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ wide variety of environments.
|
|||
## Functionality
|
||||
|
||||
Auto-Auth consists of two parts: a Method, which is the authentication method
|
||||
that should be used in the current environment; and one or more Sinks, which
|
||||
that should be used in the current environment; and any number of Sinks, which
|
||||
are locations where the agent should write a token any time the current token
|
||||
value has changed.
|
||||
|
||||
|
@ -103,7 +103,7 @@ The top level `auto_auth` block has two configuration entries:
|
|||
|
||||
- `method` `(object: required)` - Configuration for the method
|
||||
|
||||
- `sinks` `(array of objects: required)` - Configuration for the sinks
|
||||
- `sinks` `(array of objects: optional)` - Configuration for the sinks
|
||||
|
||||
### Configuration (Method)
|
||||
|
||||
|
|
|
@ -120,3 +120,7 @@ template {
|
|||
destination = "/tmp/agent/render.txt"
|
||||
}
|
||||
```
|
||||
|
||||
If you only want to use the Vault agent to render one or more templates and do
|
||||
not need to sink the acquired credentials, you can omit the `sink` stanza from
|
||||
the `auto_auth` stanza in the agent configuration.
|
||||
|
|
Loading…
Reference in New Issue