From ad47ef17e8ba7a2db78b05a5dca85db0db7460d1 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 5 Oct 2023 15:41:05 -0500 Subject: [PATCH] feat: allow reading the agent token from a file (#10080) Adds `CODER_AGENT_TOKEN_FILE` which will read the agent token from a file if `CODER_AGENT_TOKEN` is not provided. Using a Kubernetes Secret with a volume-mounted file is a more secure way to provide the agent token instead of an environment variable. --- cli/agent.go | 16 +++++++++++++--- cli/root.go | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/cli/agent.go b/cli/agent.go index 6a06f4d454..8a836cd4c3 100644 --- a/cli/agent.go +++ b/cli/agent.go @@ -199,9 +199,19 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd { var exchangeToken func(context.Context) (agentsdk.AuthenticateResponse, error) switch auth { case "token": - token, err := inv.ParsedFlags().GetString(varAgentToken) - if err != nil { - return xerrors.Errorf("CODER_AGENT_TOKEN must be set for token auth: %w", err) + token, _ := inv.ParsedFlags().GetString(varAgentToken) + if token == "" { + tokenFile, _ := inv.ParsedFlags().GetString(varAgentTokenFile) + if tokenFile != "" { + tokenBytes, err := os.ReadFile(tokenFile) + if err != nil { + return xerrors.Errorf("read token file %q: %w", tokenFile, err) + } + token = strings.TrimSpace(string(tokenBytes)) + } + } + if token == "" { + return xerrors.Errorf("CODER_AGENT_TOKEN or CODER_AGENT_TOKEN_FILE must be set for token auth") } client.SetSessionToken(token) case "google-instance-identity": diff --git a/cli/root.go b/cli/root.go index ab7dfae5f6..d75187d3f4 100644 --- a/cli/root.go +++ b/cli/root.go @@ -55,6 +55,7 @@ const ( varURL = "url" varToken = "token" varAgentToken = "agent-token" + varAgentTokenFile = "agent-token-file" varAgentURL = "agent-url" varHeader = "header" varHeaderCommand = "header-command" @@ -71,7 +72,9 @@ const ( envSessionToken = "CODER_SESSION_TOKEN" //nolint:gosec envAgentToken = "CODER_AGENT_TOKEN" - envURL = "CODER_URL" + //nolint:gosec + envAgentTokenFile = "CODER_AGENT_TOKEN_FILE" + envURL = "CODER_URL" ) var errUnauthenticated = xerrors.New(notLoggedInMessage) @@ -328,6 +331,14 @@ func (r *RootCmd) Command(subcommands []*clibase.Cmd) (*clibase.Cmd, error) { Hidden: true, Group: globalGroup, }, + { + Flag: varAgentTokenFile, + Env: envAgentTokenFile, + Description: "A file containing an agent authentication token.", + Value: clibase.StringOf(&r.agentTokenFile), + Hidden: true, + Group: globalGroup, + }, { Flag: varAgentURL, Env: "CODER_AGENT_URL", @@ -446,19 +457,20 @@ func LoggerFromContext(ctx context.Context) (slog.Logger, bool) { // RootCmd contains parameters and helpers useful to all commands. type RootCmd struct { - clientURL *url.URL - token string - globalConfig string - header []string - headerCommand string - agentToken string - agentURL *url.URL - forceTTY bool - noOpen bool - verbose bool - versionFlag bool - disableDirect bool - debugHTTP bool + clientURL *url.URL + token string + globalConfig string + header []string + headerCommand string + agentToken string + agentTokenFile string + agentURL *url.URL + forceTTY bool + noOpen bool + verbose bool + versionFlag bool + disableDirect bool + debugHTTP bool noVersionCheck bool noFeatureWarning bool