Skip to content

Commit

Permalink
Add annotation to use template path on disk
Browse files Browse the repository at this point in the history
We currently have two options to define vault agent templates:
either define the template configuration as an inline template in the
annotation or configure the vault agent directly. The former is really
not handy when template is getting complex, the latter forces us to
manage the whole vault agent configuration.

We add a new annotation that enables the vault agent to inject secrets
from a template file on the container disk. Since
hashicorp#212, this template can be
present in volume defined on the container.

Annotation example:

```yaml
vault.hashicorp.com/agent-inject-secret-foo: 'database/roles/app'
vault.hashicorp.com/agent-inject-template-source-foo: '/etc/my-app/config.toml.tmpl'
vault.hashicorp.com/agent-inject-file-foo: '/etc/my-app/config.toml',
vault.hashicorp.com/agent-copy-volume-mounts: 'MyContainerNameWithVolumes'
```

If a template content is also defined in annotation (using
`vault.hashicorp.com/agent-inject-template`, the template on disk won't be used.

refs hashicorp#84
  • Loading branch information
jfroche committed Feb 15, 2021
1 parent c3f8d0f commit edc11f2
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 5 deletions.
3 changes: 3 additions & 0 deletions agent-inject/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ type Secret struct {
// Template is the optional custom template to use when rendering the secret.
Template string

// Template source is the optional path on disk to the custom template to use when rendering the secret.
TemplateSource string

// Mount Path for the volume holding the rendered secret file
MountPath string

Expand Down
15 changes: 15 additions & 0 deletions agent-inject/agent/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ const (
// If not provided, a default generic template is used.
AnnotationAgentInjectTemplate = "vault.hashicorp.com/agent-inject-template"

// AnnotationAgentInjectTemplateSource is the optional key annotation that configures Vault
// Agent what template on disk to use for rendering the secrets. The name
// of the template is any unique string after "vault.hashicorp.com/agent-inject-template-source-",
// such as "vault.hashicorp.com/agent-inject-template-source-foobar". This should map
// to the same unique value provided in "vault.hashicorp.com/agent-inject-secret-".
// The value is the filename and path of the template used by the agent to render the secrets.
// If not provided, the template content key annotation is used.
AnnotationAgentInjectTemplateSource = "vault.hashicorp.com/agent-inject-template-source"

// AnnotationAgentInjectToken is the annotation key for injecting the token
// from auth/token/lookup-self
AnnotationAgentInjectToken = "vault.hashicorp.com/agent-inject-token"
Expand Down Expand Up @@ -379,6 +388,12 @@ func (a *Agent) secrets() []*Secret {
if val, ok := a.Annotations[templateName]; ok {
s.Template = val
}
if s.Template == "" {
templateSourceAnnotation := fmt.Sprintf("%s-%s", AnnotationAgentInjectTemplateSource, raw)
if val, ok := a.Annotations[templateSourceAnnotation]; ok {
s.TemplateSource = val
}
}

s.MountPath = a.Annotations[AnnotationVaultSecretVolumePath]
mountPathAnnotationName := fmt.Sprintf("%s-%s", AnnotationVaultSecretVolumePath, raw)
Expand Down
60 changes: 60 additions & 0 deletions agent-inject/agent/annotations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,66 @@ func TestTemplateShortcuts(t *testing.T) {
}
}

func TestSecretTemplateSourceAnnotations(t *testing.T) {
tests := []struct {
annotations map[string]string
expectedKey string
expectedTemplate string
expectedTemplateSource string
}{
{
map[string]string{
"vault.hashicorp.com/agent-inject-secret-foobar": "test1",
"vault.hashicorp.com/agent-inject-template-foobar": "foobarTemplate",
"vault.hashicorp.com/agent-inject-template-source-foobar": "/etc/config.tmpl",
}, "foobar", "foobarTemplate", "",
},
{
map[string]string{
"vault.hashicorp.com/agent-inject-secret-foobar": "test1",
"vault.hashicorp.com/agent-inject-template-foobar": "",
"vault.hashicorp.com/agent-inject-template-source-foobar": "/etc/config.tmpl",
}, "foobar", "", "/etc/config.tmpl",
},
}

for _, tt := range tests {
pod := testPod(tt.annotations)
var patches []*jsonpatch.JsonPatchOperation

agentConfig := AgentConfig{
"", "http://foobar:8200", "test", "test", true, "100", "1000",
DefaultAgentRunAsSameUser, DefaultAgentSetSecurityContext,
}
err := Init(pod, agentConfig)
if err != nil {
t.Errorf("got error, shouldn't have: %s", err)
}

agent, err := New(pod, patches)
if err != nil {
t.Errorf("got error, shouldn't have: %s", err)
}

if len(agent.Secrets) == 0 {
t.Error("Secrets length was zero, it shouldn't have been")
}

if agent.Secrets[0].Name != tt.expectedKey {
t.Errorf("expected name %s, got %s", tt.expectedKey, agent.Secrets[0].Name)
}

if agent.Secrets[0].Template != tt.expectedTemplate {
t.Errorf("expected template %s, got %s", tt.expectedTemplate, agent.Secrets[0].Template)
}

if agent.Secrets[0].TemplateSource != tt.expectedTemplateSource {
t.Errorf("expected template source path %s, got %s", tt.expectedTemplateSource, agent.Secrets[0].TemplateSource)
}

}
}

func TestSecretCommandAnnotations(t *testing.T) {
tests := []struct {
annotations map[string]string
Expand Down
12 changes: 9 additions & 3 deletions agent-inject/agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,11 @@ type Sink struct {
type Template struct {
CreateDestDirs bool `json:"create_dest_dirs,omitempty"`
Destination string `json:"destination"`
Contents string `json:"contents"`
Contents string `json:"contents,omitempty"`
LeftDelim string `json:"left_delimiter,omitempty"`
RightDelim string `json:"right_delimiter,omitempty"`
Command string `json:"command,omitempty"`
Source string `json:"source,omitempty"`
}

// Listener defines the configuration for Vault Agent Cache Listener
Expand All @@ -92,8 +93,12 @@ func (a *Agent) newTemplateConfigs() []*Template {
var templates []*Template
for _, secret := range a.Secrets {
template := secret.Template
if template == "" {
template = fmt.Sprintf(DefaultTemplate, secret.Path)
templateSource := secret.TemplateSource
if templateSource == "" {
template = secret.Template
if template == "" {
template = fmt.Sprintf(DefaultTemplate, secret.Path)
}
}

filePathAndName := fmt.Sprintf("%s/%s", secret.MountPath, secret.Name)
Expand All @@ -102,6 +107,7 @@ func (a *Agent) newTemplateConfigs() []*Template {
}

tmpl := &Template{
Source: templateSource,
Contents: template,
Destination: filePathAndName,
LeftDelim: "{{",
Expand Down
15 changes: 13 additions & 2 deletions agent-inject/agent/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ func TestNewConfig(t *testing.T) {
"vault.hashicorp.com/agent-inject-secret-different-path": "different-path",
fmt.Sprintf("%s-%s", AnnotationVaultSecretVolumePath, "different-path"): "/etc/container_environment",

// render this secret from a template on disk
"vault.hashicorp.com/agent-inject-secret-with-source-template": "with-source-template",
fmt.Sprintf("%s-%s", AnnotationAgentInjectTemplateSource, "with-source-template"): "/etc/source-template",

"vault.hashicorp.com/agent-inject-command-bar": "pkill -HUP app",

AnnotationAgentCacheEnable: "true",
Expand Down Expand Up @@ -108,8 +112,8 @@ func TestNewConfig(t *testing.T) {
t.Error("agent Cache should be disabled for init containers")
}

if len(config.Templates) != 3 {
t.Errorf("expected 3 template, got %d", len(config.Templates))
if len(config.Templates) != 4 {
t.Errorf("expected 4 template, got %d", len(config.Templates))
}

for _, template := range config.Templates {
Expand All @@ -136,6 +140,13 @@ func TestNewConfig(t *testing.T) {
if template.Destination != "/etc/container_environment/different-path" {
t.Errorf("expected template destination to be %s, got %s", "/etc/container_environment", template.Destination)
}
} else if strings.Contains(template.Destination, "with-source-template") {
if template.Source != "/etc/source-template" {
t.Errorf("expected template source path to be %s, got %s", "/etc/source-template", template.Source)
}
if template.Contents != "" {
t.Errorf("expected template contents to be empty, got %s", template.Contents)
}
} else {
t.Error("shouldn't have got here")
}
Expand Down

0 comments on commit edc11f2

Please sign in to comment.