From e8c0e21579ab01c0462aff6fa5a851229d1df8c2 Mon Sep 17 00:00:00 2001 From: Patrick Lieberg Date: Fri, 10 Jan 2025 12:18:02 -0600 Subject: [PATCH] Adding support for exit after auth as an annotation --- agent-inject/agent/agent.go | 8 +++++++ agent-inject/agent/annotations.go | 17 +++++++++++++ agent-inject/agent/annotations_test.go | 33 +++++++++++++++++++++++++- agent-inject/handler.go | 2 ++ subcommand/injector/command.go | 2 ++ subcommand/injector/flags.go | 12 ++++++++++ subcommand/injector/flags_test.go | 2 ++ 7 files changed, 75 insertions(+), 1 deletion(-) diff --git a/agent-inject/agent/agent.go b/agent-inject/agent/agent.go index 4750b5bf..7fb85b69 100644 --- a/agent-inject/agent/agent.go +++ b/agent-inject/agent/agent.go @@ -90,6 +90,9 @@ type Agent struct { // token on shutting down. RevokeOnShutdown bool + // ExitAfterAuth is used to control if the agent should exit after a successful auth. + ExitAfterAuth bool + // RevokeGrace controls after receiving the signal for pod // termination that the container will attempt to revoke its own Vault token. RevokeGrace uint64 @@ -445,6 +448,11 @@ func New(pod *corev1.Pod) (*Agent, error) { return agent, err } + agent.ExitAfterAuth, err = agent.getExitAfterAuth() + if err != nil { + return nil, err + } + agent.Containers = strings.Split(pod.Annotations[AnnotationAgentInjectContainers], ",") agent.RevokeGrace, err = agent.revokeGrace() diff --git a/agent-inject/agent/annotations.go b/agent-inject/agent/annotations.go index 1f2e46da..709f6bab 100644 --- a/agent-inject/agent/annotations.go +++ b/agent-inject/agent/annotations.go @@ -158,6 +158,9 @@ const ( // make sure it's written to `/home/vault/.vault-token`. Only supported for sidecar containers. AnnotationAgentRevokeOnShutdown = "vault.hashicorp.com/agent-revoke-on-shutdown" + // AnnotationAgentExitAfterAuth is the key of the annotation that configures whether the vault-agent sidecar will exit after successful authentication. + AnnotationAgentExitAfterAuth = "vault.hashicorp.com/agent-exit-after-auth" + // AnnotationAgentRevokeGrace sets the number of seconds after receiving the signal for pod // termination that the container will attempt to revoke its own Vault token. Defaults to 5s. AnnotationAgentRevokeGrace = "vault.hashicorp.com/agent-revoke-grace" @@ -349,6 +352,7 @@ type AgentConfig struct { VaultNamespace string Namespace string RevokeOnShutdown bool + ExitAfterAuth bool UserID string GroupID string SameID bool @@ -466,6 +470,10 @@ func Init(pod *corev1.Pod, cfg AgentConfig) error { pod.ObjectMeta.Annotations[AnnotationAgentRevokeOnShutdown] = strconv.FormatBool(cfg.RevokeOnShutdown) } + if _, ok := pod.ObjectMeta.Annotations[AnnotationAgentExitAfterAuth]; !ok { + pod.ObjectMeta.Annotations[AnnotationAgentExitAfterAuth] = strconv.FormatBool(cfg.ExitAfterAuth) + } + if _, ok := pod.ObjectMeta.Annotations[AnnotationAgentRevokeGrace]; !ok { pod.ObjectMeta.Annotations[AnnotationAgentRevokeGrace] = strconv.Itoa(DefaultRevokeGrace) } @@ -745,6 +753,15 @@ func (a *Agent) revokeOnShutdown() (bool, error) { return parseutil.ParseBool(raw) } +func (a *Agent) getExitAfterAuth() (bool, error) { + raw, ok := a.Annotations[AnnotationAgentExitAfterAuth] + if !ok { + return false, nil + } + + return parseutil.ParseBool(raw) +} + func (a *Agent) revokeGrace() (uint64, error) { raw, ok := a.Annotations[AnnotationAgentRevokeGrace] if !ok { diff --git a/agent-inject/agent/annotations_test.go b/agent-inject/agent/annotations_test.go index 757e0964..e53a11cb 100644 --- a/agent-inject/agent/annotations_test.go +++ b/agent-inject/agent/annotations_test.go @@ -40,6 +40,7 @@ func basicAgentConfig() AgentConfig { ResourceLimitCPU: DefaultResourceLimitCPU, ResourceLimitMem: DefaultResourceLimitMem, ExitOnRetryFailure: DefaultTemplateConfigExitOnRetryFailure, + ExitAfterAuth: true, } } @@ -71,7 +72,7 @@ func TestInitCanSet(t *testing.T) { } if raw != tt.annotationValue { - t.Errorf("Default annotation confiured value incorrect, wanted %s, got %s", tt.annotationValue, raw) + t.Errorf("Default annotation configured value incorrect, wanted %s, got %s", tt.annotationValue, raw) } } } @@ -927,6 +928,18 @@ func TestCouldErrorAnnotations(t *testing.T) { {AnnotationAgentAuthMaxBackoff, "1s", true}, {AnnotationAgentAuthMaxBackoff, "1m", true}, {AnnotationAgentAuthMaxBackoff, "x", false}, + + {AnnotationAgentExitAfterAuth, "true", true}, + {AnnotationAgentExitAfterAuth, "false", true}, + {AnnotationAgentExitAfterAuth, "TRUE", true}, + {AnnotationAgentExitAfterAuth, "FALSE", true}, + {AnnotationAgentExitAfterAuth, "0", true}, + {AnnotationAgentExitAfterAuth, "1", true}, + {AnnotationAgentExitAfterAuth, "t", true}, + {AnnotationAgentExitAfterAuth, "f", true}, + {AnnotationAgentExitAfterAuth, "tRuE", false}, + {AnnotationAgentExitAfterAuth, "fAlSe", false}, + {AnnotationAgentExitAfterAuth, "", true}, } for i, tt := range tests { @@ -1365,6 +1378,24 @@ func TestAutoAuthExitOnError(t *testing.T) { require.Equal(t, true, agent.AutoAuthExitOnError) } +func TestExitAfterAuth(t *testing.T) { + pod := testPod(map[string]string{ + "vault.hashicorp.com/agent-exit-after-auth": "true", + }) + agentConfig := basicAgentConfig() + err := Init(pod, agentConfig) + if err != nil { + t.Errorf("got error, shouldn't have: %s", err) + } + + agent, err := New(pod) + if err != nil { + t.Errorf("got error, shouldn't have: %s", err) + } + + require.Equal(t, true, agent.ExitAfterAuth) +} + func TestDisableIdleConnections(t *testing.T) { tests := map[string]struct { annotations map[string]string diff --git a/agent-inject/handler.go b/agent-inject/handler.go index f30282cb..37aab4c8 100644 --- a/agent-inject/handler.go +++ b/agent-inject/handler.go @@ -59,6 +59,7 @@ type Handler struct { Clientset *kubernetes.Clientset Log hclog.Logger RevokeOnShutdown bool + ExitAfterAuth bool UserID string GroupID string SameID bool @@ -229,6 +230,7 @@ func (h *Handler) Mutate(req *admissionv1.AdmissionRequest) MutateResponse { ProxyAddress: h.ProxyAddress, Namespace: req.Namespace, RevokeOnShutdown: h.RevokeOnShutdown, + ExitAfterAuth: h.ExitAfterAuth, UserID: h.UserID, GroupID: h.GroupID, SameID: h.SameID, diff --git a/subcommand/injector/command.go b/subcommand/injector/command.go index a9dd601b..77fdbc1f 100644 --- a/subcommand/injector/command.go +++ b/subcommand/injector/command.go @@ -64,6 +64,7 @@ type Command struct { flagVaultAuthPath string // Mount path of the Vault Auth Method flagVaultNamespace string // Vault enterprise namespace flagRevokeOnShutdown bool // Revoke Vault Token on pod shutdown + flagExitAfterAuth bool // Exit after successful auth flagRunAsUser string // User (uid) to run Vault agent as flagRunAsGroup string // Group (gid) to run Vault agent as flagRunAsSameUser bool // Run Vault agent as the User (uid) of the first application container @@ -208,6 +209,7 @@ func (c *Command) Run(args []string) int { RequireAnnotation: true, Log: logger, RevokeOnShutdown: c.flagRevokeOnShutdown, + ExitAfterAuth: c.flagExitAfterAuth, UserID: c.flagRunAsUser, GroupID: c.flagRunAsGroup, SameID: c.flagRunAsSameUser, diff --git a/subcommand/injector/flags.go b/subcommand/injector/flags.go index 468cec2e..6a66e0b8 100644 --- a/subcommand/injector/flags.go +++ b/subcommand/injector/flags.go @@ -92,6 +92,9 @@ type Specification struct { // RevokeOnShutdown is AGENT_INJECT_REVOKE_ON_SHUTDOWN environment variable. RevokeOnShutdown string `split_words:"true"` + // ExitAfterAuth is the AGENT_INJECT_EXIT_AFTER_AUTH environment variable. + ExitAfterAuth string `split_words:"true"` + // RunAsUser is the AGENT_INJECT_RUN_AS_USER environment variable. (uid) RunAsUser string `envconfig:"AGENT_INJECT_RUN_AS_USER"` @@ -187,6 +190,8 @@ func (c *Command) init() { c.flagSet.StringVar(&c.flagVaultNamespace, "vault-namespace", "", "Vault enterprise namespace.") c.flagSet.BoolVar(&c.flagRevokeOnShutdown, "revoke-on-shutdown", false, "Automatically revoke Vault Token on Pod termination.") + c.flagSet.BoolVar(&c.flagExitAfterAuth, "exit-after-auth", false, + "Exit after successful authentication to Vault.") c.flagSet.StringVar(&c.flagRunAsUser, "run-as-user", strconv.Itoa(agent.DefaultAgentRunAsUser), fmt.Sprintf("User (uid) to run Vault agent as. Defaults to %d.", agent.DefaultAgentRunAsUser)) c.flagSet.StringVar(&c.flagRunAsGroup, "run-as-group", strconv.Itoa(agent.DefaultAgentRunAsGroup), @@ -352,6 +357,13 @@ func (c *Command) parseEnvs() error { } } + if envs.ExitAfterAuth != "" { + c.flagExitAfterAuth, err = parseutil.ParseBool(envs.ExitAfterAuth) + if err != nil { + return err + } + } + if envs.RunAsUser != "" { c.flagRunAsUser = envs.RunAsUser } diff --git a/subcommand/injector/flags_test.go b/subcommand/injector/flags_test.go index 25cfbea1..c27cb1a4 100644 --- a/subcommand/injector/flags_test.go +++ b/subcommand/injector/flags_test.go @@ -172,6 +172,8 @@ func TestCommandEnvBools(t *testing.T) { }{ {env: "AGENT_INJECT_REVOKE_ON_SHUTDOWN", value: true, cmdPtr: &cmd.flagRevokeOnShutdown}, {env: "AGENT_INJECT_REVOKE_ON_SHUTDOWN", value: false, cmdPtr: &cmd.flagRevokeOnShutdown}, + {env: "AGENT_INJECT_EXIT_AFTER_AUTH", value: true, cmdPtr: &cmd.flagExitAfterAuth}, + {env: "AGENT_INJECT_EXIT_AFTER_AUTH", value: false, cmdPtr: &cmd.flagExitAfterAuth}, {env: "AGENT_INJECT_RUN_AS_SAME_USER", value: true, cmdPtr: &cmd.flagRunAsSameUser}, {env: "AGENT_INJECT_RUN_AS_SAME_USER", value: false, cmdPtr: &cmd.flagRunAsSameUser}, {env: "AGENT_INJECT_SET_SECURITY_CONTEXT", value: true, cmdPtr: &cmd.flagSetSecurityContext},