Skip to content

Commit

Permalink
Auth config block must support common custom arguments in environment…
Browse files Browse the repository at this point in the history
… variables and flags

Also fixes using AWS IRSA token by mistake if both included in the pod's volume hashicorp#544
This maybe a better fix then the proposed hashicorp#545 pull request as this is likely more future-proof
 to other third party k8s provider launching their own Service Account Token injection, assuming
 the third party k8s provider will follow the unsaid convention of injecting the token in the
 <third.party.url>/serviceaccount/token path
  • Loading branch information
Utkarsh Chanchlani committed Jan 17, 2024
1 parent 9379137 commit 224c1ee
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 12 deletions.
3 changes: 2 additions & 1 deletion agent-inject/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
61 changes: 59 additions & 2 deletions agent-inject/agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
},
},
},
Expand Down Expand Up @@ -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) {
Expand Down
9 changes: 9 additions & 0 deletions agent-inject/agent/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ type AgentConfig struct {
Address string
AuthType string
AuthPath string
AuthConfigExtraArgs map[string]string
VaultNamespace string
Namespace string
RevokeOnShutdown bool
Expand Down Expand Up @@ -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
}
Expand Down
6 changes: 3 additions & 3 deletions agent-inject/agent/annotations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
},
{
Expand All @@ -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",
},
},
{
Expand All @@ -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",
},
},
}
Expand Down
8 changes: 4 additions & 4 deletions agent-inject/agent/container_sidecar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
},
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions agent-inject/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type Handler struct {
VaultCACertBytes string
VaultAuthType string
VaultAuthPath string
VaultAuthConfigExtraArgs map[string]string
VaultNamespace string
ProxyAddress string
ImageVault string
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions agent-inject/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestHandlerHandle(t *testing.T) {
VolumeMounts: []corev1.VolumeMount{
{
Name: "foobar",
MountPath: "serviceaccount/somewhere",
MountPath: "kubernetes.io/serviceaccount/somewhere",
},
},
},
Expand All @@ -49,7 +49,7 @@ func TestHandlerHandle(t *testing.T) {
VolumeMounts: []corev1.VolumeMount{
{
Name: "foobar",
MountPath: "serviceaccount/somewhere",
MountPath: "kubernetes.io/serviceaccount/somewhere",
},
},
},
Expand Down
23 changes: 23 additions & 0 deletions subcommand/injector/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions subcommand/injector/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`

Expand Down Expand Up @@ -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
}
Expand Down
1 change: 1 addition & 0 deletions subcommand/injector/flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down

0 comments on commit 224c1ee

Please sign in to comment.