Skip to content
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
6 changes: 3 additions & 3 deletions lib/client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,9 +327,9 @@ type Config struct {
// SessionID is a session ID to use when opening a new session.
SessionID string

// extraEnvs contains additional environment variables that will be added
// ExtraEnvs contains additional environment variables that will be added
// to SSH session.
extraEnvs map[string]string
ExtraEnvs map[string]string

// InteractiveCommand tells tsh to launch a remote exec command in interactive mode,
// i.e. attaching the terminal to it.
Expand Down Expand Up @@ -3039,7 +3039,7 @@ func (tc *TeleportClient) newSessionEnv() map[string]string {
env[sshutils.SessionEnvVar] = tc.SessionID
}

for key, val := range tc.extraEnvs {
for key, val := range tc.ExtraEnvs {
env[key] = val
}
return env
Expand Down
6 changes: 3 additions & 3 deletions lib/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -620,10 +620,10 @@ func (c *NodeClient) RunCommand(ctx context.Context, command []string, opts ...R
// AddEnv add environment variable to SSH session. This method needs to be called
// before the session is created.
func (c *NodeClient) AddEnv(key, value string) {
if c.TC.extraEnvs == nil {
c.TC.extraEnvs = make(map[string]string)
if c.TC.ExtraEnvs == nil {
c.TC.ExtraEnvs = make(map[string]string)
}
c.TC.extraEnvs[key] = value
c.TC.ExtraEnvs[key] = value
}

func (c *NodeClient) handleGlobalRequests(ctx context.Context, requestCh <-chan *ssh.Request) {
Expand Down
10 changes: 9 additions & 1 deletion tool/tsh/common/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ var supportedOptions = map[string]setOption{
"RequestTTY": setRequestTTYOption,
"RhostsRSAAuthentication": nil,
"RSAAuthentication": nil,
"SendEnv": nil,
"SendEnv": setSendEnvOption,
"ServerAliveInterval": nil,
"ServerAliveCountMax": nil,
"StreamLocalBindMask": nil,
Expand Down Expand Up @@ -157,6 +157,9 @@ type Options struct {
// ForwardX11Timeout specifies a timeout in seconds after which X11 forwarding
// attempts will be rejected when in untrusted forwarding mode.
ForwardX11Timeout time.Duration

// SendEnvVariables is a list of local environment variables to send to remote host.
SendEnvVariables []string
}

type setOption func(*Options, string) error
Expand Down Expand Up @@ -227,6 +230,11 @@ func setRequestTTYOption(o *Options, val string) error {
return nil
}

func setSendEnvOption(o *Options, val string) error {
o.SendEnvVariables = append(o.SendEnvVariables, val)
return nil
}

func setStrictHostKeyCheckingOption(o *Options, val string) error {
parsedValue, err := parseBoolOption(val)
if err != nil {
Expand Down
17 changes: 17 additions & 0 deletions tool/tsh/common/tsh.go
Original file line number Diff line number Diff line change
Expand Up @@ -4498,6 +4498,9 @@ func loadClientConfigFromCLIConf(cf *CLIConf, proxy string) (*client.Config, err
logger.InfoContext(ctx, "X11 forwarding is not properly configured, continuing without it", "error", err)
}

// send variables from user env
setEnvVariables(c, options)

// If the caller does not want to check host keys, pass in a insecure host
// key checker.
if !options.StrictHostKeyChecking {
Expand Down Expand Up @@ -4574,6 +4577,20 @@ func loadClientConfigFromCLIConf(cf *CLIConf, proxy string) (*client.Config, err
return c, nil
}

// setEnvVariables configures extra env variables to send in client config based on the requested options.
// We match OpenSSH behavior: if the requested env var is not set (os.LookupEnv return false), we won't send it.
func setEnvVariables(c *client.Config, options Options) {
if c.ExtraEnvs == nil {
c.ExtraEnvs = map[string]string{}
}
for _, variable := range options.SendEnvVariables {
value, found := os.LookupEnv(variable)
if found {
c.ExtraEnvs[variable] = value
}
}
}

func initClientStore(cf *CLIConf, proxy string) (*client.Store, error) {
switch {
case cf.IdentityFileIn != "":
Expand Down
43 changes: 43 additions & 0 deletions tool/tsh/common/tsh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7174,3 +7174,46 @@ func TestSCP(t *testing.T) {
})
}
}

func TestSetEnvVariables(t *testing.T) {
testCases := []struct {
name string
envVars map[string]string
sendEnvVariables []string
expectedExtraEnvs map[string]string
}{
{
name: "Skip unset var",
envVars: map[string]string{
"TEST_VAR1": "value1",
"TEST_VAR2": "value2",
},
sendEnvVariables: []string{"TEST_VAR1", "TEST_VAR2", "UNSET_VAR"},
expectedExtraEnvs: map[string]string{
"TEST_VAR1": "value1",
"TEST_VAR2": "value2",
},
},
{
name: "Sending empty var",
envVars: map[string]string{"EMPTY_VAR": ""},
sendEnvVariables: []string{"EMPTY_VAR"},
expectedExtraEnvs: map[string]string{"EMPTY_VAR": ""},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for k, v := range tc.envVars {
t.Setenv(k, v)
}

c := &client.Config{}
options := Options{SendEnvVariables: tc.sendEnvVariables}

setEnvVariables(c, options)

require.Equal(t, tc.expectedExtraEnvs, c.ExtraEnvs)
})
}
}
Loading