diff --git a/agent-inject/agent/agent.go b/agent-inject/agent/agent.go index 3ecbdb07..81e461f7 100644 --- a/agent-inject/agent/agent.go +++ b/agent-inject/agent/agent.go @@ -780,7 +780,8 @@ func serviceaccount(pod *corev1.Pod) (*ServiceAccountTokenVolume, error) { // Fallback to searching for normal service account token for _, container := range pod.Spec.Containers { for _, volumes := range container.VolumeMounts { - if strings.Contains(volumes.MountPath, "serviceaccount") { + // Must find only kubernetes serviceaccount token + if strings.Contains(volumes.MountPath, "kubernetes.io/serviceaccount") { return &ServiceAccountTokenVolume{ Name: volumes.Name, MountPath: volumes.MountPath, diff --git a/agent-inject/agent/agent_test.go b/agent-inject/agent/agent_test.go index f137c6d9..24f4974f 100644 --- a/agent-inject/agent/agent_test.go +++ b/agent-inject/agent/agent_test.go @@ -25,7 +25,7 @@ func testPod(annotations map[string]string) *corev1.Pod { VolumeMounts: []corev1.VolumeMount{ { Name: "foobar", - MountPath: "serviceaccount/somewhere", + MountPath: "kubernetes.io/serviceaccount/somewhere", }, { Name: "tobecopied", @@ -70,7 +70,7 @@ func testPodIRSA(annotations map[string]string) *corev1.Pod { VolumeMounts: []corev1.VolumeMount{ { Name: "foobar", - MountPath: "serviceaccount/somewhere", + MountPath: "kubernetes.io/serviceaccount/somewhere", }, }, }, @@ -564,6 +564,63 @@ func Test_serviceaccount(t *testing.T) { }, }, }, + "multiple service account tokens from different sources": { + expected: &ServiceAccountTokenVolume{ + Name: "kube-api-access-4bfzq", + MountPath: "/var/run/secrets/kubernetes.io/serviceaccount", + TokenPath: "token", + }, + expectedError: "", + pod: &corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: "aws-iam-token", + MountPath: "/var/run/secrets/eks.amazonaws.com/serviceaccount", + }, + { + Name: "kube-api-access-4bfzq", + MountPath: "/var/run/secrets/kubernetes.io/serviceaccount", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "aws-iam-token", + VolumeSource: corev1.VolumeSource{ + Projected: &corev1.ProjectedVolumeSource{ + Sources: []corev1.VolumeProjection{ + { + ServiceAccountToken: &corev1.ServiceAccountTokenProjection{ + Path: "token", + Audience: "sts.amazonaws.com", + }, + }, + }, + }, + }, + }, + { + Name: "kube-api-access-4bfzq", + VolumeSource: corev1.VolumeSource{ + Projected: &corev1.ProjectedVolumeSource{ + Sources: []corev1.VolumeProjection{ + { + ServiceAccountToken: &corev1.ServiceAccountTokenProjection{ + Path: "token", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { diff --git a/agent-inject/agent/annotations.go b/agent-inject/agent/annotations.go index f6b8b9f3..c26f9a48 100644 --- a/agent-inject/agent/annotations.go +++ b/agent-inject/agent/annotations.go @@ -317,6 +317,7 @@ type AgentConfig struct { Address string AuthType string AuthPath string + AuthConfigExtraArgs map[string]string VaultNamespace string Namespace string RevokeOnShutdown bool @@ -385,6 +386,14 @@ func Init(pod *corev1.Pod, cfg AgentConfig) error { pod.ObjectMeta.Annotations[AnnotationVaultAuthPath] = cfg.AuthPath } + // Set auth config extra args to annotations + for key, val := range cfg.AuthConfigExtraArgs { + annotationKey := fmt.Sprintf("%s-%s", AnnotationVaultAuthConfig, key) + if _, ok := pod.ObjectMeta.Annotations[annotationKey]; !ok { + pod.ObjectMeta.Annotations[annotationKey] = val + } + } + if _, ok := pod.ObjectMeta.Annotations[AnnotationVaultNamespace]; !ok { pod.ObjectMeta.Annotations[AnnotationVaultNamespace] = cfg.VaultNamespace } diff --git a/agent-inject/agent/annotations_test.go b/agent-inject/agent/annotations_test.go index a3806e44..ff71acff 100644 --- a/agent-inject/agent/annotations_test.go +++ b/agent-inject/agent/annotations_test.go @@ -973,7 +973,7 @@ func TestAuthConfigAnnotations(t *testing.T) { }, map[string]interface{}{ "role": "backwardscompat", - "token_path": "serviceaccount/somewhere/token", + "token_path": "kubernetes.io/serviceaccount/somewhere/token", }, }, { @@ -983,7 +983,7 @@ func TestAuthConfigAnnotations(t *testing.T) { }, map[string]interface{}{ "role": "backwardscompat", - "token_path": "serviceaccount/somewhere/token", + "token_path": "kubernetes.io/serviceaccount/somewhere/token", }, }, { @@ -1008,7 +1008,7 @@ func TestAuthConfigAnnotations(t *testing.T) { "client_cert": "baz", "credential_poll_interval": "1", // string->int conversion left up to consuming app HCL parser "remove_secret_id_file_after_reading": "false", // string->bool, same as above - "token_path": "serviceaccount/somewhere/token", + "token_path": "kubernetes.io/serviceaccount/somewhere/token", }, }, } diff --git a/agent-inject/agent/container_sidecar_test.go b/agent-inject/agent/container_sidecar_test.go index de9801b7..3cf6c621 100644 --- a/agent-inject/agent/container_sidecar_test.go +++ b/agent-inject/agent/container_sidecar_test.go @@ -1289,14 +1289,14 @@ func TestAgentJsonPatch(t *testing.T) { baseContainerEnvVars, corev1.EnvVar{Name: "VAULT_LOG_LEVEL", Value: "info"}, corev1.EnvVar{Name: "VAULT_LOG_FORMAT", Value: "standard"}, - corev1.EnvVar{Name: "VAULT_CONFIG", Value: "eyJhdXRvX2F1dGgiOnsibWV0aG9kIjp7InR5cGUiOiJrdWJlcm5ldGVzIiwibW91bnRfcGF0aCI6InRlc3QiLCJjb25maWciOnsicm9sZSI6InJvbGUiLCJ0b2tlbl9wYXRoIjoic2VydmljZWFjY291bnQvc29tZXdoZXJlL3Rva2VuIn19LCJzaW5rIjpbeyJ0eXBlIjoiZmlsZSIsImNvbmZpZyI6eyJwYXRoIjoiL2hvbWUvdmF1bHQvLnZhdWx0LXRva2VuIn19XX0sImV4aXRfYWZ0ZXJfYXV0aCI6ZmFsc2UsInBpZF9maWxlIjoiL2hvbWUvdmF1bHQvLnBpZCIsInZhdWx0Ijp7ImFkZHJlc3MiOiJodHRwOi8vZm9vYmFyOjEyMzQifSwidGVtcGxhdGVfY29uZmlnIjp7ImV4aXRfb25fcmV0cnlfZmFpbHVyZSI6dHJ1ZX19"}, + corev1.EnvVar{Name: "VAULT_CONFIG", Value: "eyJhdXRvX2F1dGgiOnsibWV0aG9kIjp7InR5cGUiOiJrdWJlcm5ldGVzIiwibW91bnRfcGF0aCI6InRlc3QiLCJjb25maWciOnsicm9sZSI6InJvbGUiLCJ0b2tlbl9wYXRoIjoia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zb21ld2hlcmUvdG9rZW4ifX0sInNpbmsiOlt7InR5cGUiOiJmaWxlIiwiY29uZmlnIjp7InBhdGgiOiIvaG9tZS92YXVsdC8udmF1bHQtdG9rZW4ifX1dfSwiZXhpdF9hZnRlcl9hdXRoIjpmYWxzZSwicGlkX2ZpbGUiOiIvaG9tZS92YXVsdC8ucGlkIiwidmF1bHQiOnsiYWRkcmVzcyI6Imh0dHA6Ly9mb29iYXI6MTIzNCJ9LCJ0ZW1wbGF0ZV9jb25maWciOnsiZXhpdF9vbl9yZXRyeV9mYWlsdXJlIjp0cnVlfX0="}, ), Resources: v1.ResourceRequirements{ Limits: v1.ResourceList{"cpu": resource.MustParse("500m"), "memory": resource.MustParse("128Mi")}, Requests: v1.ResourceList{"cpu": resource.MustParse("250m"), "memory": resource.MustParse("64Mi")}, }, VolumeMounts: []v1.VolumeMount{ - {Name: "foobar", ReadOnly: true, MountPath: "serviceaccount/somewhere"}, + {Name: "foobar", ReadOnly: true, MountPath: "kubernetes.io/serviceaccount/somewhere"}, {Name: "home-sidecar", MountPath: "/home/vault"}, {Name: "vault-secrets", MountPath: "/vault/secrets"}, }, @@ -1325,11 +1325,11 @@ func TestAgentJsonPatch(t *testing.T) { baseContainerEnvVars, corev1.EnvVar{Name: "VAULT_LOG_LEVEL", Value: "info"}, corev1.EnvVar{Name: "VAULT_LOG_FORMAT", Value: "standard"}, - corev1.EnvVar{Name: "VAULT_CONFIG", Value: "eyJhdXRvX2F1dGgiOnsibWV0aG9kIjp7InR5cGUiOiJrdWJlcm5ldGVzIiwibW91bnRfcGF0aCI6InRlc3QiLCJjb25maWciOnsicm9sZSI6InJvbGUiLCJ0b2tlbl9wYXRoIjoic2VydmljZWFjY291bnQvc29tZXdoZXJlL3Rva2VuIn19LCJzaW5rIjpbeyJ0eXBlIjoiZmlsZSIsImNvbmZpZyI6eyJwYXRoIjoiL2hvbWUvdmF1bHQvLnZhdWx0LXRva2VuIn19XX0sImV4aXRfYWZ0ZXJfYXV0aCI6dHJ1ZSwicGlkX2ZpbGUiOiIvaG9tZS92YXVsdC8ucGlkIiwidmF1bHQiOnsiYWRkcmVzcyI6Imh0dHA6Ly9mb29iYXI6MTIzNCJ9LCJ0ZW1wbGF0ZV9jb25maWciOnsiZXhpdF9vbl9yZXRyeV9mYWlsdXJlIjp0cnVlfX0="}, + corev1.EnvVar{Name: "VAULT_CONFIG", Value: "eyJhdXRvX2F1dGgiOnsibWV0aG9kIjp7InR5cGUiOiJrdWJlcm5ldGVzIiwibW91bnRfcGF0aCI6InRlc3QiLCJjb25maWciOnsicm9sZSI6InJvbGUiLCJ0b2tlbl9wYXRoIjoia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zb21ld2hlcmUvdG9rZW4ifX0sInNpbmsiOlt7InR5cGUiOiJmaWxlIiwiY29uZmlnIjp7InBhdGgiOiIvaG9tZS92YXVsdC8udmF1bHQtdG9rZW4ifX1dfSwiZXhpdF9hZnRlcl9hdXRoIjp0cnVlLCJwaWRfZmlsZSI6Ii9ob21lL3ZhdWx0Ly5waWQiLCJ2YXVsdCI6eyJhZGRyZXNzIjoiaHR0cDovL2Zvb2JhcjoxMjM0In0sInRlbXBsYXRlX2NvbmZpZyI6eyJleGl0X29uX3JldHJ5X2ZhaWx1cmUiOnRydWV9fQ=="}, ) baseInitContainer.VolumeMounts = []v1.VolumeMount{ {Name: "home-init", MountPath: "/home/vault"}, - {Name: "foobar", ReadOnly: true, MountPath: "serviceaccount/somewhere"}, + {Name: "foobar", ReadOnly: true, MountPath: "kubernetes.io/serviceaccount/somewhere"}, {Name: "vault-secrets", MountPath: "/vault/secrets"}, } baseInitContainer.Lifecycle = nil diff --git a/agent-inject/handler.go b/agent-inject/handler.go index d3588504..5588097d 100644 --- a/agent-inject/handler.go +++ b/agent-inject/handler.go @@ -52,6 +52,7 @@ type Handler struct { VaultCACertBytes string VaultAuthType string VaultAuthPath string + VaultAuthConfigExtraArgs map[string]string VaultNamespace string ProxyAddress string ImageVault string @@ -192,6 +193,7 @@ func (h *Handler) Mutate(req *admissionv1.AdmissionRequest) *admissionv1.Admissi Address: h.VaultAddress, AuthType: h.VaultAuthType, AuthPath: h.VaultAuthPath, + AuthConfigExtraArgs: h.VaultAuthConfigExtraArgs, VaultNamespace: h.VaultNamespace, ProxyAddress: h.ProxyAddress, Namespace: req.Namespace, diff --git a/agent-inject/handler_test.go b/agent-inject/handler_test.go index 53fb7cc1..036ec73d 100644 --- a/agent-inject/handler_test.go +++ b/agent-inject/handler_test.go @@ -38,7 +38,7 @@ func TestHandlerHandle(t *testing.T) { VolumeMounts: []corev1.VolumeMount{ { Name: "foobar", - MountPath: "serviceaccount/somewhere", + MountPath: "kubernetes.io/serviceaccount/somewhere", }, }, }, @@ -49,7 +49,7 @@ func TestHandlerHandle(t *testing.T) { VolumeMounts: []corev1.VolumeMount{ { Name: "foobar", - MountPath: "serviceaccount/somewhere", + MountPath: "kubernetes.io/serviceaccount/somewhere", }, }, }, diff --git a/subcommand/injector/command.go b/subcommand/injector/command.go index 1529b618..b4a95f26 100644 --- a/subcommand/injector/command.go +++ b/subcommand/injector/command.go @@ -59,6 +59,7 @@ type Command struct { flagVaultImage string // Name of the Vault Image to use flagVaultAuthType string // Type of Vault Auth Method to use flagVaultAuthPath string // Mount path of the Vault Auth Method + flagVaultAuthConfigExtraArgs string // Extra arguments for the Vault Auth Method Config block flagVaultNamespace string // Vault enterprise namespace flagRevokeOnShutdown bool // Revoke Vault Token on pod shutdown flagRunAsUser string // User (uid) to run Vault agent as @@ -192,11 +193,17 @@ func (c *Command) Run(args []string) int { go c.certWatcher(ctx, certCh, clientset, leaderElector, logger.Named("certwatcher")) // Build the HTTP handler and server + authConfigExtraArgs, err := c.makeVaultAuthConfigExtraArgs() + if err != nil { + c.UI.Error(fmt.Sprintf("Error reading VaultAuthConfigExtraArgs: %s", err)) + return 1 + } injector := agentInject.Handler{ VaultAddress: c.flagVaultService, VaultCACertBytes: c.flagVaultCACertBytes, VaultAuthType: c.flagVaultAuthType, VaultAuthPath: c.flagVaultAuthPath, + VaultAuthConfigExtraArgs: authConfigExtraArgs, VaultNamespace: c.flagVaultNamespace, ProxyAddress: c.flagProxyAddress, ImageVault: c.flagVaultImage, @@ -310,6 +317,22 @@ func (c *Command) handleReady(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(204) } +func (c *Command) makeVaultAuthConfigExtraArgs() (map[string]string, error) { + extraArgs := make(map[string]string) + if c.flagVaultAuthConfigExtraArgs == "" { + return extraArgs, nil + } + for _, extraArg := range strings.Split(c.flagVaultAuthConfigExtraArgs, ",") { + extraArgSlice := strings.Split(extraArg, ":") + if len(extraArgSlice) != 2 { + return nil, fmt.Errorf("invalid auth config extra args: %s", + c.flagVaultAuthConfigExtraArgs) + } + extraArgs[strings.TrimSpace(extraArgSlice[0])] = strings.TrimSpace(extraArgSlice[1]) + } + return extraArgs, nil +} + func (c *Command) makeTLSConfig() (*tls.Config, error) { minTLSVersion, ok := tlsutil.TLSLookup[c.flagTLSMinVersion] if !ok { diff --git a/subcommand/injector/flags.go b/subcommand/injector/flags.go index 5b809f50..26689ce6 100644 --- a/subcommand/injector/flags.go +++ b/subcommand/injector/flags.go @@ -78,6 +78,9 @@ type Specification struct { // VaultAuthPath is the AGENT_INJECT_VAULT_AUTH_PATH environment variable. VaultAuthPath string `split_words:"true"` + // VaultAuthConfigExtraArgs is the AGENT_INJECT_VAULT_AUTH_CONFIG_EXTRA_ARGS environment variable. + VaultAuthConfigExtraArgs string `split_words:"true"` + // VaultNamespace is the AGENT_INJECT_VAULT_NAMESPACE environment variable. VaultNamespace string `split_words:"true"` @@ -320,6 +323,10 @@ func (c *Command) parseEnvs() error { c.flagVaultAuthPath = envs.VaultAuthPath } + if envs.VaultAuthConfigExtraArgs != "" { + c.flagVaultAuthConfigExtraArgs = envs.VaultAuthConfigExtraArgs + } + if envs.VaultNamespace != "" { c.flagVaultNamespace = envs.VaultNamespace } diff --git a/subcommand/injector/flags_test.go b/subcommand/injector/flags_test.go index 854d9bb1..3e945f59 100644 --- a/subcommand/injector/flags_test.go +++ b/subcommand/injector/flags_test.go @@ -119,6 +119,7 @@ func TestCommandEnvs(t *testing.T) { {env: "AGENT_INJECT_VAULT_CACERT_BYTES", value: "foo", cmdPtr: &cmd.flagVaultCACertBytes}, {env: "AGENT_INJECT_PROXY_ADDR", value: "http://proxy:3128", cmdPtr: &cmd.flagProxyAddress}, {env: "AGENT_INJECT_VAULT_AUTH_PATH", value: "auth-path-test", cmdPtr: &cmd.flagVaultAuthPath}, + {env: "AGENT_INJECT_VAULT_AUTH_CONFIG_EXTRA_ARGS", value: "key1:val1", cmdPtr: &cmd.flagVaultAuthConfigExtraArgs}, {env: "AGENT_INJECT_VAULT_IMAGE", value: "hashicorp/vault:1.15.1", cmdPtr: &cmd.flagVaultImage}, {env: "AGENT_INJECT_VAULT_NAMESPACE", value: "test-namespace", cmdPtr: &cmd.flagVaultNamespace}, {env: "AGENT_INJECT_TLS_KEY_FILE", value: "server.key", cmdPtr: &cmd.flagKeyFile},