diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a5f62e7..31379eba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## Unreleased + +Features: +* Add agent-inject-containers annotation [GH-313](https://github.com/hashicorp/vault-k8s/pull/313) + ## 0.14.2 (January 19, 2022) Changes: diff --git a/agent-inject/agent/agent.go b/agent-inject/agent/agent.go index ab99a146..c36f5f87 100644 --- a/agent-inject/agent/agent.go +++ b/agent-inject/agent/agent.go @@ -7,6 +7,7 @@ import ( "strconv" "strings" + "github.com/hashicorp/vault/sdk/helper/strutil" "github.com/mattbaird/jsonpatch" corev1 "k8s.io/api/core/v1" ) @@ -49,6 +50,9 @@ type Agent struct { // sidecar container. ImageName string + // Containers determine which containers should be injected + Containers []string + // Inject is the flag used to determine if a container should be requested // in a pod request. Inject bool @@ -93,7 +97,7 @@ type Agent struct { RequestsMem string // Secrets are all the templates, the path in Vault where the secret can be - //found, and the unique name of the secret which will be used for the filename. + // found, and the unique name of the secret which will be used for the filename. Secrets []*Secret // ServiceAccountTokenVolume holds details of a volume mount for a @@ -308,6 +312,7 @@ func New(pod *corev1.Pod, patches []*jsonpatch.JsonPatchOperation) (*Agent, erro Namespace: pod.Annotations[AnnotationAgentRequestNamespace], Patches: patches, Pod: pod, + Containers: []string{}, RequestsCPU: pod.Annotations[AnnotationAgentRequestsCPU], RequestsMem: pod.Annotations[AnnotationAgentRequestsMem], ServiceAccountTokenVolume: sa, @@ -363,6 +368,8 @@ func New(pod *corev1.Pod, patches []*jsonpatch.JsonPatchOperation) (*Agent, erro return agent, err } + agent.Containers = strings.Split(pod.Annotations[AnnotationAgentInjectContainers], ",") + agent.RevokeGrace, err = agent.revokeGrace() if err != nil { return agent, err @@ -520,12 +527,14 @@ func (a *Agent) Patch() ([]byte, error) { "/spec/volumes")...) } - //Add Volume Mounts + // Add Volume Mounts for i, container := range a.Pod.Spec.Containers { - a.Patches = append(a.Patches, addVolumeMounts( - container.VolumeMounts, - a.ContainerVolumeMounts(), - fmt.Sprintf("/spec/containers/%d/volumeMounts", i))...) + if strutil.StrListContains(a.Containers, container.Name) { + a.Patches = append(a.Patches, addVolumeMounts( + container.VolumeMounts, + a.ContainerVolumeMounts(), + fmt.Sprintf("/spec/containers/%d/volumeMounts", i))...) + } } // Init Container @@ -562,7 +571,7 @@ func (a *Agent) Patch() ([]byte, error) { "/spec/initContainers")...) } - //Add Volume Mounts + // Add Volume Mounts for i, container := range containers { if container.Name == "vault-agent-init" { continue diff --git a/agent-inject/agent/agent_test.go b/agent-inject/agent/agent_test.go index dd13642d..994a3b96 100644 --- a/agent-inject/agent/agent_test.go +++ b/agent-inject/agent/agent_test.go @@ -31,6 +31,24 @@ func testPod(annotations map[string]string) *corev1.Pod { }, }, }, + { + Name: "foo1", + VolumeMounts: []corev1.VolumeMount{ + { + Name: "foo1", + MountPath: "/data/foo1", + }, + }, + }, + { + Name: "foo2", + VolumeMounts: []corev1.VolumeMount{ + { + Name: "foo2", + MountPath: "/data/foo2", + }, + }, + }, }, }, } diff --git a/agent-inject/agent/annotations.go b/agent-inject/agent/annotations.go index 67b65765..33b45035 100644 --- a/agent-inject/agent/annotations.go +++ b/agent-inject/agent/annotations.go @@ -54,6 +54,12 @@ const ( // If not provided, a default generic template is used. AnnotationAgentInjectTemplate = "vault.hashicorp.com/agent-inject-template" + // AnnotationAgentInjectContainers is the key of the annotation that controls + // in which containers the secrets volume should be mounted. Multiple containers can + // be specified in a comma-separated list. If not provided, the secrets volume will + // be mounted in all containers in the pod. + AnnotationAgentInjectContainers = "vault.hashicorp.com/agent-inject-containers" + // AnnotationAgentInjectDefaultTemplate sets the default template type. Possible values // are "json" and "map". AnnotationAgentInjectDefaultTemplate = "vault.hashicorp.com/agent-inject-default-template" @@ -418,6 +424,14 @@ func Init(pod *corev1.Pod, cfg AgentConfig) error { pod.ObjectMeta.Annotations[AnnotationAgentCacheExitOnErr] = strconv.FormatBool(DefaultAgentCacheExitOnErr) } + if _, ok := pod.ObjectMeta.Annotations[AnnotationAgentInjectContainers]; !ok { + containerNames := make([]string, len(pod.Spec.Containers)) + for i, v := range pod.Spec.Containers { + containerNames[i] = v.Name + } + pod.ObjectMeta.Annotations[AnnotationAgentInjectContainers] = strings.Join(containerNames, ",") + } + if _, ok := pod.ObjectMeta.Annotations[AnnotationAgentInjectDefaultTemplate]; !ok { pod.ObjectMeta.Annotations[AnnotationAgentInjectDefaultTemplate] = cfg.DefaultTemplate } @@ -561,7 +575,6 @@ func (a *Agent) tlsSkipVerify() (bool, error) { } func (a *Agent) preserveSecretCase(secretName string) (bool, error) { - preserveSecretCaseAnnotationName := fmt.Sprintf("%s-%s", AnnotationPreserveSecretCase, secretName) var raw string diff --git a/agent-inject/agent/annotations_test.go b/agent-inject/agent/annotations_test.go index 01277203..b13b4fac 100644 --- a/agent-inject/agent/annotations_test.go +++ b/agent-inject/agent/annotations_test.go @@ -1,6 +1,7 @@ package agent import ( + "encoding/json" "fmt" "reflect" "strconv" @@ -955,6 +956,91 @@ func TestAuthConfigAnnotations(t *testing.T) { } } +func TestInjectContainers(t *testing.T) { + tests := []struct { + name string + annotations map[string]string + expectedValue string + ExpectedPatch []jsonpatch.JsonPatchOperation + }{ + { + name: "No InjectionContainers annotations", + annotations: map[string]string{}, + expectedValue: "foobar,foo1,foo2", + ExpectedPatch: []jsonpatch.JsonPatchOperation{ + {Operation: "add", Path: "/spec/volumes"}, + {Operation: "add", Path: "/spec/volumes/-"}, + {Operation: "add", Path: "/spec/volumes"}, + {Operation: "add", Path: "/spec/containers/0/volumeMounts/-"}, + {Operation: "add", Path: "/spec/containers/1/volumeMounts/-"}, + {Operation: "add", Path: "/spec/containers/2/volumeMounts/-"}, + {Operation: "add", Path: "/spec/initContainers"}, + {Operation: "add", Path: "/spec/containers/-"}, + {Operation: "add", Path: "/metadata/annotations/" + EscapeJSONPointer(AnnotationAgentStatus)}, + }, + }, + { + name: "InjectionContainers annotation with container name", + annotations: map[string]string{AnnotationAgentInjectContainers: "foo1"}, + expectedValue: "foo1", + ExpectedPatch: []jsonpatch.JsonPatchOperation{ + {Operation: "add", Path: "/spec/volumes"}, + {Operation: "add", Path: "/spec/volumes/-"}, + {Operation: "add", Path: "/spec/volumes"}, + {Operation: "add", Path: "/spec/containers/1/volumeMounts/-"}, + {Operation: "add", Path: "/spec/initContainers"}, + {Operation: "add", Path: "/spec/containers/-"}, + {Operation: "add", Path: "/metadata/annotations/" + EscapeJSONPointer(AnnotationAgentStatus)}, + }, + }, + { + name: "InjectionContainer annotations with multiple containers names", + annotations: map[string]string{AnnotationAgentInjectContainers: "foo1,foo2"}, + expectedValue: "foo1,foo2", + ExpectedPatch: []jsonpatch.JsonPatchOperation{ + {Operation: "add", Path: "/spec/volumes"}, + {Operation: "add", Path: "/spec/volumes/-"}, + {Operation: "add", Path: "/spec/volumes"}, + {Operation: "add", Path: "/spec/containers/1/volumeMounts/-"}, + {Operation: "add", Path: "/spec/containers/2/volumeMounts/-"}, + {Operation: "add", Path: "/spec/initContainers"}, + {Operation: "add", Path: "/spec/containers/-"}, + {Operation: "add", Path: "/metadata/annotations/" + EscapeJSONPointer(AnnotationAgentStatus)}, + }, + }, + } + + 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, shouldn't have: %s", err) + } + agent, err := New(pod, patches) + if err != nil { + t.Errorf("got error, shouldn't have: %s", err) + } + patch, err := agent.Patch() + if err != nil { + t.Errorf("got error, shouldn't have: %s", err) + } + require.Equal(t, pod.Annotations[AnnotationAgentInjectContainers], tt.expectedValue) + + var output []jsonpatch.JsonPatchOperation + require.NoError(t, json.Unmarshal(patch, &output)) + for i := range output { + output[i].Value = nil + } + require.Equal(t, tt.ExpectedPatch, output) + + }) + } + +} + func TestDefaultTemplateOverride(t *testing.T) { tests := []struct { annotations map[string]string