Skip to content

Commit

Permalink
[v13] Fixed an issue with tsh aws ssm start-session (#30668)
Browse files Browse the repository at this point in the history
* Fixed `tsh aws ssm start-session`

* a little more context
  • Loading branch information
greedy52 committed Aug 21, 2023
1 parent c14e079 commit 1f17e86
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 0 deletions.
42 changes: 42 additions & 0 deletions tool/tsh/app_aws.go
Expand Up @@ -21,6 +21,7 @@ import (
"net"
"os"
"os/exec"
"strings"
"sync"

awsarn "github.com/aws/aws-sdk-go/aws/arn"
Expand All @@ -46,6 +47,11 @@ func onAWS(cf *CLIConf) error {
return trace.Wrap(err)
}

if shouldUseAWSEndpointURLMode(cf) {
log.Debugf("Forcing endpoint URL mode for AWS command %q.", cf.AWSCommandArgs)
cf.AWSEndpointURLMode = true
}

err = awsApp.StartLocalProxies()
if err != nil {
return trace.Wrap(err)
Expand All @@ -71,6 +77,42 @@ func onAWS(cf *CLIConf) error {
return awsApp.RunCommand(cmd)
}

func shouldUseAWSEndpointURLMode(cf *CLIConf) bool {
// `aws ssm start-session` first calls ssm.<region>.amazonaws.com to get an
// stream URL and an token. Then it makes a wss connection with the
// provided token to the provided stream URL. The wss request currently
// respects HTTPS_PROXY but does not respect local CA bundle we provided
// thus causing a failure. Even if this is resolved one day, the wss send
// the token through websocket data channel for authentication, instead of
// sigv4, which likely we won't support.
//
// When using the endpoint URL mode, only the first request goes through
// Teleport Proxy. The wss connection does not respect the endpoint URL and
// goes to AWS directly (thus working fine).
//
// Reference:
// https://github.com/aws/session-manager-plugin/
return isAWSCommand(cf, "ssm start-session")
}

func isAWSCommand(cf *CLIConf, wantCommand string) bool {
return strings.Join(removeAWSCommandFlags(cf.AWSCommandArgs), " ") == wantCommand
}

func removeAWSCommandFlags(args []string) (ret []string) {
for i := 0; i < len(args); i++ {
arg := args[i]
switch {
case strings.HasPrefix(arg, "--"):
i++
continue
default:
ret = append(ret, arg)
}
}
return
}

// awsApp is an AWS app that can start local proxies to serve AWS APIs.
type awsApp struct {
cf *CLIConf
Expand Down
17 changes: 17 additions & 0 deletions tool/tsh/app_aws_test.go
Expand Up @@ -142,6 +142,23 @@ func TestAWS(t *testing.T) {
setCmdRunner(validateCmd),
)
require.NoError(t, err)

t.Run("aws ssm start-session", func(t *testing.T) {
// Validate --endpoint-url 127.0.0.1:<port> is added to the command.
validateCmd := func(cmd *exec.Cmd) error {
require.Len(t, cmd.Args, 9)
require.Equal(t, []string{"aws", "ssm", "--region", "us-west-1", "start-session", "--target", "target-id", "--endpoint-url"}, cmd.Args[:8])
require.Contains(t, cmd.Args[8], "127.0.0.1:")
return nil
}
err = Run(
context.Background(),
[]string{"aws", "ssm", "--region", "us-west-1", "start-session", "--target", "target-id"},
setHomePath(tmpHomePath),
setCmdRunner(validateCmd),
)
require.NoError(t, err)
})
}

func makeUserWithAWSRole(t *testing.T) (types.User, types.Role) {
Expand Down

0 comments on commit 1f17e86

Please sign in to comment.