From 958ccda6b1ccd3c96843bfe1bcdd160a85b473a1 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Thu, 25 May 2023 09:23:56 -0400 Subject: [PATCH] agent: Add implementation for injecting secrets as environment variables to vault agent cmd (#20739) * added exec and env_template config/parsing * add tests * we can reuse ctconfig here * do not create a non-nil map * check defaults * Apply suggestions from code review Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com> * first go of exec server Signed-off-by: Daniel Huckins * convert to list Signed-off-by: Daniel Huckins * convert to list Signed-off-by: Daniel Huckins * sig test Signed-off-by: Daniel Huckins * add failing example Signed-off-by: Daniel Huckins * refactor for config changes Signed-off-by: Daniel Huckins * add test for invalid signal Signed-off-by: Daniel Huckins * account for auth token changes Signed-off-by: Daniel Huckins * only start the runner once we have a token * tests in diff branch Signed-off-by: Daniel Huckins * Apply suggestions from code review Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com> * fix rename Signed-off-by: Daniel Huckins * Update command/agent/exec/exec.go Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com> * apply suggestions from code review Signed-off-by: Daniel Huckins * cleanup Signed-off-by: Daniel Huckins * remove unnecessary lock Signed-off-by: Daniel Huckins * refactor to use enum Signed-off-by: Daniel Huckins * dont block Signed-off-by: Daniel Huckins * handle default Signed-off-by: Daniel Huckins * make more explicit Signed-off-by: Daniel Huckins * cleanup Signed-off-by: Daniel Huckins * remove unused Signed-off-by: Daniel Huckins * remove unused file Signed-off-by: Daniel Huckins * remove test app Signed-off-by: Daniel Huckins * Apply suggestions from code review Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com> * apply suggestions from code review Signed-off-by: Daniel Huckins * update comment Signed-off-by: Daniel Huckins * add changelog Signed-off-by: Daniel Huckins * new channel for exec server token * wire to run with vault agent Signed-off-by: Daniel Huckins * watch for child process to exit on its own Signed-off-by: Daniel Huckins * block before returning Signed-off-by: Daniel Huckins --------- Signed-off-by: Daniel Huckins Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com> --- command/agent.go | 45 +++++++++++++++++++++------ command/agent/exec/exec.go | 1 + command/agentproxyshared/auth/auth.go | 16 ++++++++++ go.sum | 2 -- 4 files changed, 53 insertions(+), 11 deletions(-) diff --git a/command/agent.go b/command/agent.go index 10f49afa4..e080f1eda 100644 --- a/command/agent.go +++ b/command/agent.go @@ -6,6 +6,7 @@ package command import ( "context" "crypto/tls" + "errors" "flag" "fmt" "io" @@ -24,8 +25,17 @@ import ( "github.com/hashicorp/go-secure-stdlib/gatedwriter" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/go-secure-stdlib/reloadutil" + "github.com/kr/pretty" + "github.com/mitchellh/cli" + "github.com/oklog/run" + "github.com/posener/complete" + "golang.org/x/text/cases" + "golang.org/x/text/language" + "google.golang.org/grpc/test/bufconn" + "github.com/hashicorp/vault/api" agentConfig "github.com/hashicorp/vault/command/agent/config" + "github.com/hashicorp/vault/command/agent/exec" "github.com/hashicorp/vault/command/agent/template" "github.com/hashicorp/vault/command/agentproxyshared" "github.com/hashicorp/vault/command/agentproxyshared/auth" @@ -42,13 +52,6 @@ import ( "github.com/hashicorp/vault/sdk/helper/consts" "github.com/hashicorp/vault/sdk/logical" "github.com/hashicorp/vault/version" - "github.com/kr/pretty" - "github.com/mitchellh/cli" - "github.com/oklog/run" - "github.com/posener/complete" - "golang.org/x/text/cases" - "golang.org/x/text/language" - "google.golang.org/grpc/test/bufconn" ) var ( @@ -271,7 +274,7 @@ func (c *AgentCommand) Run(args []string) int { "functionality, plan to move to Vault Proxy instead.") } - // ctx and cancelFunc are passed to the AuthHandler, SinkServer, and + // ctx and cancelFunc are passed to the AuthHandler, SinkServer, ExecServer and // TemplateServer that periodically listen for ctx.Done() to fire and shut // down accordingly. ctx, cancelFunc := context.WithCancel(context.Background()) @@ -724,6 +727,14 @@ func (c *AgentCommand) Run(args []string) int { ExitAfterAuth: config.ExitAfterAuth, }) + es := exec.NewServer(&exec.ServerConfig{ + AgentConfig: c.config, + Namespace: templateNamespace, + Logger: c.logger.Named("exec.server"), + LogLevel: c.logger.GetLevel(), + LogWriter: c.logWriter, + }) + g.Add(func() error { return ah.Run(ctx, method) }, func(error) { @@ -778,6 +789,17 @@ func (c *AgentCommand) Run(args []string) int { ts.Stop() }) + g.Add(func() error { + return es.Run(ctx, ah.ExecTokenCh) + }, func(err error) { + // Let the lease cache know this is a shutdown; no need to evict + // everything + if leaseCache != nil { + leaseCache.SetShuttingDown(true) + } + cancelFunc() + }) + } // Server configuration output @@ -816,7 +838,12 @@ func (c *AgentCommand) Run(args []string) int { if err := g.Run(); err != nil { c.logger.Error("runtime error encountered", "error", err) c.UI.Error("Error encountered during run, refer to logs for more details.") - exitCode = 1 + var processExitError *exec.ProcessExitError + if errors.As(err, &processExitError) { + exitCode = processExitError.ExitCode + } else { + exitCode = 1 + } } c.notifySystemd(systemd.SdNotifyStopping) return exitCode diff --git a/command/agent/exec/exec.go b/command/agent/exec/exec.go index 692bc68b6..fa9100396 100644 --- a/command/agent/exec/exec.go +++ b/command/agent/exec/exec.go @@ -99,6 +99,7 @@ func (s *Server) Run(ctx context.Context, incomingVaultToken chan string) error if len(s.config.AgentConfig.EnvTemplates) == 0 || s.config.AgentConfig.Exec == nil { s.logger.Info("no env templates or exec config, exiting") + <-ctx.Done() return nil } diff --git a/command/agentproxyshared/auth/auth.go b/command/agentproxyshared/auth/auth.go index 776c678ea..fdcf12f94 100644 --- a/command/agentproxyshared/auth/auth.go +++ b/command/agentproxyshared/auth/auth.go @@ -13,6 +13,7 @@ import ( "github.com/armon/go-metrics" "github.com/hashicorp/go-hclog" + "github.com/hashicorp/vault/api" "github.com/hashicorp/vault/sdk/helper/jsonutil" ) @@ -52,6 +53,7 @@ type AuthConfig struct { type AuthHandler struct { OutputCh chan string TemplateTokenCh chan string + ExecTokenCh chan string token string userAgent string metricsSignifier string @@ -63,6 +65,7 @@ type AuthHandler struct { minBackoff time.Duration enableReauthOnNewCredentials bool enableTemplateTokenCh bool + enableExecTokenCh bool exitOnError bool } @@ -81,6 +84,7 @@ type AuthHandlerConfig struct { MetricsSignifier string EnableReauthOnNewCredentials bool EnableTemplateTokenCh bool + EnableExecTokenCh bool ExitOnError bool } @@ -90,6 +94,7 @@ func NewAuthHandler(conf *AuthHandlerConfig) *AuthHandler { // has been shut down, during agent/proxy shutdown, we won't block OutputCh: make(chan string, 1), TemplateTokenCh: make(chan string, 1), + ExecTokenCh: make(chan string, 1), token: conf.Token, logger: conf.Logger, client: conf.Client, @@ -99,6 +104,7 @@ func NewAuthHandler(conf *AuthHandlerConfig) *AuthHandler { maxBackoff: conf.MaxBackoff, enableReauthOnNewCredentials: conf.EnableReauthOnNewCredentials, enableTemplateTokenCh: conf.EnableTemplateTokenCh, + enableExecTokenCh: conf.EnableExecTokenCh, exitOnError: conf.ExitOnError, userAgent: conf.UserAgent, metricsSignifier: conf.MetricsSignifier, @@ -143,6 +149,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error { am.Shutdown() close(ah.OutputCh) close(ah.TemplateTokenCh) + close(ah.ExecTokenCh) ah.logger.Info("auth handler stopped") }() @@ -342,6 +349,9 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error { if ah.enableTemplateTokenCh { ah.TemplateTokenCh <- string(wrappedResp) } + if ah.enableExecTokenCh { + ah.ExecTokenCh <- string(wrappedResp) + } am.CredSuccess() backoffCfg.reset() @@ -397,6 +407,9 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error { if ah.enableTemplateTokenCh { ah.TemplateTokenCh <- token } + if ah.enableExecTokenCh { + ah.ExecTokenCh <- token + } tokenType := secret.Data["type"].(string) if tokenType == "batch" { @@ -428,6 +441,9 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error { if ah.enableTemplateTokenCh { ah.TemplateTokenCh <- secret.Auth.ClientToken } + if ah.enableExecTokenCh { + ah.ExecTokenCh <- secret.Auth.ClientToken + } } am.CredSuccess() diff --git a/go.sum b/go.sum index 6c814731c..0e99fef0d 100644 --- a/go.sum +++ b/go.sum @@ -2914,8 +2914,6 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20230519143937-03e91628a987 h1:3xJIFvzUFbu4ls0BTBYcgbCGhA63eAOEMxIHugyXJqA= -golang.org/x/exp v0.0.0-20230519143937-03e91628a987/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a h1:Jw5wfR+h9mnIYH+OtGT2im5wV1YGGDora5vTv/aa5bE=