Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow overriding of volume mount path and added a flag to preserve case sensitivity of secrets #71

Merged
9 changes: 6 additions & 3 deletions agent-inject/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ type Secret struct {
// Template is the optional custom template to use when rendering the secret.
Template string

// Mount Path
MountPath string

// Command is the optional command to run after rendering the secret.
Command string
}
Expand Down Expand Up @@ -176,7 +179,6 @@ func New(pod *corev1.Pod, patches []*jsonpatch.JsonPatchOperation) (*Agent, erro
Pod: pod,
RequestsCPU: pod.Annotations[AnnotationAgentRequestsCPU],
RequestsMem: pod.Annotations[AnnotationAgentRequestsMem],
Secrets: secrets(pod.Annotations),
ServiceAccountName: saName,
ServiceAccountPath: saPath,
Status: pod.Annotations[AnnotationAgentStatus],
Expand All @@ -198,6 +200,7 @@ func New(pod *corev1.Pod, patches []*jsonpatch.JsonPatchOperation) (*Agent, erro
}

var err error
agent.Secrets = agent.secrets()
agent.Inject, err = agent.inject()
if err != nil {
return agent, err
Expand Down Expand Up @@ -273,7 +276,7 @@ func (a *Agent) Patch() ([]byte, error) {
// for passing data in the pod.
a.Patches = append(a.Patches, addVolumes(
a.Pod.Spec.Volumes,
[]corev1.Volume{a.ContainerVolume()},
a.ContainerVolumes(),
"/spec/volumes")...)

// Add ConfigMap if one was provided
Expand All @@ -296,7 +299,7 @@ func (a *Agent) Patch() ([]byte, error) {
for i, container := range a.Pod.Spec.Containers {
a.Patches = append(a.Patches, addVolumeMounts(
container.VolumeMounts,
[]corev1.VolumeMount{a.ContainerVolumeMount()},
a.ContainerVolumeMounts(),
fmt.Sprintf("/spec/containers/%d/volumeMounts", i))...)
}

Expand Down
59 changes: 50 additions & 9 deletions agent-inject/agent/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,14 @@ const (
// AnnotationVaultAuthPath specifies the mount path to be used for the Kubernetes auto-auth
// method.
AnnotationVaultAuthPath = "vault.hashicorp.com/auth-path"

// AnnotationVaultSecretVolumePath specifies where the secrets are to be
// Mounted after fetching.
AnnotationVaultSecretVolumePath = "vault.hashicorp.com/secret-volume-path"

// AnnotationPreserveSecretCase if enabled will preserve the case of secret name
// by default the name is converted to lower case.
AnnotationPreserveSecretCase = "vault.hashicorp.com/preserve-secret-case"
)

// Init configures the expected annotations required to create a new instance
Expand Down Expand Up @@ -199,6 +207,10 @@ func Init(pod *corev1.Pod, image, address, authPath, namespace string, revokeOnS
pod.ObjectMeta.Annotations[AnnotationAgentRequestsMem] = DefaultResourceRequestMem
}

if _, ok := pod.ObjectMeta.Annotations[AnnotationVaultSecretVolumePath]; !ok {
pod.ObjectMeta.Annotations[AnnotationVaultSecretVolumePath] = secretVolumePath
}

if _, ok := pod.ObjectMeta.Annotations[AnnotationAgentRevokeOnShutdown]; !ok {
pod.ObjectMeta.Annotations[AnnotationAgentRevokeOnShutdown] = strconv.FormatBool(revokeOnShutdown)
}
Expand All @@ -220,18 +232,23 @@ func Init(pod *corev1.Pod, image, address, authPath, namespace string, revokeOnS
//
// For example: "vault.hashicorp.com/agent-inject-secret-foobar: db/creds/foobar"
// name: foobar, value: db/creds/foobar
func secrets(annotations map[string]string) []*Secret {
func (a *Agent) secrets() []*Secret {
var secrets []*Secret

// First check for the token-only injection annotation
if _, found := annotations[AnnotationAgentInjectToken]; found {
annotations[fmt.Sprintf("%s-%s", AnnotationAgentInjectSecret, "token")] = TokenSecret
annotations[fmt.Sprintf("%s-%s", AnnotationAgentInjectTemplate, "token")] = TokenTemplate
if _, found := a.Annotations[AnnotationAgentInjectToken]; found {
a.Annotations[fmt.Sprintf("%s-%s", AnnotationAgentInjectSecret, "token")] = TokenSecret
a.Annotations[fmt.Sprintf("%s-%s", AnnotationAgentInjectTemplate, "token")] = TokenTemplate
}
for name, path := range annotations {
for name, path := range a.Annotations {
secretName := fmt.Sprintf("%s-", AnnotationAgentInjectSecret)
if strings.Contains(name, secretName) {
raw := strings.ReplaceAll(name, secretName, "")
name := strings.ToLower(raw)
name := raw

if ok, _ := a.preserveSecretCase(raw); !ok {
name = strings.ToLower(raw)
}

if name == "" {
continue
Expand All @@ -240,18 +257,25 @@ func secrets(annotations map[string]string) []*Secret {
var template string
templateName := fmt.Sprintf("%s-%s", AnnotationAgentInjectTemplate, raw)

if val, ok := annotations[templateName]; ok {
if val, ok := a.Annotations[templateName]; ok {
template = val
}

mountPath := a.Annotations[AnnotationVaultSecretVolumePath]
mountPathAnnotationName := fmt.Sprintf("%s-%s", AnnotationVaultSecretVolumePath, raw)

if val, ok := a.Annotations[mountPathAnnotationName]; ok {
mountPath = val
}

var command string
commandName := fmt.Sprintf("%s-%s", AnnotationAgentInjectCommand, raw)

if val, ok := annotations[commandName]; ok {
if val, ok := a.Annotations[commandName]; ok {
command = val
}

secrets = append(secrets, &Secret{Name: name, Path: path, Template: template, Command: command})
secrets = append(secrets, &Secret{Name: name, Path: path, Template: template, Command: command, MountPath: mountPath})
}
}
return secrets
Expand Down Expand Up @@ -310,3 +334,20 @@ func (a *Agent) tlsSkipVerify() (bool, error) {

return strconv.ParseBool(raw)
}

func (a *Agent) preserveSecretCase(secretName string) (bool, error) {

preserveSecretCaseAnnotationName := fmt.Sprintf("%s-%s", AnnotationPreserveSecretCase, secretName)

var raw string

if val, ok := a.Annotations[preserveSecretCaseAnnotationName]; ok {
raw = val
} else {
raw, ok = a.Annotations[AnnotationPreserveSecretCase]
if !ok {
return false, nil
}
}
return strconv.ParseBool(raw)
}
60 changes: 52 additions & 8 deletions agent-inject/agent/annotations_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package agent

import (
"fmt"
"reflect"
"strings"
"testing"
Expand Down Expand Up @@ -106,7 +107,7 @@ func TestInitError(t *testing.T) {
}
}

func TestSecretAnnotations(t *testing.T) {
func TestSecretAnnotationsWithPreserveCaseSensitivityFlagOff(t *testing.T) {
tests := []struct {
key string
value string
Expand All @@ -119,11 +120,14 @@ func TestSecretAnnotations(t *testing.T) {
{"vault.hashicorp.com/agent-inject-secret-server.crt", "creds/tls/somecert", "server.crt", "creds/tls/somecert"},
{"vault.hashicorp.com/agent-inject-secret", "test4", "", ""},
{"vault.hashicorp.com/agent-inject-secret-", "test5", "", ""},
// explicitly turn on preserve case sensitivity flag
{"vault.hashicorp.com/agent-inject-secret-FOOBAR_EXPLICIT", "test2", "FOOBAR_EXPLICIT", "test2"},
}

for _, tt := range tests {
annotation := map[string]string{
tt.key: tt.value,
fmt.Sprintf("%s-%s", AnnotationPreserveSecretCase, "FOOBAR_EXPLICIT"): "true",
}
pod := testPod(annotation)
var patches []*jsonpatch.JsonPatchOperation
Expand All @@ -136,20 +140,60 @@ func TestSecretAnnotations(t *testing.T) {
if tt.expectedKey != "" {
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 %s, got %s", tt.expectedKey, agent.Secrets[0].Name)
}

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

if agent.Secrets[0].Path != tt.expectedPath {
t.Errorf("expected %s, got %s", tt.expectedPath, agent.Secrets[0].Path)
if agent.Secrets[0].Path != tt.expectedPath {
t.Errorf("expected %s, got %s", tt.expectedPath, agent.Secrets[0].Path)

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

func TestSecretAnnotationsWithPreserveCaseSensitivityFlagOn(t *testing.T) {
tests := []struct {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jasonodonnell this is not dry but making it dry would make tests a bit complicated so I duplicated the code

key string
value string
expectedKey string
expectedPath string
}{
{"vault.hashicorp.com/agent-inject-secret-foobar", "test1", "foobar", "test1"},
{"vault.hashicorp.com/agent-inject-secret-FOOBAR", "test2", "FOOBAR", "test2"},
}

for _, tt := range tests {
annotation := map[string]string{
tt.key: tt.value,
AnnotationPreserveSecretCase: "true",
}
pod := testPod(annotation)
var patches []*jsonpatch.JsonPatchOperation

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

if tt.expectedKey != "" {
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 %s, got %s", tt.expectedKey, agent.Secrets[0].Name)
}

if agent.Secrets[0].Path != tt.expectedPath {
t.Errorf("expected %s, got %s", tt.expectedPath, agent.Secrets[0].Path)

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

Expand Down
2 changes: 1 addition & 1 deletion agent-inject/agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func (a *Agent) newTemplateConfigs() []*Template {

tmpl := &Template{
Contents: template,
Destination: fmt.Sprintf("/vault/secrets/%s", secret.Name),
Destination: fmt.Sprintf("%s/%s", secret.MountPath, secret.Name),
LeftDelim: "{{",
RightDelim: "}}",
Command: secret.Command,
Expand Down
18 changes: 14 additions & 4 deletions agent-inject/agent/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package agent

import (
"encoding/json"
"fmt"
"strings"
"testing"

Expand All @@ -22,10 +23,16 @@ func TestNewConfig(t *testing.T) {
AnnotationVaultCAKey: "ca-key",
AnnotationVaultClientCert: "client-cert",
AnnotationVaultClientKey: "client-key",
AnnotationVaultSecretVolumePath: "/vault/secrets",
"vault.hashicorp.com/agent-inject-secret-foo": "db/creds/foo",
"vault.hashicorp.com/agent-inject-template-foo": "template foo",
"vault.hashicorp.com/agent-inject-secret-bar": "db/creds/bar",
"vault.hashicorp.com/agent-inject-command-bar": "pkill -HUP app",

// render this secret at a different path
"vault.hashicorp.com/agent-inject-secret-different-path": "different-path",
fmt.Sprintf("%s-%s", AnnotationVaultSecretVolumePath, "different-path"): "/etc/container_environment",

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

pod := testPod(annotations)
Expand Down Expand Up @@ -82,8 +89,8 @@ func TestNewConfig(t *testing.T) {
t.Errorf("auto_auth mount path: expected path to be %s, got %s", annotations[AnnotationVaultAuthPath], config.AutoAuth.Method.MountPath)
}

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

for _, template := range config.Templates {
Expand All @@ -103,10 +110,13 @@ func TestNewConfig(t *testing.T) {
if !strings.Contains(template.Contents, "with secret \"db/creds/bar\"") {
t.Errorf("expected template contents to contain %s, got %s", "with secret \"db/creds/bar\"", template.Contents)
}

if !strings.Contains(template.Command, "pkill -HUP app") {
t.Errorf("expected command contents to contain %s, got %s", "pkill -HUP app", template.Command)
}
} else if strings.Contains(template.Destination, "different-path") {
if template.Destination != "/etc/container_environment/different-path" {
t.Errorf("expected template destination to be %s, got %s", "/etc/container_environment", template.Destination)
}
} else {
t.Error("shouldn't have got here")
}
Expand Down
6 changes: 1 addition & 5 deletions agent-inject/agent/container_init_sidecar.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,13 @@ import (
// two config files.
func (a *Agent) ContainerInitSidecar() (corev1.Container, error) {
volumeMounts := []corev1.VolumeMount{
{
Name: secretVolumeName,
MountPath: secretVolumePath,
ReadOnly: false,
},
{
Name: a.ServiceAccountName,
MountPath: a.ServiceAccountPath,
ReadOnly: true,
},
}
volumeMounts = append(volumeMounts, a.ContainerVolumeMounts()...)

arg := DefaultContainerArg

Expand Down
6 changes: 1 addition & 5 deletions agent-inject/agent/container_sidecar.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,13 @@ const (
// to the pod being mutated.
func (a *Agent) ContainerSidecar() (corev1.Container, error) {
volumeMounts := []corev1.VolumeMount{
{
Name: secretVolumeName,
MountPath: secretVolumePath,
ReadOnly: false,
},
{
Name: a.ServiceAccountName,
MountPath: a.ServiceAccountPath,
ReadOnly: true,
},
}
volumeMounts = append(volumeMounts, a.ContainerVolumeMounts()...)

arg := DefaultContainerArg

Expand Down
Loading