diff --git a/agent-inject/agent/agent.go b/agent-inject/agent/agent.go index e4aed610..4ec1e92d 100644 --- a/agent-inject/agent/agent.go +++ b/agent-inject/agent/agent.go @@ -182,6 +182,9 @@ type Secret struct { // FilePathAndName is the optional file path and name for the rendered secret file. FilePathAndName string + + // FilePermission is the optional file permission for the rendered secret file + FilePermission string } type Vault struct { diff --git a/agent-inject/agent/annotations.go b/agent-inject/agent/annotations.go index 12eef9ec..4494f02a 100644 --- a/agent-inject/agent/annotations.go +++ b/agent-inject/agent/annotations.go @@ -37,6 +37,14 @@ const ( // secret-volume-path annotation. AnnotationAgentInjectFile = "vault.hashicorp.com/agent-inject-file" + // AnnotationAgentInjectFilePermission is the key of the annotation that contains the + // permission of the file to create on disk. The name of the + // secret is the string after "vault.hashicorp.com/agent-inject-perms-", and + // should map to the same unique value provided in + // "vault.hashicorp.com/agent-inject-secret-". The value is the value of the permission, for + // example "0644" + AnnotationAgentInjectFilePermission = "vault.hashicorp.com/agent-inject-perms" + // AnnotationAgentInjectTemplate is the key annotation that configures Vault // Agent what template to use for rendering the secrets. The name // of the template is any unique string after "vault.hashicorp.com/agent-inject-template-", @@ -460,6 +468,11 @@ func (a *Agent) secrets() []*Secret { s.FilePathAndName = val } + filePerm := fmt.Sprintf("%s-%s", AnnotationAgentInjectFilePermission, raw) + if val, ok := a.Annotations[filePerm]; ok { + s.FilePermission = val + } + secrets = append(secrets, s) } } diff --git a/agent-inject/agent/annotations_test.go b/agent-inject/agent/annotations_test.go index 22cc84b3..5ad4e10e 100644 --- a/agent-inject/agent/annotations_test.go +++ b/agent-inject/agent/annotations_test.go @@ -537,6 +537,55 @@ func TestSecretTemplateFileAnnotations(t *testing.T) { } } +func TestSecretPermissionAnnotations(t *testing.T) { + tests := []struct { + annotations map[string]string + expectedKey string + expectedPermission string + }{ + { + map[string]string{ + "vault.hashicorp.com/agent-inject-secret-foobar": "test1", + "vault.hashicorp.com/agent-inject-perms-foobar": "0600", + }, "foobar", "0600", + }, + { + map[string]string{ + "vault.hashicorp.com/agent-inject-secret-foobar": "test2", + "vault.hashicorp.com/agent-inject-perms-foobar2": "0600", + }, "foobar", "", + }, + } + + for _, tt := range tests { + pod := testPod(tt.annotations) + agentConfig := basicAgentConfig() + err := Init(pod, agentConfig) + if err != nil { + t.Errorf("got error, shouldn't have: %s", err) + } + + var patches []*jsonpatch.JsonPatchOperation + + 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].FilePermission != tt.expectedPermission { + t.Errorf("expected permission %s, got %s", tt.expectedPermission, agent.Secrets[0].Command) + } + } +} + func TestSecretCommandAnnotations(t *testing.T) { tests := []struct { annotations map[string]string diff --git a/agent-inject/agent/config.go b/agent-inject/agent/config.go index 132d2782..b14b5679 100644 --- a/agent-inject/agent/config.go +++ b/agent-inject/agent/config.go @@ -77,6 +77,7 @@ type Template struct { RightDelim string `json:"right_delimiter,omitempty"` Command string `json:"command,omitempty"` Source string `json:"source,omitempty"` + Perms string `json:"perms,omitempty"` } // Listener defines the configuration for Vault Agent Cache Listener @@ -136,6 +137,9 @@ func (a *Agent) newTemplateConfigs() []*Template { RightDelim: "}}", Command: secret.Command, } + if secret.FilePermission != "" { + tmpl.Perms = secret.FilePermission + } templates = append(templates, tmpl) } return templates diff --git a/agent-inject/agent/config_test.go b/agent-inject/agent/config_test.go index 92701739..a4db0a6b 100644 --- a/agent-inject/agent/config_test.go +++ b/agent-inject/agent/config_test.go @@ -224,6 +224,9 @@ func TestFilePathAndName(t *testing.T) { } agent, err := New(pod, patches) + if err != nil { + t.Errorf("got error creating agent, shouldn't have: %s", err) + } cfg, err := agent.newConfig(true) if err != nil { t.Errorf("got error creating Vault config, shouldn't have: %s", err) @@ -240,6 +243,95 @@ func TestFilePathAndName(t *testing.T) { } } +func TestFilePermission(t *testing.T) { + + tests := []struct { + name string + annotations map[string]string + permission string + }{ + { + "just secret", + map[string]string{ + "vault.hashicorp.com/agent-inject-secret-foo": "db/creds/foo", + "vault.hashicorp.com/agent-inject-perms-foo": "0600", + }, + "0600", + }, + { + "just secret without permission", + map[string]string{ + "vault.hashicorp.com/agent-inject-secret-foo": "db/creds/foo", + }, + "", + }, + { + "with relative file path", + map[string]string{ + "vault.hashicorp.com/agent-inject-secret-foo": "db/creds/foo", + "vault.hashicorp.com/agent-inject-file-foo": "nested/foofile", + "vault.hashicorp.com/agent-inject-perms-foo": "0600", + }, + "0600", + }, + { + "with relative file path without permission", + map[string]string{ + "vault.hashicorp.com/agent-inject-secret-foo": "db/creds/foo", + "vault.hashicorp.com/agent-inject-file-foo": "nested/foofile", + }, + "", + }, + { + "with absolute file path", + map[string]string{ + "vault.hashicorp.com/agent-inject-secret-foo": "db/creds/foo", + "vault.hashicorp.com/agent-inject-file-foo": "/special/volume/foofile", + "vault.hashicorp.com/agent-inject-perms-foo": "0600", + }, + "0600", + }, + { + "with absolute file path without permission", + map[string]string{ + "vault.hashicorp.com/agent-inject-secret-foo": "db/creds/foo", + "vault.hashicorp.com/agent-inject-file-foo": "/special/volume/foofile", + }, + "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pod := testPod(tt.annotations) + var patches []*jsonpatch.JsonPatchOperation + + agentConfig := basicAgentConfig() + err := Init(pod, agentConfig) + if err != nil { + t.Errorf("got error initialising pod, shouldn't have: %s", err) + } + + agent, err := New(pod, patches) + if err != nil { + t.Errorf("got error creating agent, shouldn't have: %s", err) + } + cfg, err := agent.newConfig(true) + if err != nil { + t.Errorf("got error creating Vault config, shouldn't have: %s", err) + } + + config := &Config{} + if err := json.Unmarshal(cfg, config); err != nil { + t.Errorf("got error unmarshalling Vault config, shouldn't have: %s", err) + } + if config.Templates[0].Perms != tt.permission { + t.Errorf("wrong permission: %s != %s", config.Templates[0].Perms, tt.permission) + } + }) + } +} + func TestConfigVaultAgentCacheNotEnabledByDefault(t *testing.T) { annotations := map[string]string{}