Skip to content

fix: Use instance identity session token for git subcommands #4884

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const (

type Options struct {
Filesystem afero.Fs
ExchangeToken func(ctx context.Context) error
ExchangeToken func(ctx context.Context) (string, error)
Client Client
ReconnectingPTYTimeout time.Duration
EnvironmentVariables map[string]string
Expand All @@ -78,6 +78,11 @@ func New(options Options) io.Closer {
if options.Filesystem == nil {
options.Filesystem = afero.NewOsFs()
}
if options.ExchangeToken == nil {
options.ExchangeToken = func(ctx context.Context) (string, error) {
return "", nil
}
}
ctx, cancelFunc := context.WithCancel(context.Background())
server := &agent{
reconnectingPTYTimeout: options.ReconnectingPTYTimeout,
Expand All @@ -97,7 +102,7 @@ func New(options Options) io.Closer {
type agent struct {
logger slog.Logger
client Client
exchangeToken func(ctx context.Context) error
exchangeToken func(ctx context.Context) (string, error)
filesystem afero.Fs

reconnectingPTYs sync.Map
Expand All @@ -110,8 +115,9 @@ type agent struct {

envVars map[string]string
// metadata is atomic because values can change after reconnection.
metadata atomic.Value
sshServer *ssh.Server
metadata atomic.Value
sessionToken atomic.Pointer[string]
sshServer *ssh.Server

network *tailnet.Conn
stats *Stats
Expand Down Expand Up @@ -147,14 +153,13 @@ func (a *agent) run(ctx context.Context) error {
// This allows the agent to refresh it's token if necessary.
// For instance identity this is required, since the instance
// may not have re-provisioned, but a new agent ID was created.
if a.exchangeToken != nil {
err := a.exchangeToken(ctx)
if err != nil {
return xerrors.Errorf("exchange token: %w", err)
}
sessionToken, err := a.exchangeToken(ctx)
if err != nil {
return xerrors.Errorf("exchange token: %w", err)
}
a.sessionToken.Store(&sessionToken)

err := a.client.PostWorkspaceAgentVersion(ctx, buildinfo.Version())
err = a.client.PostWorkspaceAgentVersion(ctx, buildinfo.Version())
if err != nil {
return xerrors.Errorf("update workspace agent version: %w", err)
}
Expand Down Expand Up @@ -576,6 +581,9 @@ func (a *agent) createCommand(ctx context.Context, rawCommand string, env []stri
unixExecutablePath := strings.ReplaceAll(executablePath, "\\", "/")
cmd.Env = append(cmd.Env, fmt.Sprintf(`GIT_SSH_COMMAND=%s gitssh --`, unixExecutablePath))

// Specific Coder subcommands require the agent token exposed!
cmd.Env = append(cmd.Env, fmt.Sprintf("CODER_AGENT_TOKEN=%s", *a.sessionToken.Load()))

// Set SSH connection environment variables (these are also set by OpenSSH
// and thus expected to be present by SSH clients). Since the agent does
// networking in-memory, trying to provide accurate values here would be
Expand Down
8 changes: 4 additions & 4 deletions agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,9 +532,9 @@ func TestAgent(t *testing.T) {
}
initialized := atomic.Int32{}
closer := agent.New(agent.Options{
ExchangeToken: func(ctx context.Context) error {
ExchangeToken: func(ctx context.Context) (string, error) {
initialized.Add(1)
return nil
return "", nil
},
Client: client,
Logger: slogtest.Make(t, nil).Leveled(slog.LevelInfo),
Expand Down Expand Up @@ -565,8 +565,8 @@ func TestAgent(t *testing.T) {
}
filesystem := afero.NewMemMapFs()
closer := agent.New(agent.Options{
ExchangeToken: func(ctx context.Context) error {
return nil
ExchangeToken: func(ctx context.Context) (string, error) {
return "", nil
},
Client: client,
Logger: slogtest.Make(t, nil).Leveled(slog.LevelInfo),
Expand Down
13 changes: 5 additions & 8 deletions cli/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,22 +156,19 @@ func workspaceAgent() *cobra.Command {
closer := agent.New(agent.Options{
Client: client,
Logger: logger,
ExchangeToken: func(ctx context.Context) error {
ExchangeToken: func(ctx context.Context) (string, error) {
if exchangeToken == nil {
return nil
return client.SessionToken, nil
}
resp, err := exchangeToken(ctx)
if err != nil {
return err
return "", err
}
client.SessionToken = resp.SessionToken
return nil
return "", nil
},
EnvironmentVariables: map[string]string{
// Override the "CODER_AGENT_TOKEN" variable in all
// shells so "gitssh" and "gitaskpass" works!
"CODER_AGENT_TOKEN": client.SessionToken,
"GIT_ASKPASS": executablePath,
"GIT_ASKPASS": executablePath,
},
})
<-cmd.Context().Done()
Expand Down