From 11477fcb8d22102aaa90f36b4a279f37478a3ab8 Mon Sep 17 00:00:00 2001 From: rosstimothy <39066650+rosstimothy@users.noreply.github.com> Date: Tue, 18 Jun 2024 07:00:05 -0400 Subject: [PATCH 01/20] Remove lib/shell from lib/client (#43119) (#43141) The shell package requires cgo and by importing it in lib/client, which all client tools have in their dependency tree, it prevents them from building without GCO_ENBALED=1. Updates https://github.com/gravitational/teleport/issues/43112. --- integration/helpers/trustedclusters.go | 2 +- integration/integration_test.go | 76 +++++++++---------- integration/port_forwarding_test.go | 2 +- integration/proxy/proxy_helpers.go | 2 +- integration/proxy/proxy_test.go | 4 +- .../proxy/proxy_tunnel_strategy_test.go | 2 +- lib/benchmark/ssh.go | 2 +- lib/client/api.go | 51 +++++-------- lib/client/conntest/ssh.go | 2 +- tool/tsh/common/tsh.go | 35 ++++++++- 10 files changed, 98 insertions(+), 80 deletions(-) diff --git a/integration/helpers/trustedclusters.go b/integration/helpers/trustedclusters.go index 393a8f7b7cbf0..452db624bb0c3 100644 --- a/integration/helpers/trustedclusters.go +++ b/integration/helpers/trustedclusters.go @@ -218,7 +218,7 @@ func CheckTrustedClustersCanConnect(ctx context.Context, t *testing.T, tcSetup T cmd := []string{"echo", "hello world"} require.Eventually(t, func() bool { - return tc.SSH(ctx, cmd, false) == nil + return tc.SSH(ctx, cmd) == nil }, 10*time.Second, 1*time.Second, "Two clusters cannot connect to each other") require.Equal(t, "hello world\n", output.String()) diff --git a/integration/integration_test.go b/integration/integration_test.go index 95fcd570999d9..b941374a54033 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -272,7 +272,7 @@ func testDifferentPinnedIP(t *testing.T, suite *integrationTestSuite) { require.NoError(t, err) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - test.errAssertion(t, cl.SSH(ctx, []string{"echo hi"}, false)) + test.errAssertion(t, cl.SSH(ctx, []string{"echo hi"})) }) } } @@ -486,7 +486,7 @@ func testAuditOn(t *testing.T, suite *integrationTestSuite) { cl.Stdout = myTerm cl.Stdin = myTerm - err = cl.SSH(context.TODO(), []string{}, false) + err = cl.SSH(context.TODO(), []string{}) endC <- err }() @@ -756,7 +756,7 @@ func testInteroperability(t *testing.T, suite *integrationTestSuite) { go func() { // don't check for err, because sometimes this process should fail // with an error and that's what the test is checking for. - cl.SSH(context.TODO(), []string{tt.inCommand}, false) + cl.SSH(context.TODO(), []string{tt.inCommand}) sessionEndC <- true }() err = waitFor(sessionEndC, time.Second*10) @@ -948,7 +948,7 @@ func testSSHTracker(t *testing.T, suite *integrationTestSuite) { cl.Stdout = personA cl.Stdin = personA personA.Type("\aecho hi\n\r") - go cl.SSH(ctx, []string{}, false) + go cl.SSH(ctx, []string{}) condition := func() bool { // verify that the tracker was created @@ -1030,7 +1030,7 @@ func testSessionRecordingModes(t *testing.T, suite *integrationTestSuite) { cl.Stdout = term cl.Stdin = term - errCh <- cl.SSH(ctx, []string{}, false) + errCh <- cl.SSH(ctx, []string{}) }() return term, errCh @@ -1392,7 +1392,7 @@ func testEscapeSequenceTriggers(t *testing.T, suite *integrationTestSuite) { cl.Stdin = terminal sess := make(chan error) go func() { - sess <- cl.SSH(ctx, []string{}, false) + sess <- cl.SSH(ctx, []string{}) }() require.Eventually(t, func() bool { @@ -1851,7 +1851,7 @@ func verifySessionJoin(t *testing.T, username string, teleport *helpers.TeleInst cl.Stdin = personA // Person A types something into the terminal (including "exit") personA.Type("\aecho hi\n\r\aexit\n\r\a") - sessionA <- cl.SSH(context.TODO(), []string{}, false) + sessionA <- cl.SSH(context.TODO(), []string{}) } // PersonB: wait for a session to become available, then join: @@ -1937,7 +1937,7 @@ func testShutdown(t *testing.T, suite *integrationTestSuite) { sshCtx, sshCancel := context.WithCancel(context.Background()) t.Cleanup(sshCancel) go func() { - sshErr <- tc.SSH(sshCtx, nil, false) + sshErr <- tc.SSH(sshCtx, nil) sshCancel() }() }, @@ -2166,7 +2166,7 @@ func testClientIdleConnection(t *testing.T, suite *integrationTestSuite) { // Terminate the session after 3x the idle timeout ctx, cancel := context.WithTimeout(context.Background(), netConfig.GetClientIdleTimeout()*3) defer cancel() - sessionErr <- cl.SSH(ctx, nil, false) + sessionErr <- cl.SSH(ctx, nil) } go openSession() @@ -2362,7 +2362,7 @@ func runDisconnectTest(t *testing.T, suite *integrationTestSuite, tc disconnectT cl.Stdout = person cl.Stdin = person - err = cl.SSH(ctx, []string{}, false) + err = cl.SSH(ctx, []string{}) select { case <-ctx.Done(): // either we timed out, or a different session @@ -2476,7 +2476,7 @@ func testEnvironmentVariables(t *testing.T, suite *integrationTestSuite) { out := &bytes.Buffer{} tc.Stdout = out tc.Stdin = nil - err = tc.SSH(ctx, cmd, false /* runLocally */) + err = tc.SSH(ctx, cmd) require.NoError(t, err) output := out.String() require.Contains(t, output, tc.SessionID) @@ -2485,7 +2485,7 @@ func testEnvironmentVariables(t *testing.T, suite *integrationTestSuite) { term := NewTerminal(250) tc.Stdout = term tc.Stdin = strings.NewReader(strings.Join(cmd, " ") + "\r\nexit\r\n") - err = tc.SSH(ctx, nil, false /* runLocally */) + err = tc.SSH(ctx, nil) require.NoError(t, err) output = term.AllOutput() require.Contains(t, output, tc.SessionID) @@ -2514,7 +2514,7 @@ func testInvalidLogins(t *testing.T, suite *integrationTestSuite) { }) require.NoError(t, err) - err = tc.SSH(context.Background(), cmd, false) + err = tc.SSH(context.Background(), cmd) require.ErrorIs(t, err, trace.NotFound("failed to dial target host\n\tlooking up remote cluster \"wrong-site\"\n\t\tnot found")) } @@ -2653,7 +2653,7 @@ func twoClustersTunnel(t *testing.T, suite *integrationTestSuite, now time.Time, }) require.NoError(t, err) tc.Stdout = &outputA - err = tc.SSH(ctx, cmd, false) + err = tc.SSH(ctx, cmd) require.NoError(t, err) require.Equal(t, "hello world\n", outputA.String()) @@ -2697,7 +2697,7 @@ func twoClustersTunnel(t *testing.T, suite *integrationTestSuite, now time.Time, }) require.NoError(t, err) tc.Stdout = &outputB - err = tc.SSH(ctx, cmd, false) + err = tc.SSH(ctx, cmd) require.NoError(t, err) require.Equal(t, outputA.String(), outputB.String()) @@ -2725,7 +2725,7 @@ func twoClustersTunnel(t *testing.T, suite *integrationTestSuite, now time.Time, // Stop "site-A" and try to connect to it again via "site-A" (expect a connection error) require.NoError(t, a.StopAuth(false)) - err = tc.SSH(ctx, cmd, false) + err = tc.SSH(ctx, cmd) require.IsType(t, err, trace.ConnectionProblem(nil, "")) // Reset and start "Site-A" again @@ -2738,7 +2738,7 @@ func twoClustersTunnel(t *testing.T, suite *integrationTestSuite, now time.Time, // and 'tc' (client) is also supposed to reconnect var sshErr error tcHasReconnected := func() bool { - sshErr = tc.SSH(ctx, cmd, false) + sshErr = tc.SSH(ctx, cmd) return sshErr == nil } require.Eventually(t, tcHasReconnected, 10*time.Second, 250*time.Millisecond, @@ -2865,7 +2865,7 @@ func testHA(t *testing.T, suite *integrationTestSuite) { // and 'tc' (client) is also supposed to reconnect for i := 0; i < 10; i++ { time.Sleep(time.Millisecond * 50) - err = tc.SSH(ctx, cmd, false) + err = tc.SSH(ctx, cmd) if err == nil { break } @@ -2898,7 +2898,7 @@ func testHA(t *testing.T, suite *integrationTestSuite) { // and 'tc' (client) is also supposed to reconnect for i := 0; i < 30; i++ { time.Sleep(1 * time.Second) - err = tc.SSH(ctx, cmd, false) + err = tc.SSH(ctx, cmd) if err == nil { break } @@ -3022,7 +3022,7 @@ func testMapRoles(t *testing.T, suite *integrationTestSuite) { // and 'tc' (client) is also supposed to reconnect for i := 0; i < 10; i++ { time.Sleep(time.Millisecond * 50) - err = tc.SSH(context.TODO(), cmd, false) + err = tc.SSH(context.TODO(), cmd) if err == nil { break } @@ -3360,7 +3360,7 @@ func trustedClusters(t *testing.T, suite *integrationTestSuite, test trustedClus require.NoError(t, err) for i := 0; i < 10; i++ { time.Sleep(time.Millisecond * 50) - err = tc.SSH(ctx, cmd, false) + err = tc.SSH(ctx, cmd) if err == nil { break } @@ -3392,7 +3392,7 @@ func trustedClusters(t *testing.T, suite *integrationTestSuite, test trustedClus require.NoError(t, aux.Process.GetAuthServer().DeleteTrustedCluster(ctx, trustedCluster.GetName())) for i := 0; i < 10; i++ { time.Sleep(time.Millisecond * 50) - err = tc.SSH(ctx, cmd, false) + err = tc.SSH(ctx, cmd) if err != nil { break } @@ -3418,7 +3418,7 @@ func trustedClusters(t *testing.T, suite *integrationTestSuite, test trustedClus tc.Stdout = output for i := 0; i < 10; i++ { time.Sleep(time.Millisecond * 50) - err = tc.SSH(ctx, cmd, false) + err = tc.SSH(ctx, cmd) if err == nil { break } @@ -3841,7 +3841,7 @@ func testTrustedTunnelNode(t *testing.T, suite *integrationTestSuite) { require.NoError(t, err) for i := 0; i < 10; i++ { time.Sleep(time.Millisecond * 50) - err = tc.SSH(context.TODO(), cmd, false) + err = tc.SSH(context.TODO(), cmd) if err == nil { break } @@ -3863,7 +3863,7 @@ func testTrustedTunnelNode(t *testing.T, suite *integrationTestSuite) { // Use assert package to get access to the returned error. In this way we can log it. if !assert.Eventually(t, func() bool { - err = tunnelClient.SSH(context.Background(), cmd, false) + err = tunnelClient.SSH(context.Background(), cmd) return err == nil }, 10*time.Second, 200*time.Millisecond) { require.FailNow(t, "Failed to established SSH connection", err) @@ -5127,7 +5127,7 @@ func testAuditOff(t *testing.T, suite *integrationTestSuite) { } cl.Stdout = myTerm cl.Stdin = myTerm - err = cl.SSH(ctx, []string{}, false) + err = cl.SSH(ctx, []string{}) endCh <- err }() @@ -5354,7 +5354,7 @@ func testPAM(t *testing.T, suite *integrationTestSuite) { cl.Stdin = termSession termSession.Type("\aecho hi\n\r\aexit\n\r\a") - err = cl.SSH(context.TODO(), []string{}, false) + err = cl.SSH(context.TODO(), []string{}) if !isSSHError(err) { errCh <- err return @@ -6023,7 +6023,7 @@ func runAndMatch(tc *client.TeleportClient, attempts int, command []string, patt tc.Stdout = output var err error for i := 0; i < attempts; i++ { - err = tc.SSH(context.TODO(), command, false) + err = tc.SSH(context.TODO(), command) if err != nil { time.Sleep(500 * time.Millisecond) continue @@ -6069,7 +6069,7 @@ func testWindowChange(t *testing.T, suite *integrationTestSuite) { cl.Stdout = personA cl.Stdin = personA - err = cl.SSH(ctx, []string{}, false) + err = cl.SSH(ctx, []string{}) if !isSSHError(err) { require.NoError(t, err) } @@ -6553,7 +6553,7 @@ func testBPFInteractive(t *testing.T, suite *integrationTestSuite) { // "Type" a command into the terminal. term.Type(fmt.Sprintf("\a%v\n\r\aexit\n\r\a", lsPath)) - err = client.SSH(context.TODO(), []string{}, false) + err = client.SSH(context.TODO(), []string{}) require.NoError(t, err) // Signal that the client has finished the interactive session. @@ -6799,7 +6799,7 @@ func testSSHExitCode(t *testing.T, suite *integrationTestSuite) { } // run the ssh command - err = cli.SSH(doneContext, tt.command, false) + err = cli.SSH(doneContext, tt.command) tt.errorAssertion(t, err) // check that the exit code of the session matches the expected one @@ -6886,7 +6886,7 @@ func testBPFSessionDifferentiation(t *testing.T, suite *integrationTestSuite) { // "Type" a command into the terminal. term.Type(fmt.Sprintf("\a%v\n\r\aexit\n\r\a", lsPath)) - err = client.SSH(context.Background(), []string{}, false) + err = client.SSH(context.Background(), []string{}) if err != nil { t.Errorf("Failed to start SSH session: %v.", err) } @@ -7300,7 +7300,7 @@ func runCommandWithCertReissue(t *testing.T, instance *helpers.TeleInstance, cmd out := &bytes.Buffer{} tc.Stdout = out - err = tc.SSH(context.TODO(), cmd, false) + err = tc.SSH(context.TODO(), cmd) if err != nil { return trace.Wrap(err) } @@ -7324,7 +7324,7 @@ func runCommandWithContext(ctx context.Context, t *testing.T, instance *helpers. }() tc.Stdout = write for i := 0; i < attempts; i++ { - err = tc.SSH(ctx, cmd, false) + err = tc.SSH(ctx, cmd) if err == nil { break } @@ -9054,7 +9054,7 @@ func TestConnectivityWithoutAuth(t *testing.T) { defer cancel() errChan := make(chan error, 1) go func() { - errChan <- cli.SSH(ctx, test.command, false) + errChan <- cli.SSH(ctx, test.command) }() t.Run("auth running", func(t *testing.T) { @@ -9071,7 +9071,7 @@ func TestConnectivityWithoutAuth(t *testing.T) { ctx, cancel = context.WithTimeout(context.Background(), 15*time.Second) defer cancel() go func() { - errChan <- cli.SSH(ctx, test.command, false) + errChan <- cli.SSH(ctx, test.command) }() t.Run("auth not running", func(t *testing.T) { @@ -9199,7 +9199,7 @@ func TestConnectivityDuringAuthRestart(t *testing.T) { errChan := make(chan error, 1) go func() { - errChan <- cli.SSH(ctx, nil, false) + errChan <- cli.SSH(ctx, nil) }() // validate that the session is active @@ -9384,7 +9384,7 @@ func testModeratedSessions(t *testing.T, suite *integrationTestSuite) { cl.WebauthnLogin = customWebauthnLogin cl.Stdout = peerTerminal cl.Stdin = peerTerminal - if err := cl.SSH(ctx, []string{}, false); err != nil { + if err := cl.SSH(ctx, []string{}); err != nil { cancel(trace.Wrap(err, "peer session failed")) return } diff --git a/integration/port_forwarding_test.go b/integration/port_forwarding_test.go index dddf800a4e5bc..70263d571b0b0 100644 --- a/integration/port_forwarding_test.go +++ b/integration/port_forwarding_test.go @@ -248,7 +248,7 @@ func testPortForwarding(t *testing.T, suite *integrationTestSuite) { cl.Labels = tt.labels sshSessionCtx, sshSessionCancel := context.WithCancel(context.Background()) - go cl.SSH(sshSessionCtx, []string{}, false) + go cl.SSH(sshSessionCtx, []string{}) defer sshSessionCancel() timeout, cancel := context.WithTimeout(context.Background(), 5*time.Second) diff --git a/integration/proxy/proxy_helpers.go b/integration/proxy/proxy_helpers.go index ad9fdca7b6071..834ad5310389c 100644 --- a/integration/proxy/proxy_helpers.go +++ b/integration/proxy/proxy_helpers.go @@ -235,7 +235,7 @@ func (p *Suite) mustConnectToClusterAndRunSSHCommand(t *testing.T, config helper cmd := []string{"echo", "hello world"} err = retryutils.RetryStaticFor(deadline, nextIterWaitTime, func() error { - err = tc.SSH(context.TODO(), cmd, false) + err = tc.SSH(context.TODO(), cmd) return trace.Wrap(err) }) require.NoError(t, err) diff --git a/integration/proxy/proxy_test.go b/integration/proxy/proxy_test.go index b8b0e2427e3ad..be93eea65abfa 100644 --- a/integration/proxy/proxy_test.go +++ b/integration/proxy/proxy_test.go @@ -1479,14 +1479,14 @@ func TestALPNProxyDialProxySSHWithoutInsecureMode(t *testing.T) { // Try to connect to the separate proxy SSH listener. tc.TLSRoutingEnabled = false - err = tc.SSH(ctx, cmd, false) + err = tc.SSH(ctx, cmd) require.NoError(t, err) require.Equal(t, "hello world\n", output.String()) output.Reset() // Try to connect to the ALPN SNI Listener. tc.TLSRoutingEnabled = true - err = tc.SSH(ctx, cmd, false) + err = tc.SSH(ctx, cmd) require.NoError(t, err) require.Equal(t, "hello world\n", output.String()) } diff --git a/integration/proxy/proxy_tunnel_strategy_test.go b/integration/proxy/proxy_tunnel_strategy_test.go index 39476614d9036..1b8c497c8083a 100644 --- a/integration/proxy/proxy_tunnel_strategy_test.go +++ b/integration/proxy/proxy_tunnel_strategy_test.go @@ -226,7 +226,7 @@ func (p *proxyTunnelStrategy) dialNode(t *testing.T) { client.Stdout = output cmd := []string{"echo", "hello world"} - err = client.SSH(context.Background(), cmd, false) + err = client.SSH(context.Background(), cmd) require.NoError(t, err) require.Equal(t, "hello world\n", output.String()) } diff --git a/lib/benchmark/ssh.go b/lib/benchmark/ssh.go index 3184a3c055d79..705393fd006d9 100644 --- a/lib/benchmark/ssh.go +++ b/lib/benchmark/ssh.go @@ -65,7 +65,7 @@ func (s SSHBenchmark) BenchBuilder(ctx context.Context, tc *client.TeleportClien opts = append(opts, client.WithHostAddress(chooseRandomHost(resources))) } - return tc.SSH(ctx, s.Command, false, opts...) + return tc.SSH(ctx, s.Command, opts...) }, nil } diff --git a/lib/client/api.go b/lib/client/api.go index f010bac537dab..1f0e998fc2475 100644 --- a/lib/client/api.go +++ b/lib/client/api.go @@ -27,7 +27,6 @@ import ( "net" "net/url" "os" - "os/exec" "path/filepath" "runtime" "strconv" @@ -83,7 +82,6 @@ import ( "github.com/gravitational/teleport/lib/observability/tracing" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/session" - "github.com/gravitational/teleport/lib/shell" alpncommon "github.com/gravitational/teleport/lib/srv/alpnproxy/common" "github.com/gravitational/teleport/lib/sshutils" "github.com/gravitational/teleport/lib/sshutils/sftp" @@ -1535,6 +1533,10 @@ type SSHOptions struct { // HostAddress is the address of the target host. If specified it // will be used instead of the target provided when `tsh ssh` was invoked. HostAddress string + // LocalCommandExecutor should be used to execute the command on the local + // machine. If provided, it will be used instead of establishing a connection + // to the target host and executing the command remotely. + LocalCommandExecutor func(string, []string) error } // WithHostAddress returns a SSHOptions which overrides the @@ -1545,11 +1547,19 @@ func WithHostAddress(addr string) func(*SSHOptions) { } } +// WithLocalCommandExecutor returns a SSHOptions which specifies +// an executor that should be used to invoke commands locally. +func WithLocalCommandExecutor(executor func(string, []string) error) func(*SSHOptions) { + return func(opt *SSHOptions) { + opt.LocalCommandExecutor = executor + } +} + // SSH connects to a node and, if 'command' is specified, executes the command on it, // otherwise runs interactive shell // // Returns nil if successful, or (possibly) *exec.ExitError -func (tc *TeleportClient) SSH(ctx context.Context, command []string, runLocally bool, opts ...func(*SSHOptions)) error { +func (tc *TeleportClient) SSH(ctx context.Context, command []string, opts ...func(*SSHOptions)) error { ctx, span := tc.Tracer.Start( ctx, "teleportClient/SSH", @@ -1589,7 +1599,7 @@ func (tc *TeleportClient) SSH(ctx context.Context, command []string, runLocally if len(nodeAddrs) > 1 { return tc.runShellOrCommandOnMultipleNodes(ctx, clt, nodeAddrs, command) } - return tc.runShellOrCommandOnSingleNode(ctx, clt, nodeAddrs[0], command, runLocally) + return tc.runShellOrCommandOnSingleNode(ctx, clt, nodeAddrs[0], command, options.LocalCommandExecutor) } // ConnectToNode attempts to establish a connection to the node resolved to by the provided @@ -1790,7 +1800,7 @@ func (tc *TeleportClient) connectToNodeWithMFA(ctx context.Context, clt *Cluster return nodeClient, trace.Wrap(err) } -func (tc *TeleportClient) runShellOrCommandOnSingleNode(ctx context.Context, clt *ClusterClient, nodeAddr string, command []string, runLocally bool) error { +func (tc *TeleportClient) runShellOrCommandOnSingleNode(ctx context.Context, clt *ClusterClient, nodeAddr string, command []string, commandExecutor func(string, []string) error) error { cluster := clt.ClusterName() ctx, span := tc.Tracer.Start( ctx, @@ -1845,11 +1855,11 @@ func (tc *TeleportClient) runShellOrCommandOnSingleNode(ctx context.Context, clt // After port forwarding, run a local command that uses the connection, and // then disconnect. - if runLocally { + if commandExecutor != nil { if len(tc.Config.LocalForwardPorts) == 0 { fmt.Println("Executing command locally without connecting to any servers. This makes no sense.") } - return runLocalCommand(tc.Config.HostLogin, command) + return commandExecutor(tc.Config.HostLogin, command) } if len(command) > 0 { @@ -1880,7 +1890,7 @@ func (tc *TeleportClient) runShellOrCommandOnMultipleNodes(ctx context.Context, // Issue "shell" request to the first matching node. fmt.Printf("\x1b[1mWARNING\x1b[0m: Multiple nodes match the label selector, picking first: %q\n", nodeAddrs[0]) - return tc.runShellOrCommandOnSingleNode(ctx, clt, nodeAddrs[0], nil, false) + return tc.runShellOrCommandOnSingleNode(ctx, clt, nodeAddrs[0], nil, nil) } func (tc *TeleportClient) startPortForwarding(ctx context.Context, nodeClient *NodeClient) error { @@ -4864,31 +4874,6 @@ func ParseSearchKeywords(spec string, customDelimiter rune) []string { return tokens } -// Executes the given command on the client machine (localhost). If no command is given, -// executes shell -func runLocalCommand(hostLogin string, command []string) error { - if len(command) == 0 { - if hostLogin == "" { - user, err := apiutils.CurrentUser() - if err != nil { - return trace.Wrap(err) - } - hostLogin = user.Username - } - shell, err := shell.GetLoginShell(hostLogin) - if err != nil { - return trace.Wrap(err) - } - command = []string{shell} - } - - cmd := exec.Command(command[0], command[1:]...) - cmd.Stderr = os.Stderr - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - return cmd.Run() -} - // String returns the same string spec which can be parsed by ParsePortForwardSpec. func (fp ForwardedPorts) String() (retval []string) { for _, p := range fp { diff --git a/lib/client/conntest/ssh.go b/lib/client/conntest/ssh.go index dcda6993d7246..417144386587b 100644 --- a/lib/client/conntest/ssh.go +++ b/lib/client/conntest/ssh.go @@ -193,7 +193,7 @@ func (s *SSHConnectionTester) TestConnection(ctx context.Context, req TestConnec ctxWithTimeout, cancelFunc := context.WithTimeout(ctx, req.DialTimeout) defer cancelFunc() - if err := tc.SSH(ctxWithTimeout, []string{"whoami"}, false); err != nil { + if err := tc.SSH(ctxWithTimeout, []string{"whoami"}); err != nil { return s.handleErrFromSSH(ctx, connectionDiagnosticID, req.SSHPrincipal, err, processStdout, currentUser, req) } diff --git a/tool/tsh/common/tsh.go b/tool/tsh/common/tsh.go index bf193bd6af0af..f1992ef57d51a 100644 --- a/tool/tsh/common/tsh.go +++ b/tool/tsh/common/tsh.go @@ -62,6 +62,7 @@ import ( "github.com/gravitational/teleport/api/types/accesslist" apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/api/types/wrappers" + apiutils "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/api/utils/prompt" "github.com/gravitational/teleport/lib/asciitable" @@ -79,6 +80,7 @@ import ( "github.com/gravitational/teleport/lib/observability/tracing" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/session" + "github.com/gravitational/teleport/lib/shell" "github.com/gravitational/teleport/lib/sshutils/sftp" "github.com/gravitational/teleport/lib/sshutils/x11" "github.com/gravitational/teleport/lib/tlsca" @@ -3415,6 +3417,31 @@ func onSSHLatency(cf *CLIConf) error { return trace.Wrap(showLatency(cf.Context, clt.ProxyClient, targetPinger, "Proxy", tc.Host)) } +// Executes the given command on the client machine (localhost). If no command is given, +// executes shell +func runLocalCommand(hostLogin string, command []string) error { + if len(command) == 0 { + if hostLogin == "" { + user, err := apiutils.CurrentUser() + if err != nil { + return trace.Wrap(err) + } + hostLogin = user.Username + } + shell, err := shell.GetLoginShell(hostLogin) + if err != nil { + return trace.Wrap(err) + } + command = []string{shell} + } + + cmd := exec.Command(command[0], command[1:]...) + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + return cmd.Run() +} + // onSSH executes 'tsh ssh' command func onSSH(cf *CLIConf) error { tc, err := makeClient(cf) @@ -3427,7 +3454,13 @@ func onSSH(cf *CLIConf) error { tc.Stdin = os.Stdin err = retryWithAccessRequest(cf, tc, func() error { err = client.RetryWithRelogin(cf.Context, tc, func() error { - return tc.SSH(cf.Context, cf.RemoteCommand, cf.LocalExec) + + var opts []func(*client.SSHOptions) + if cf.LocalExec { + opts = append(opts, client.WithLocalCommandExecutor(runLocalCommand)) + } + + return tc.SSH(cf.Context, cf.RemoteCommand, opts...) }) if err != nil { if strings.Contains(utils.UserMessageFromError(err), teleport.NodeIsAmbiguous) { From 42e6c00aedfedd5e096a7b13d13baec2ce880808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Cie=C5=9Blak?= Date: Tue, 18 Jun 2024 15:38:54 +0200 Subject: [PATCH 02/20] [v14] Rotate Connect logs in ascending order (#43163) * Rotate Connect logs in ascending order * Limit number of log files in packaged app --- web/packages/teleterm/src/services/logger/loggerService.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/packages/teleterm/src/services/logger/loggerService.ts b/web/packages/teleterm/src/services/logger/loggerService.ts index 07516724030a1..b2d693709669c 100644 --- a/web/packages/teleterm/src/services/logger/loggerService.ts +++ b/web/packages/teleterm/src/services/logger/loggerService.ts @@ -85,9 +85,10 @@ export function createFileLoggerService( transports: [ new transports.File({ maxsize: 4194304, // 4 MB - max size of a single file - maxFiles: 5, + maxFiles: opts.dev ? 5 : 3, dirname: opts.dir, filename: `${opts.name}.log`, + tailable: true, }), ], }); From c4788467caf0d69ea49ac1d20d7fe119c9ea3197 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Tue, 18 Jun 2024 15:57:27 +0100 Subject: [PATCH 03/20] [v14] [buddy] Prevent unnecessary Jamf service validation when it is disabled (#43170) * Prevent unnecessary Jamf service validation when it is disabled (#43095) Signed-off-by: minhthong582000 <55283557+minhthong582000@users.noreply.github.com> Signed-off-by: Tiago Silva Co-authored-by: minhthong582000 <55283557+minhthong582000@users.noreply.github.com> * fix test --------- Signed-off-by: minhthong582000 <55283557+minhthong582000@users.noreply.github.com> Signed-off-by: Tiago Silva Co-authored-by: minhthong582000 <55283557+minhthong582000@users.noreply.github.com> --- lib/config/configuration.go | 8 ++++---- lib/config/configuration_test.go | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/config/configuration.go b/lib/config/configuration.go index 08917548f8c0e..9ce0154fa5b42 100644 --- a/lib/config/configuration.go +++ b/lib/config/configuration.go @@ -586,10 +586,10 @@ func ApplyFileConfig(fc *FileConfig, cfg *servicecfg.Config) error { } } - // Apply regardless of Jamf being enabled. - // If a config is present, we want it to be valid. - if err := applyJamfConfig(fc, cfg); err != nil { - return trace.Wrap(err) + if fc.Jamf.Enabled() { + if err := applyJamfConfig(fc, cfg); err != nil { + return trace.Wrap(err) + } } return nil diff --git a/lib/config/configuration_test.go b/lib/config/configuration_test.go index d181b176cede1..ea66f59069a84 100644 --- a/lib/config/configuration_test.go +++ b/lib/config/configuration_test.go @@ -3596,13 +3596,12 @@ jamf_service: yaml: `jamf_service: {}`, }, { - name: "disabled config is validated", + name: "disabled config ignored", yaml: ` jamf_service: enabled: false api_endpoint: https://yourtenant.jamfcloud.com username: llama`, - wantErr: "password_file", }, } for _, test := range tests { From 164ad7c2082e7187fb55763b84ad60054b6cbdd6 Mon Sep 17 00:00:00 2001 From: Alan Parra Date: Tue, 18 Jun 2024 13:19:27 -0300 Subject: [PATCH 04/20] chore: Bump Buf to v1.33.0 (#43120) (#43132) * chore: Bump Buf to v1.33.0 * Update generated protos --- build.assets/versions.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.assets/versions.mk b/build.assets/versions.mk index a9fb44271e236..fa71893fdf9a1 100644 --- a/build.assets/versions.mk +++ b/build.assets/versions.mk @@ -16,7 +16,7 @@ LIBPCSCLITE_VERSION ?= 1.9.9-teleport DEVTOOLSET ?= devtoolset-12 # Protogen related versions. -BUF_VERSION ?= v1.32.1 +BUF_VERSION ?= v1.33.0 # Keep in sync with api/proto/buf.yaml (and buf.lock). GOGO_PROTO_TAG ?= v1.3.2 NODE_GRPC_TOOLS_VERSION ?= 1.12.4 From 13d86ac4a805f758d68aabdb41f2a7205e695423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Cie=C5=9Blak?= Date: Tue, 18 Jun 2024 18:25:44 +0200 Subject: [PATCH 05/20] Refactor story decorators to enable use of useTheme (#43167) --- web/.storybook/preview.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/web/.storybook/preview.js b/web/.storybook/preview.js index 99902b747d919..61591f7ce8602 100644 --- a/web/.storybook/preview.js +++ b/web/.storybook/preview.js @@ -16,7 +16,7 @@ limitations under the License. import React from 'react'; import { rest, setupWorker } from 'msw'; -import { addDecorator, addParameters } from '@storybook/react'; +import { addParameters } from '@storybook/react'; import { darkTheme, lightTheme, @@ -46,7 +46,7 @@ if (typeof global.process === 'undefined') { history.init(); // wrap each story with theme provider -const ThemeDecorator = (storyFn, meta) => { +const ThemeDecorator = (Story, meta) => { let ThemeProvider; let theme; @@ -73,27 +73,29 @@ const ThemeDecorator = (storyFn, meta) => { return ( - {storyFn()} + + + ); }; // wrap stories with an argument of {userContext: true} with user context provider -const UserDecorator = (storyFn, meta) => { +const UserDecorator = (Story, meta) => { if (meta.args.userContext) { const UserProvider = UserContextProvider; return ( - {storyFn()} + ); } - return {storyFn()}; + return ; }; -addDecorator(UserDecorator); -addDecorator(ThemeDecorator); +export const decorators = [UserDecorator, ThemeDecorator]; + addParameters({ options: { showPanel: false, From 548d633ea887f49212248c6756159da789c7985c Mon Sep 17 00:00:00 2001 From: Gus Luxton Date: Tue, 18 Jun 2024 13:58:37 -0300 Subject: [PATCH 06/20] docs: Add 502 Bad Gateway to the list of potential errors (#43097) (#43111) * docs: Add 502 Bad Gateway to the list of potential errors * Also remove surplus tab list The instructions for OSS and Commercial are identical, so we don't need tabs any more. --- .../deploy-a-cluster/helm-deployments/aws.mdx | 15 --------------- docs/pages/management/admin/troubleshooting.mdx | 4 ++-- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/docs/pages/deploy-a-cluster/helm-deployments/aws.mdx b/docs/pages/deploy-a-cluster/helm-deployments/aws.mdx index 32731c88d0825..bd9094b1367d6 100644 --- a/docs/pages/deploy-a-cluster/helm-deployments/aws.mdx +++ b/docs/pages/deploy-a-cluster/helm-deployments/aws.mdx @@ -677,8 +677,6 @@ $ aws route53 get-change --id "${CHANGEID?}" | jq '.ChangeInfo.Status' Create a user to be able to log into Teleport. This needs to be done on the Teleport auth server, so we can run the command using `kubectl`: - - ```code $ kubectl --namespace exec deploy/-auth -- tctl users add test --roles=access,editor @@ -687,19 +685,6 @@ https://teleport.example.com:443/web/invite/91cfbd08bc89122275006e48b516cc68 NOTE: Make sure teleport.example.com:443 points at a Teleport proxy that users can access. ``` - - -```code -$ kubectl --namespace teleport exec deploy/teleport-auth -- tctl users add test --roles=access,editor,reviewer - -User "test" has been created but requires a password. Share this URL with the user to complete user setup, link is valid for 1h: -https://teleport.example.com:443/web/invite/91cfbd08bc89122275006e48b516cc68 - -NOTE: Make sure teleport.example.com:443 points at a Teleport proxy that users can access. -``` - - - Load the user creation link to create a password and set up multi-factor authentication for the Teleport user via the web UI. diff --git a/docs/pages/management/admin/troubleshooting.mdx b/docs/pages/management/admin/troubleshooting.mdx index 295120f99f78a..1fb2d45b15867 100644 --- a/docs/pages/management/admin/troubleshooting.mdx +++ b/docs/pages/management/admin/troubleshooting.mdx @@ -142,7 +142,7 @@ Teleport v9.0.4 git: go1.18 -If you have a question or need assistance please submit a request +If you have a question or need assistance please submit a request through the [Teleport support portal](https://support.goteleport.com). @@ -189,7 +189,7 @@ tunnel via the Proxy Service. This means that `teleport.cluster.local` appears in log messages that show the URL of a request made to the Auth Service, and does not explicitly indicate that something is misconfigured. -### `ssh: overflow reading version string` +### `ssh: overflow reading version string` and/or `502: Bad Gateway` errors (!docs/pages/includes/tls-multiplexing-warnings.mdx!) From 38472bf13570f2cf3c41ac430b50f3c4c2bb9c3f Mon Sep 17 00:00:00 2001 From: Steven Martin Date: Tue, 18 Jun 2024 15:28:07 -0400 Subject: [PATCH 07/20] [v14] docs: networking proxy update (#43207) * docs: networking proxy update * docs: update comment on public address --- docs/pages/reference/networking.mdx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/pages/reference/networking.mdx b/docs/pages/reference/networking.mdx index d0cd4a18a3902..a21e3b46f8eeb 100644 --- a/docs/pages/reference/networking.mdx +++ b/docs/pages/reference/networking.mdx @@ -14,9 +14,18 @@ configuration file. The public address can take an IP or a DNS name. It can also be a list of values: ```yaml -public_addr: ["proxy-one.example.com", "proxy-two.example.com"] +public_addr: ["service1.example.com", "service2.example.com"] ``` + + Only a single Proxy Service `public_addr` should be configured. Attempting + to have multiple addresses can result in redirects to the first listed address + that may not be available to the client. + + Specifying a public address for a Teleport service may be useful in the following use cases: From 3c5b111dcb6305be61bc61be3082092e8e783997 Mon Sep 17 00:00:00 2001 From: Paul Schisa <75806143+pschisa@users.noreply.github.com> Date: Tue, 18 Jun 2024 16:55:39 -0400 Subject: [PATCH 08/20] [v14] Update metrics.mdx with better clarity for heartbeat_connections_received_total (#43215) * Update metrics.mdx with better clarity for heartbeat_connections_received_total adding clarity to heartbeat_connections_received_total due to confusion around what a heartbeat connection represented. Confirmed that the metric counts the number of times an agent has connected to the endpoint on auth used to periodically send heartbeats. So it represents the number of agents actively heartbeating rather than the number of heartbeats received over the lifespan of that particular auth instance. * Update docs/pages/includes/metrics.mdx Co-authored-by: Paul Gottschling * Update metrics.mdx change heartbeating to heart beating --------- Co-authored-by: Paul Gottschling --- docs/pages/includes/metrics.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/includes/metrics.mdx b/docs/pages/includes/metrics.mdx index 7886388f04742..95d4e858bd73e 100644 --- a/docs/pages/includes/metrics.mdx +++ b/docs/pages/includes/metrics.mdx @@ -46,7 +46,7 @@ | `grpc_server_handled_total` | counter | Teleport Auth | Total number of RPCs completed on the server, regardless of success or failure. | | `grpc_server_msg_received_total` | counter | Teleport Auth | Total number of RPC stream messages received on the server. | | `grpc_server_msg_sent_total` | counter | Teleport Auth | Total number of gRPC stream messages sent by the server. | -| `heartbeat_connections_received_total` | counter | Teleport Auth | Number of times the Auth Service received a heartbeat connection. | +| `heartbeat_connections_received_total` | counter | Teleport Auth | Number of times the Auth Service received a heartbeat connection, representing total heart beating Agents. | | `s3_requests_total` | counter | Amazon S3 | Total number of requests to the S3 API. | | `s3_requests` | counter | Amazon S3 | Total number of requests to the S3 API grouped by result. | | `s3_requests_seconds` | histogram | Amazon S3 | Request latency for the S3 API. | From 63b701d258b91b55fff9b39a7aae27e93f30b38d Mon Sep 17 00:00:00 2001 From: Grzegorz Zdunek Date: Wed, 19 Jun 2024 08:43:28 +0200 Subject: [PATCH 09/20] Update electron, node-pty and Node.js (#43098) (#43193) (cherry picked from commit 899f12a7fe34a21842a8158139257867f9ca4ce2) --- build.assets/versions.mk | 2 +- web/packages/build/package.json | 2 +- web/packages/teleterm/package.json | 4 ++-- yarn.lock | 24 ++++++++++++------------ 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/build.assets/versions.mk b/build.assets/versions.mk index fa71893fdf9a1..d1f6ece58eca6 100644 --- a/build.assets/versions.mk +++ b/build.assets/versions.mk @@ -6,7 +6,7 @@ GOLANG_VERSION ?= go1.21.11 GOLANGCI_LINT_VERSION ?= v1.58.1 -NODE_VERSION ?= 20.13.0 +NODE_VERSION ?= 20.14.0 # Run lint-rust check locally before merging code after you bump this. RUST_VERSION ?= 1.77.0 diff --git a/web/packages/build/package.json b/web/packages/build/package.json index 877a5c5b6b8b7..a4b69f09a6384 100644 --- a/web/packages/build/package.json +++ b/web/packages/build/package.json @@ -49,7 +49,7 @@ "@testing-library/user-event": "^14.4.3", "@types/jest": "^29.5.6", "@types/jsdom": "^21.1.0", - "@types/node": "^20.12.11", + "@types/node": "^20.14.2", "@types/react": "^16.8.19", "@types/react-router-dom": "^5.1.1", "@types/react-transition-group": "^4.4.5", diff --git a/web/packages/teleterm/package.json b/web/packages/teleterm/package.json index 60b972538d095..1440081c913a7 100644 --- a/web/packages/teleterm/package.json +++ b/web/packages/teleterm/package.json @@ -29,7 +29,7 @@ "dependencies": { "@types/tar-fs": "^2.0.1", "emittery": "^1.0.1", - "node-pty": "1.1.0-beta12", + "node-pty": "1.1.0-beta14", "strip-ansi": "^7.1.0", "tar-fs": "^3.0.3" }, @@ -43,7 +43,7 @@ "@types/whatwg-url": "^11.0.1", "clean-webpack-plugin": "4.0.0", "cross-env": "5.0.5", - "electron": "30.0.3", + "electron": "31.0.1", "electron-notarize": "^1.2.1", "eslint-import-resolver-webpack": "0.13.2", "eslint-loader": "3.0.3", diff --git a/yarn.lock b/yarn.lock index 0c4454695227e..9ce3b37da2acc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4271,10 +4271,10 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@^20.12.11", "@types/node@^20.9.0": - version "20.12.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.11.tgz#c4ef00d3507000d17690643278a60dc55a9dc9be" - integrity sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw== +"@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@^20.14.2", "@types/node@^20.9.0": + version "20.14.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.2.tgz#a5f4d2bcb4b6a87bffcaa717718c5a0f208f4a18" + integrity sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q== dependencies: undici-types "~5.26.4" @@ -7634,10 +7634,10 @@ electron-to-chromium@^1.4.668: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.772.tgz#7a1efabf86b20e5709736ce64dbc2ce13cb68936" integrity sha512-jFfEbxR/abTTJA3ci+2ok1NTuOBBtB4jH+UT6PUmRN+DY3WSD4FFRsgoVQ+QNIJ0T7wrXwzsWCI2WKC46b++2A== -electron@30.0.3: - version "30.0.3" - resolved "https://registry.yarnpkg.com/electron/-/electron-30.0.3.tgz#7c25ddb12ba89fd117991d010f1b274b1bafcb73" - integrity sha512-h+suwx6e0fnv/9wi0/cmCMtG+4LrPzJZa+3DEEpxcPcP+pcWnBI70t8QspxgMNIh2wzXLMD9XVqrLkEbiBAInw== +electron@31.0.1: + version "31.0.1" + resolved "https://registry.yarnpkg.com/electron/-/electron-31.0.1.tgz#0039524f8f38c24da802c3b18a42c3951acb5897" + integrity sha512-2eBcp4iqLkTsml6mMq+iqrS5u3kJ/2mpOLP7Mj7lo0uNK3OyfNqRS9z1ArsHjBF2/HV250Te/O9nKrwQRTX/+g== dependencies: "@electron/get" "^2.0.0" "@types/node" "^20.9.0" @@ -12532,10 +12532,10 @@ node-modules-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= -node-pty@1.1.0-beta12: - version "1.1.0-beta12" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.1.0-beta12.tgz#702f8c05ac1d175dcbc17901f1c66ee5d67b27cd" - integrity sha512-xhWrczF9AN+TnIZoHcSiclt4dkD1IcncUOzcdgx3by0jwctt54ZgWEp68O0lE0D8ydxa/bk3nA9sWEDhDNJuwg== +node-pty@1.1.0-beta14: + version "1.1.0-beta14" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.1.0-beta14.tgz#e6e9eeb3ac7186144d17902977f9da011e513563" + integrity sha512-w/o/q6UL95azHQhrKvMnIojs1AGiqxnjNgcH3138H/RIRkIpE8S5GvT4dpvQT+ZVbTJxoBFt/DnaBAT1PAN7WQ== dependencies: node-addon-api "^7.1.0" From bad4a9c3e63e2ac51b1f9500a15035352b5a2508 Mon Sep 17 00:00:00 2001 From: Gus Luxton Date: Wed, 19 Jun 2024 11:31:05 -0300 Subject: [PATCH 10/20] docs: Restore reviewer role to AWS Helm guide (#43239) --- .../deploy-a-cluster/helm-deployments/aws.mdx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/pages/deploy-a-cluster/helm-deployments/aws.mdx b/docs/pages/deploy-a-cluster/helm-deployments/aws.mdx index bd9094b1367d6..5d61d5fb25924 100644 --- a/docs/pages/deploy-a-cluster/helm-deployments/aws.mdx +++ b/docs/pages/deploy-a-cluster/helm-deployments/aws.mdx @@ -677,6 +677,8 @@ $ aws route53 get-change --id "${CHANGEID?}" | jq '.ChangeInfo.Status' Create a user to be able to log into Teleport. This needs to be done on the Teleport auth server, so we can run the command using `kubectl`: + + ```code $ kubectl --namespace exec deploy/-auth -- tctl users add test --roles=access,editor @@ -685,6 +687,18 @@ https://teleport.example.com:443/web/invite/91cfbd08bc89122275006e48b516cc68 NOTE: Make sure teleport.example.com:443 points at a Teleport proxy that users can access. ``` + + +```code +$ kubectl --namespace exec deploy/-auth -- tctl users add test --roles=access,editor,reviewer + +User "test" has been created but requires a password. Share this URL with the user to complete user setup, link is valid for 1h: +https://teleport.example.com:443/web/invite/91cfbd08bc89122275006e48b516cc68 + +NOTE: Make sure teleport.example.com:443 points at a Teleport proxy that users can access. +``` + + Load the user creation link to create a password and set up multi-factor authentication for the Teleport user via the web UI. From b6e5d242dcc60b20ca13af613aa0f67dfdfa2cf9 Mon Sep 17 00:00:00 2001 From: Gus Luxton Date: Wed, 19 Jun 2024 15:20:36 -0300 Subject: [PATCH 11/20] ami: Add install section to teleport-acm unit file (#43213) (#43258) --- assets/aws/files/system/teleport-acm.service | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/assets/aws/files/system/teleport-acm.service b/assets/aws/files/system/teleport-acm.service index 231f68ee4ca98..eb847e69510a9 100644 --- a/assets/aws/files/system/teleport-acm.service +++ b/assets/aws/files/system/teleport-acm.service @@ -13,4 +13,7 @@ RuntimeDirectory=teleport ExecStart=/usr/local/bin/teleport start --config=/etc/teleport.yaml --diag-addr=127.0.0.1:3000 --pid-file=/run/teleport/teleport.pid ExecReload=/bin/kill -HUP $MAINPID PIDFile=/run/teleport/teleport.pid -LimitNOFILE=524288 \ No newline at end of file +LimitNOFILE=524288 + +[Install] +WantedBy=multi-user.target From 63ebde94ac2945bc0793fa8caff8c06d89d4b972 Mon Sep 17 00:00:00 2001 From: Sakshyam Shah Date: Wed, 19 Jun 2024 19:29:24 -0400 Subject: [PATCH 12/20] set maxHeight in user editor dialog (#43228) --- web/packages/teleport/src/Users/UserAddEdit/UserAddEdit.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/packages/teleport/src/Users/UserAddEdit/UserAddEdit.tsx b/web/packages/teleport/src/Users/UserAddEdit/UserAddEdit.tsx index 6492df43dcb79..22af6d2f9d918 100644 --- a/web/packages/teleport/src/Users/UserAddEdit/UserAddEdit.tsx +++ b/web/packages/teleport/src/Users/UserAddEdit/UserAddEdit.tsx @@ -73,7 +73,8 @@ export function UserAddEdit(props: ReturnType) { dialogCss={() => ({ maxWidth: '700px', width: '100%', - height: '70%', + height: '100%', + maxHeight: '600px', })} disableEscapeKeyDown={false} onClose={onClose} @@ -82,7 +83,7 @@ export function UserAddEdit(props: ReturnType) { {isNew ? 'Create User' : 'Edit User'} - + {attempt.status === 'failed' && ( )} From 3f7faee1e4e6ec06944db8df0323e373369003b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Cie=C5=9Blak?= Date: Thu, 20 Jun 2024 10:52:48 +0200 Subject: [PATCH 13/20] Move `@types/google-protobuf` from teleterm to build (#43190) --- web/packages/build/package.json | 1 + web/packages/teleterm/package.json | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/web/packages/build/package.json b/web/packages/build/package.json index a4b69f09a6384..a580030d03ebb 100644 --- a/web/packages/build/package.json +++ b/web/packages/build/package.json @@ -47,6 +47,7 @@ "@testing-library/react": "^12.1.2", "@testing-library/react-hooks": "^8.0.1", "@testing-library/user-event": "^14.4.3", + "@types/google-protobuf": "^3.10.0", "@types/jest": "^29.5.6", "@types/jsdom": "^21.1.0", "@types/node": "^20.14.2", diff --git a/web/packages/teleterm/package.json b/web/packages/teleterm/package.json index 1440081c913a7..9051fa3bc7ee5 100644 --- a/web/packages/teleterm/package.json +++ b/web/packages/teleterm/package.json @@ -38,7 +38,6 @@ "@gravitational/design": "1.0.0", "@gravitational/shared": "1.0.0", "@grpc/grpc-js": "1.8.22", - "@types/google-protobuf": "^3.10.0", "@types/node-forge": "^1.0.4", "@types/whatwg-url": "^11.0.1", "clean-webpack-plugin": "4.0.0", From 7caed924e5cc2f8229569b0eb22e8ea0c9df2f9e Mon Sep 17 00:00:00 2001 From: rosstimothy <39066650+rosstimothy@users.noreply.github.com> Date: Thu, 20 Jun 2024 11:04:54 -0400 Subject: [PATCH 14/20] Remove lib/devicetrust from lib/client (#43186) (#43279) The native device trust libraries included in lib/devicetrust prevent building client tools that don't use device trust from building with cgo disabled. This moves the default device auth ceremony and device enrollment from lib/client into a location specific to tsh since it's the only tool that requires them. Updates https://github.com/gravitational/teleport/issues/43112. --- lib/client/api.go | 26 ++++++++++++-------------- lib/client/export_test.go | 4 ++-- lib/teleterm/clusters/storage.go | 23 +++++++++++++++-------- tool/tsh/common/tsh.go | 14 ++++++++++++-- 4 files changed, 41 insertions(+), 26 deletions(-) diff --git a/lib/client/api.go b/lib/client/api.go index 1f0e998fc2475..102a37a748bb0 100644 --- a/lib/client/api.go +++ b/lib/client/api.go @@ -73,8 +73,6 @@ import ( "github.com/gravitational/teleport/lib/client/terminal" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/devicetrust" - dtauthn "github.com/gravitational/teleport/lib/devicetrust/authn" - dtenroll "github.com/gravitational/teleport/lib/devicetrust/enroll" "github.com/gravitational/teleport/lib/events" kubeutils "github.com/gravitational/teleport/lib/kube/utils" "github.com/gravitational/teleport/lib/modules" @@ -452,9 +450,9 @@ type Config struct { // PROXYSigner is used to sign PROXY headers for securely propagating client IP address PROXYSigner multiplexer.PROXYHeaderSigner - // DTAuthnRunCeremony allows tests to override the default device - // authentication function. - // Defaults to "dtauthn.NewCeremony().Run()". + // DTAuthnRunCeremony is the device authentication function to execute + // during device login ceremonies. If not provided and device trust is + // required, then the device login will fail. DTAuthnRunCeremony DTAuthnRunCeremonyFunc // dtAttemptLoginIgnorePing and dtAutoEnrollIgnorePing allow Device Trust @@ -462,10 +460,10 @@ type Config struct { // Useful to force flows that only typically happen on Teleport Enterprise. dtAttemptLoginIgnorePing, dtAutoEnrollIgnorePing bool - // dtAutoEnroll allows tests to override the default device auto-enroll - // function. - // Defaults to [dtenroll.AutoEnroll]. - dtAutoEnroll dtAutoEnrollFunc + // DTAutoEnroll is the device auto-enroll function to execute during + // device enrollment. If not provided and device trust auto-enrollment + // is enabled, then the enrollment process will fail. + DTAutoEnroll DTAutoEnrollFunc // PromptMFAFunc allows tests to override the default MFA prompt function. // Defaults to [mfa.NewPrompt().Run]. @@ -1042,8 +1040,8 @@ func (c *Config) ResourceFilter(kind string) *proto.ListResourcesRequest { // DTAuthnRunCeremonyFunc matches the signature of [dtauthn.Ceremony.Run]. type DTAuthnRunCeremonyFunc func(context.Context, devicepb.DeviceTrustServiceClient, *devicepb.UserCertificates) (*devicepb.UserCertificates, error) -// dtAutoEnrollFunc matches the signature of [dtenroll.AutoEnroll]. -type dtAutoEnrollFunc func(context.Context, devicepb.DeviceTrustServiceClient) (*devicepb.Device, error) +// DTAutoEnrollFunc matches the signature of [dtenroll.AutoEnroll]. +type DTAutoEnrollFunc func(context.Context, devicepb.DeviceTrustServiceClient) (*devicepb.Device, error) // TeleportClient is a wrapper around SSH client with teleport specific // workflow built in. @@ -3575,7 +3573,7 @@ func (tc *TeleportClient) DeviceLogin(ctx context.Context, rootAuthClient authcl // Allow tests to override the default authn function. runCeremony := tc.DTAuthnRunCeremony if runCeremony == nil { - runCeremony = dtauthn.NewCeremony().Run + return nil, trace.BadParameter("device authentication not enabled") } // Login without a previous auto-enroll attempt. @@ -3596,9 +3594,9 @@ func (tc *TeleportClient) DeviceLogin(ctx context.Context, rootAuthClient authcl return nil, trace.Wrap(loginErr) // err swallowed for loginErr } - autoEnroll := tc.dtAutoEnroll + autoEnroll := tc.DTAutoEnroll if autoEnroll == nil { - autoEnroll = dtenroll.AutoEnroll + return nil, trace.BadParameter("device auto enrollment not enabled") } // Auto-enroll and Login again. diff --git a/lib/client/export_test.go b/lib/client/export_test.go index 07bd1ab5131f1..3112fc326bc7c 100644 --- a/lib/client/export_test.go +++ b/lib/client/export_test.go @@ -28,6 +28,6 @@ func (tc *TeleportClient) SetDTAuthnRunCeremony(fn DTAuthnRunCeremonyFunc) { tc.DTAuthnRunCeremony = fn } -func (tc *TeleportClient) SetDTAutoEnroll(fn dtAutoEnrollFunc) { - tc.dtAutoEnroll = fn +func (tc *TeleportClient) SetDTAutoEnroll(fn DTAutoEnrollFunc) { + tc.DTAutoEnroll = fn } diff --git a/lib/teleterm/clusters/storage.go b/lib/teleterm/clusters/storage.go index 3b37a32e61205..a7a6304eb09b7 100644 --- a/lib/teleterm/clusters/storage.go +++ b/lib/teleterm/clusters/storage.go @@ -25,6 +25,8 @@ import ( "github.com/gravitational/teleport/api/profile" "github.com/gravitational/teleport/lib/client" + dtauthn "github.com/gravitational/teleport/lib/devicetrust/authn" + dtenroll "github.com/gravitational/teleport/lib/devicetrust/enroll" "github.com/gravitational/teleport/lib/teleterm/api/uri" ) @@ -151,11 +153,8 @@ func (s *Storage) addCluster(ctx context.Context, dir, webProxyAddress string) ( return nil, nil, trace.BadParameter("cluster directory is missing") } - cfg := client.MakeDefaultConfig() + cfg := s.makeDefaultClientConfig() cfg.WebProxyAddr = webProxyAddress - cfg.HomePath = s.Dir - cfg.KeysDir = s.Dir - cfg.InsecureSkipVerify = s.InsecureSkipVerify profileName := parseName(webProxyAddress) clusterURI := uri.NewClusterURI(profileName) @@ -208,13 +207,10 @@ func (s *Storage) fromProfile(profileName, leafClusterName string) (*Cluster, *c profileStore := client.NewFSProfileStore(s.Dir) - cfg := client.MakeDefaultConfig() + cfg := s.makeDefaultClientConfig() if err := cfg.LoadProfile(profileStore, profileName); err != nil { return nil, nil, trace.Wrap(err) } - cfg.KeysDir = s.Dir - cfg.HomePath = s.Dir - cfg.InsecureSkipVerify = s.InsecureSkipVerify if leafClusterName != "" { clusterNameForKey = leafClusterName @@ -271,6 +267,17 @@ func (s *Storage) loadProfileStatusAndClusterKey(clusterClient *client.TeleportC return status, nil } +func (s *Storage) makeDefaultClientConfig() *client.Config { + cfg := client.MakeDefaultConfig() + + cfg.HomePath = s.Dir + cfg.KeysDir = s.Dir + cfg.InsecureSkipVerify = s.InsecureSkipVerify + cfg.DTAuthnRunCeremony = dtauthn.NewCeremony().Run + cfg.DTAutoEnroll = dtenroll.AutoEnroll + return cfg +} + // parseName gets cluster name from cluster web proxy address func parseName(webProxyAddress string) string { clusterName, _, err := net.SplitHostPort(webProxyAddress) diff --git a/tool/tsh/common/tsh.go b/tool/tsh/common/tsh.go index f1992ef57d51a..8ef88df341847 100644 --- a/tool/tsh/common/tsh.go +++ b/tool/tsh/common/tsh.go @@ -74,6 +74,8 @@ import ( dbprofile "github.com/gravitational/teleport/lib/client/db" "github.com/gravitational/teleport/lib/client/identityfile" "github.com/gravitational/teleport/lib/defaults" + dtauthn "github.com/gravitational/teleport/lib/devicetrust/authn" + dtenroll "github.com/gravitational/teleport/lib/devicetrust/enroll" "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/kube/kubeconfig" "github.com/gravitational/teleport/lib/modules" @@ -495,6 +497,11 @@ type CLIConf struct { // Defaults to [dtauthn.NewCeremony().Run]. DTAuthnRunCeremony client.DTAuthnRunCeremonyFunc + // DTAutoEnroll allows tests to override the default device + // auto-enroll function. + // Defaults to [dtenroll.AutoEnroll]. + DTAutoEnroll client.DTAutoEnrollFunc + // WebauthnLogin allows tests to override the Webauthn Login func. // Defaults to [wancli.Login]. WebauthnLogin client.WebauthnLoginFunc @@ -644,8 +651,10 @@ func initLogger(cf *CLIConf) { // DO NOT RUN TESTS that call Run() in parallel (unless you taken precautions). func Run(ctx context.Context, args []string, opts ...CliOption) error { cf := CLIConf{ - Context: ctx, - TracingProvider: tracing.NoopProvider(), + Context: ctx, + TracingProvider: tracing.NoopProvider(), + DTAuthnRunCeremony: dtauthn.NewCeremony().Run, + DTAutoEnroll: dtenroll.AutoEnroll, } // run early to enable debug logging if env var is set. @@ -3977,6 +3986,7 @@ func loadClientConfigFromCLIConf(cf *CLIConf, proxy string) (*client.Config, err c.MockSSOLogin = cf.MockSSOLogin c.MockHeadlessLogin = cf.MockHeadlessLogin c.DTAuthnRunCeremony = cf.DTAuthnRunCeremony + c.DTAutoEnroll = cf.DTAutoEnroll c.WebauthnLogin = cf.WebauthnLogin // pass along MySQL/Postgres path overrides (only used in tests). From bb39c9d49d8ea59b8fecfdd673bae142c171937b Mon Sep 17 00:00:00 2001 From: Noah Stride Date: Thu, 20 Jun 2024 18:04:41 +0100 Subject: [PATCH 15/20] [v14] Fix gRPC connections being disconnected regardless of DisconnectCertExpiry (#43292) * Fix gRPC connections being disconnected regardless of DisconnectCertExpiry * Remove log lines * Add test * back out the changes that added Authorizer --- lib/auth/transport_credentials.go | 7 +++++-- lib/authz/permissions.go | 7 ++++++- lib/authz/permissions_test.go | 22 +++++++++++++++++++++- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/lib/auth/transport_credentials.go b/lib/auth/transport_credentials.go index 4c357130dab77..c1bc0dc9d929d 100644 --- a/lib/auth/transport_credentials.go +++ b/lib/auth/transport_credentials.go @@ -179,8 +179,11 @@ func newTimeoutConn(conn net.Conn, clock clockwork.Clock, expires time.Time) (ne } return &timeoutConn{ - Conn: conn, - timer: clock.AfterFunc(expires.Sub(clock.Now()), func() { conn.Close() }), + Conn: conn, + timer: clock.AfterFunc(expires.Sub(clock.Now()), func() { + log.Debug("Closing gRPC connection due to certificate expiry") + conn.Close() + }), }, nil } diff --git a/lib/authz/permissions.go b/lib/authz/permissions.go index 6368f68b651ee..ee620b0a242c4 100644 --- a/lib/authz/permissions.go +++ b/lib/authz/permissions.go @@ -265,7 +265,12 @@ func (c *Context) GetDisconnectCertExpiry(authPref types.AuthPreference) time.Ti // See https://github.com/gravitational/teleport/issues/18544 // If the session doesn't need to be disconnected on cert expiry just return the default value. - if c.Checker != nil && !c.Checker.AdjustDisconnectExpiredCert(authPref.GetDisconnectExpiredCert()) { + disconnectExpiredCert := authPref.GetDisconnectExpiredCert() + if c.Checker != nil { + disconnectExpiredCert = c.Checker.AdjustDisconnectExpiredCert(disconnectExpiredCert) + } + + if !disconnectExpiredCert { return time.Time{} } diff --git a/lib/authz/permissions_test.go b/lib/authz/permissions_test.go index 5fc5971382c55..416195ae5ed9d 100644 --- a/lib/authz/permissions_test.go +++ b/lib/authz/permissions_test.go @@ -51,12 +51,14 @@ func TestGetDisconnectExpiredCertFromIdentity(t *testing.T) { name string expires time.Time previousIdentityExpires time.Time + checker services.AccessChecker mfaVerified bool disconnectExpiredCert bool expected time.Time }{ { name: "mfa overrides expires when set", + checker: &fakeCtxChecker{}, expires: now, previousIdentityExpires: inAnHour, mfaVerified: true, @@ -65,6 +67,7 @@ func TestGetDisconnectExpiredCertFromIdentity(t *testing.T) { }, { name: "expires returned when mfa unset", + checker: &fakeCtxChecker{}, expires: now, mfaVerified: false, disconnectExpiredCert: true, @@ -72,11 +75,28 @@ func TestGetDisconnectExpiredCertFromIdentity(t *testing.T) { }, { name: "unset when disconnectExpiredCert is false", + checker: &fakeCtxChecker{}, expires: now, previousIdentityExpires: inAnHour, mfaVerified: true, disconnectExpiredCert: false, }, + { + name: "no expiry returned when checker nil and disconnectExpiredCert false", + checker: nil, + expires: now, + mfaVerified: false, + disconnectExpiredCert: false, + expected: time.Time{}, + }, + { + name: "expiry returned when checker nil and disconnectExpiredCert true", + checker: nil, + expires: now, + mfaVerified: false, + disconnectExpiredCert: true, + expected: now, + }, } { t.Run(test.name, func(t *testing.T) { var mfaVerified string @@ -92,7 +112,7 @@ func TestGetDisconnectExpiredCertFromIdentity(t *testing.T) { authPref := types.DefaultAuthPreference() authPref.SetDisconnectExpiredCert(test.disconnectExpiredCert) - ctx := Context{Checker: &fakeCtxChecker{}, Identity: WrapIdentity(identity)} + ctx := Context{Checker: test.checker, Identity: WrapIdentity(identity)} got := ctx.GetDisconnectCertExpiry(authPref) require.Equal(t, test.expected, got) From 8dc8e861ea7b6ee9aae8711c80a941617a4b50af Mon Sep 17 00:00:00 2001 From: Hugo Shaka Date: Thu, 20 Jun 2024 14:51:08 -0400 Subject: [PATCH 16/20] [v14] Mark Discovery service healthy on startup (#43285) * Mark Discovery service healthy on startup * bump e --- e | 2 +- lib/service/discovery.go | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/e b/e index a3a88c2c15b3f..c937c071297b0 160000 --- a/e +++ b/e @@ -1 +1 @@ -Subproject commit a3a88c2c15b3fa6188e85852c784e8587addf68c +Subproject commit c937c071297b02c58fab86bca8ab4938eed80814 diff --git a/lib/service/discovery.go b/lib/service/discovery.go index 28bd85efc153d..68741f269fc7d 100644 --- a/lib/service/discovery.go +++ b/lib/service/discovery.go @@ -127,6 +127,11 @@ func (process *TeleportProcess) initDiscoveryService() error { } log.Infof("Discovery service has successfully started") + // The Discovery service doesn't have heartbeats so we cannot use them to check health. + // For now, we just mark ourselves ready all the time on startup. + // If we don't, a process only running the Discovery service will never report ready. + process.OnHeartbeat(teleport.ComponentDiscovery)(nil) + if err := discoveryService.Wait(); err != nil { return trace.Wrap(err) } From a3d94dc6726c92c1148069719d12b06995763d58 Mon Sep 17 00:00:00 2001 From: Gus Luxton Date: Thu, 20 Jun 2024 17:30:51 -0300 Subject: [PATCH 17/20] [v14] docs: Add more methods to set Origin/Host headers (#43242) * docs: Add more methods to set Origin/Host headers * Address review feedback * Fix formatting * Fix formatting * Don't lint me --- .../troubleshooting-apps.mdx | 133 ++++++++++++------ 1 file changed, 89 insertions(+), 44 deletions(-) diff --git a/docs/pages/application-access/troubleshooting-apps.mdx b/docs/pages/application-access/troubleshooting-apps.mdx index ad31e380fed9e..a5ad10dcfbed1 100644 --- a/docs/pages/application-access/troubleshooting-apps.mdx +++ b/docs/pages/application-access/troubleshooting-apps.mdx @@ -32,61 +32,106 @@ Issues with Cross-Site Request Forgery (CSRF) or Cross-Origin Resource Sharing ( result in a loss of application functionality, errors in the application itself indicating that traffic isn't being permitted, or application logs that indicate CORS or CSRF errors. -### Solution - In most cases, you can fix these types of issues by adding explicit `rewrite` settings for the Origin and Host headers -in the Teleport configuration file for each application. +in the Teleport configuration for each application. -To fix CSRF or CORS issues: +### Solution 1: Application Service configuration file + +To fix CSRF or CORS issues if you use statically configured apps in `/etc/teleport.yaml`: 1. Open the `/etc/teleport.yaml` file that contains the application configuration in a text editor. -1. Add a `rewrite.headers` section similar to the following `grafana` example: +{/*lint ignore ordered-list-marker-value*/} +2. Add a `rewrite.headers` section similar to the following `grafana` example: + + ```yaml + app_service: + enabled: true + apps: + - name: grafana + uri: http://localhost:3000 + public_addr: grafana.teleport.example.com + rewrite: + headers: + - "Origin: https://grafana.teleport.example.com" # Teleport application subdomain prepended with "https://" + - "Host: grafana.teleport.example.com" # Teleport application subdomain itself + ``` - ```yaml - app_service: - enabled: true - apps: - - name: grafana - uri: http://localhost:3000 - public_addr: grafana.teleport.example.com - rewrite: - headers: - - "Origin: https://grafana.teleport.example.com" # Teleport application subdomain prepended with "https://" - - "Host: grafana.teleport.example.com" # Teleport application subdomain itself - ``` +3. Save your changes and restart the Teleport service. -1. Save your changes and restart the Teleport service. +### Solution 2: `teleport-kube-agent` values file To fix CSRF or CORS issues if you deploy applications using Kubernetes and `teleport-kube-agent`: 1. Open the `teleport/examples/chart/teleport-kube-agent/values.yaml` file that contains the application - configuration in a text editor. - -1. Locate the `apps` section in the `values.yaml` file. - - ```yaml - # Details of at least one app to be proxied. Example: - # apps: - # - name: grafana - # uri: http://localhost:3000 - apps: [] - ``` - -1. Add a `rewrite.headers` section similar to the following `grafana` example: - - ```yaml - app_service: - enabled: true - apps: - - name: grafana - uri: http://localhost:3000 - public_addr: grafana.teleport.example.com - rewrite: - headers: - - "Origin: https://grafana.teleport.example.com" # Teleport application subdomain prepended with "https://" - - "Host: grafana.teleport.example.com" # Teleport application subdomain itself - ``` + configuration in a text editor. + +{/*lint ignore ordered-list-marker-value*/} +2. Locate the `apps` section in the `values.yaml` file. + + ```yaml + # Details of at least one app to be proxied. Example: + # apps: + # - name: grafana + # uri: http://localhost:3000 + apps: [] + ``` + +3. Add a `rewrite.headers` section similar to the following `grafana` example: + + ```yaml + apps: + - name: grafana + uri: http://localhost:3000 + public_addr: grafana.teleport.example.com + rewrite: + headers: + - "Origin: https://grafana.teleport.example.com" # Teleport application subdomain prepended with "https://" + - "Host: grafana.teleport.example.com" # Teleport application subdomain itself + ``` + +### Solution 3: Dynamic app configuration + +To fix CSRF or CORS issues if you deploy applications with dynamic configuration: + +1. Edit your dynamic app configuration to include the `rewrite.headers` section: + + ```yaml + kind: app + version: v3 + metadata: + name: grafana + labels: + env: dev + spec: + uri: http://localhost:3000 + public_addr: grafana.teleport.example.com + rewrite: + headers: + - name: "Origin" + value: "https://grafana.teleport.example.com" # Teleport application subdomain prepended with "https://" + - name: "Host" + value: "grafana.teleport.example.com" # Teleport application subdomain itself + ``` + +### Solution 4: Kubernetes app autodiscovery + +To fix CSRF or CORS issues if you deploy applications using Kubernetes autodiscovery: + +1. Edit your Kubernetes `Service` configuration to include the `rewrite.headers` section: + + ```yaml + apiVersion: v1 + kind: Service + metadata: + annotations: + teleport.dev/app-rewrite: | + headers: + - name: "Origin" + value: "https://grafana.teleport.example.com" # Teleport application subdomain prepended with "https://" + - name: "Host" + value: "grafana.teleport.example.com" # Teleport application subdomain itself + ``` ## Untrusted certificate errors From 545fe4815b1a5501b72977e9c0104eeea1be0858 Mon Sep 17 00:00:00 2001 From: Paul Gottschling Date: Thu, 20 Jun 2024 17:26:16 -0400 Subject: [PATCH 18/20] [v14] Fix docs markup errors (#43316) Backports #43223 Co-authored-by: Alexey Ivanov --- docs/config.json | 17 +- docs/img/IBM/IBM_HA.svg | 213 +++++++++++++++++- .../access-request-plugins/servicenow.mdx | 2 +- .../access-controls/login-rules/guide.mdx | 2 +- .../management/admin/trustedclusters.mdx | 2 +- .../management/operations/db-ca-rotation.mdx | 4 +- docs/pages/reference/terraform-provider.mdx | 9 - 7 files changed, 216 insertions(+), 33 deletions(-) diff --git a/docs/config.json b/docs/config.json index 19e9a5e8ec6a1..daa585b7dac57 100644 --- a/docs/config.json +++ b/docs/config.json @@ -3220,16 +3220,6 @@ "destination": "/access-controls/device-trust/device-management/", "permanent": true }, - { - "source": "/management/guides/teleport-operator/", - "destination": "/management/dynamic-resources/teleport-operator/", - "permanent": true - }, - { - "source": "/management/guides/terraform-provider/", - "destination": "/management/dynamic-resources/terraform-provider/", - "permanent": true - }, { "source": "/get-started/", "destination": "/", @@ -3287,7 +3277,7 @@ }, { "source": "/machine-id/guides/", - "destination": "/machine-id/", + "destination": "/machine-id/introduction/", "permanent": true }, { @@ -3325,11 +3315,6 @@ "destination": "/machine-id/deployment/gitlab/", "permanent": true }, - { - "source": "/machine-id/guides/gitlab/", - "destination": "/machine-id/deployment/github-actions/", - "permanent": true - }, { "source": "/server-access/guides/openssh/", "destination": "/server-access/guides/openssh/openssh/", diff --git a/docs/img/IBM/IBM_HA.svg b/docs/img/IBM/IBM_HA.svg index 2dde67c51c185..246d230c0b1d7 100644 --- a/docs/img/IBM/IBM_HA.svg +++ b/docs/img/IBM/IBM_HA.svg @@ -1,3 +1,210 @@ - - -
IBM Cloud
IBM Cloud
Teleport Proxy Autoscale Group
Teleport Proxy Aut...
IBM Cloud Virtual Server
IBM Cloud Virtual...
Teleport Auth
Autoscale Group
Teleport Auth...
IBM Cloud Virtual Server
IBM Cloud Virtual...
VPC
VPC
Instances
Instances
One Teleport cluster can support 10,000 nodes. See Teleport Node setup for more info. 
One Teleport cluster c...
IBM Object Store
IBM Object Store
IBM Database for etcd
IBM Database for etcd
Client
Client
End user using Teleport
Client Tool tsh
End user using T...
SSO Provider
SSO Provider
\ No newline at end of file + + + + + +
+
+
+ IBM Cloud +
+
+
+
+ IBM Cloud +
+ + + + +
+
+
+ + Teleport Proxy Autoscale Group + +
+
+
+
+ Teleport Proxy Aut... +
+ + + + +
+
+
+ IBM Cloud Virtual Server +
+
+
+
+ IBM Cloud Virtual... +
+ + + + + + + +
+
+
+ + Teleport Auth +
+ Autoscale Group +
+
+
+
+
+ Teleport Auth... +
+ + + + +
+
+
+ IBM Cloud Virtual Server +
+
+
+
+ IBM Cloud Virtual... +
+ + + +
+
+
+ VPC +
+
+
+
+ VPC +
+ + + + + +
+
+
+ Instances +
+
+
+
+ Instances +
+ + +
+
+
+ + + One Teleport cluster can support + + + 10,000 nodes. See Teleport Node + + + setup for more info. + + +
+
+
+
+ One Teleport cluster c... +
+ + +
+
+
+ IBM Object Store +
+
+
+
+ IBM Object Store +
+ + + +
+
+
+ IBM Database for etcd +
+
+
+
+ IBM Database for etcd +
+ + + + + + + + + + + + + +
+
+
+ Client +
+
+
+
+ Client +
+ + +
+
+
+ End user using Teleport +
+ Client Tool + + tsh + +
+
+
+
+ End user using T... +
+ + +
+
+
+ SSO Provider +
+
+
+
+ SSO Provider +
+ + +
diff --git a/docs/pages/access-controls/access-request-plugins/servicenow.mdx b/docs/pages/access-controls/access-request-plugins/servicenow.mdx index a47fe600daefe..c4013213c1ab3 100644 --- a/docs/pages/access-controls/access-request-plugins/servicenow.mdx +++ b/docs/pages/access-controls/access-request-plugins/servicenow.mdx @@ -63,7 +63,7 @@ To retrieve the ServiceNow rotation ID, navigate to the group record of the ServiceNow group the rotation belongs to and right click on header, then click 'Select copy sys_id' to copy the ID. -Then using the ServiceNow endpoint '/api/now/on_call_rota/workbench/group/{groupSysId}' +Then using the ServiceNow endpoint `/api/now/on_call_rota/workbench/group/{groupSysId}` retrieve the group's on-call rota information. Select the value of the desired 'rota' from the response. diff --git a/docs/pages/access-controls/login-rules/guide.mdx b/docs/pages/access-controls/login-rules/guide.mdx index 2968d502ebd7b..9fee51b16b766 100644 --- a/docs/pages/access-controls/login-rules/guide.mdx +++ b/docs/pages/access-controls/login-rules/guide.mdx @@ -17,7 +17,7 @@ cluster on version `11.3.1` or greater. Login Rules only operate on SSO logins, so make sure you have configured an OIDC, SAML, or GitHub connector before you begin. -Check the [Single Sign-On](../sso/) docs to learn how to set this up. +Check the [Single Sign-On](../sso.mdx) docs to learn how to set this up. ## Step 1/5. Configure RBAC diff --git a/docs/pages/management/admin/trustedclusters.mdx b/docs/pages/management/admin/trustedclusters.mdx index 6e9cc702592a1..66ffe521efcac 100644 --- a/docs/pages/management/admin/trustedclusters.mdx +++ b/docs/pages/management/admin/trustedclusters.mdx @@ -106,7 +106,7 @@ configured with a single sign-on identity provider that authenticates her identi Based on the information from the identity provider, the root cluster assigns Alice the `full-access` role and issues her a certificate. The mapping of single sign-on properties to Teleport roles is configured when you add an authentication connector to the Teleport cluster. To learn more about configuring single sign-on -through an external identity provider, see [Configure Single Sign-on](../../access-controls/sso). +through an external identity provider, see [Configure Single Sign-on](../../access-controls/sso.mdx). Alice receives the certificate that specifies the roles assigned to her in the root cluster. This metadata about her roles is contained in the certificate extensions and is protected by the signature of the root diff --git a/docs/pages/management/operations/db-ca-rotation.mdx b/docs/pages/management/operations/db-ca-rotation.mdx index 0454968fcd205..5b22f91f571fa 100644 --- a/docs/pages/management/operations/db-ca-rotation.mdx +++ b/docs/pages/management/operations/db-ca-rotation.mdx @@ -128,7 +128,7 @@ You do not need to reconfigure databases at this point if you are rotating only the `db` CA, although there is no harm in doing so. Consult the appropriate -[Teleport Database Access Guide](../../database-access/guides) for your +[Teleport Database Access Guide](../../database-access/guides.mdx) for your databases before proceeding to the `update_clients` rotation phase. @@ -172,7 +172,7 @@ lose access** to those databases after transitioning to the `standby` phase in this final step. To avoid down time, consult the appropriate -[Teleport Database Access Guide](../../database-access/guides) and reconfigure +[Teleport Database Access Guide](../../database-access/guides.mdx) and reconfigure your databases before proceeding. Otherwise, access may still be restored by reconfiguring your self-hosted databases after this step. diff --git a/docs/pages/reference/terraform-provider.mdx b/docs/pages/reference/terraform-provider.mdx index 3ed283811cb06..1e66056712b85 100644 --- a/docs/pages/reference/terraform-provider.mdx +++ b/docs/pages/reference/terraform-provider.mdx @@ -1413,15 +1413,6 @@ GitLab allows the configuration of options specific to the "gitlab" join method. Allow is a list of TokenRules, nodes using this token must match one allow rule to use this token. -| Name | Type | Required | Description | -|-----------------|--------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| environment | string | | Environment limits access by the environment the job deploys to (if one is associated) | -| namespace_path | string | | NamespacePath is used to limit access to jobs in a group or user's projects. Example: `mygroup` | -| pipeline_source | string | | PipelineSource limits access by the job pipeline source type. https://docs.gitlab.com/ee/ci/jobs/job_control.html#common-if-clauses-for-rules Example: `web` | -| project_path | string | | ProjectPath is used to limit access to jobs belonging to an individual project. Example: `mygroup/myproject` | -| ref | string | | Ref allows access to be limited to jobs triggered by a specific git ref. Ensure this is used in combination with ref_type. | -| ref_type | string | | RefType allows access to be limited to jobs triggered by a specific git ref type. Example: `branch` or `tag` | -| sub | string | | Sub roughly uniquely identifies the workload. Example: `project_path:mygroup/my-project:ref_type:branch:ref:main` project_path:{group}/{project}:ref_type:{type}:ref:{branch_name} | #### spec.kubernetes From aa568d45c58c91956b3f9eb6bea6cdd7c5a28a59 Mon Sep 17 00:00:00 2001 From: Forrest <30576607+fspmarshall@users.noreply.github.com> Date: Thu, 20 Jun 2024 14:28:25 -0700 Subject: [PATCH 19/20] add ability to disable unqualified hostname lookups (#42952) (#43320) --- api/utils/route.go | 10 +++++- api/utils/route_test.go | 63 +++++++++++++++++++++++++++++++++++++ lib/auth/auth_with_roles.go | 13 +++++++- lib/proxy/router.go | 11 ++++--- 4 files changed, 91 insertions(+), 6 deletions(-) diff --git a/api/utils/route.go b/api/utils/route.go index 476b53f177511..a34ccc00d5c76 100644 --- a/api/utils/route.go +++ b/api/utils/route.go @@ -18,6 +18,7 @@ import ( "context" "errors" "net" + "strings" "unicode/utf8" "github.com/google/uuid" @@ -49,6 +50,8 @@ type SSHRouteMatcherConfig struct { Resolver HostResolver // CaseInsensitive enabled case insensitive routing when true. CaseInsensitive bool + // DisableUnqualifiedLookups disables lookups for unqualified hostnames. + DisableUnqualifiedLookups bool } // HostResolver provides an interface matching the net.Resolver.LookupHost method. Typically @@ -88,7 +91,12 @@ func newSSHRouteMatcher(cfg SSHRouteMatcherConfig) SSHRouteMatcher { _, err := uuid.Parse(cfg.Host) dialByID := err == nil || aws.IsEC2NodeID(cfg.Host) - ips, _ := cfg.Resolver.LookupHost(context.Background(), cfg.Host) + var ips []string + if !(cfg.DisableUnqualifiedLookups && !strings.Contains(cfg.Host, ".")) { + // unqualified lookups are still on by default, but future versions of teleport may disable them as they tend + // to be responsible for the majority of all lookups generated by a teleport cluster and are of questionable utility. + ips, _ = cfg.Resolver.LookupHost(context.Background(), cfg.Host) + } return SSHRouteMatcher{ cfg: cfg, diff --git a/api/utils/route_test.go b/api/utils/route_test.go index 5de05efa673d8..ce971e04e6ea3 100644 --- a/api/utils/route_test.go +++ b/api/utils/route_test.go @@ -323,3 +323,66 @@ func TestSSHRouteMatcherScoring(t *testing.T) { }) } } + +type recordingHostResolver struct { + didLookup bool +} + +func (r *recordingHostResolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) { + r.didLookup = true + return nil, nil +} + +// TestDisableUnqualifiedLookups verifies that unqualified lookups being disabled results +// in single-element/tld style hostname targets not being resolved. +func TestDisableUnqualifiedLookups(t *testing.T) { + tts := []struct { + desc string + target string + lookup bool + }{ + { + desc: "qualified hostname", + target: "example.com", + lookup: true, + }, + { + desc: "unqualified hostname", + target: "example", + lookup: false, + }, + { + desc: "localhost", + target: "localhost", + lookup: false, + }, + { + desc: "foo.localhost", + target: "foo.localhost", + lookup: true, + }, + { + desc: "uuid", + target: uuid.NewString(), + lookup: false, + }, + { + desc: "qualified uuid", + target: "foo." + uuid.NewString(), + lookup: true, + }, + } + + for _, tt := range tts { + t.Run(tt.desc, func(t *testing.T) { + resolver := &recordingHostResolver{} + _, err := NewSSHRouteMatcherFromConfig(SSHRouteMatcherConfig{ + Host: tt.target, + Resolver: resolver, + DisableUnqualifiedLookups: true, + }) + require.NoError(t, err) + require.Equal(t, tt.lookup, resolver.didLookup) + }) + } +} diff --git a/lib/auth/auth_with_roles.go b/lib/auth/auth_with_roles.go index 572b920e16d33..1749c94bb4402 100644 --- a/lib/auth/auth_with_roles.go +++ b/lib/auth/auth_with_roles.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "net/url" + "os" "slices" "strings" "time" @@ -1763,6 +1764,8 @@ func (a *ServerWithRoles) authContextForSearch(ctx context.Context, req *proto.L return extendedContext, nil } +var disableUnqualifiedLookups = os.Getenv("TELEPORT_UNSTABLE_DISABLE_UNQUALIFIED_LOOKUPS") == "yes" + // GetSSHTargets gets all servers that would match an equivalent ssh dial request. Note that this method // returns all resources directly accessible to the user *and* all resources available via 'SearchAsRoles', // which is what we want when handling things like ambiguous host errors and resource-based access requests, @@ -1775,7 +1778,15 @@ func (a *ServerWithRoles) GetSSHTargets(ctx context.Context, req *proto.GetSSHTa caseInsensitiveRouting = cfg.GetCaseInsensitiveRouting() } - matcher := apiutils.NewSSHRouteMatcher(req.Host, req.Port, caseInsensitiveRouting) + matcher, err := apiutils.NewSSHRouteMatcherFromConfig(apiutils.SSHRouteMatcherConfig{ + Host: req.Host, + Port: req.Port, + CaseInsensitive: caseInsensitiveRouting, + DisableUnqualifiedLookups: disableUnqualifiedLookups, + }) + if err != nil { + return nil, trace.Wrap(err) + } lreq := proto.ListResourcesRequest{ ResourceType: types.KindNode, diff --git a/lib/proxy/router.go b/lib/proxy/router.go index 3e1ba64361149..fc9d503cbbb3e 100644 --- a/lib/proxy/router.go +++ b/lib/proxy/router.go @@ -448,6 +448,8 @@ func getServer(ctx context.Context, host, port string, site site) (types.Server, return getServerWithResolver(ctx, host, port, site, nil /* use default resolver */) } +var disableUnqualifiedLookups = os.Getenv("TELEPORT_UNSTABLE_DISABLE_UNQUALIFIED_LOOKUPS") == "yes" + // getServerWithResolver attempts to locate a node matching the provided host and port in // the provided site. The resolver argument is used in certain tests to mock DNS resolution // and can generally be left nil. @@ -464,10 +466,11 @@ func getServerWithResolver(ctx context.Context, host, port string, site site, re } routeMatcher, err := apiutils.NewSSHRouteMatcherFromConfig(apiutils.SSHRouteMatcherConfig{ - Host: host, - Port: port, - CaseInsensitive: caseInsensitiveRouting, - Resolver: resolver, + Host: host, + Port: port, + CaseInsensitive: caseInsensitiveRouting, + Resolver: resolver, + DisableUnqualifiedLookups: disableUnqualifiedLookups, }) if err != nil { return nil, trace.Wrap(err) From cd08fcc945191c277c89beeaba8bff2aedd314d9 Mon Sep 17 00:00:00 2001 From: Alan Parra Date: Thu, 20 Jun 2024 18:29:40 -0300 Subject: [PATCH 20/20] Use libfido2 on builds if present (#43224) (#43253) * Use libfido2 on builds if present * Use static libfido2 linking on OS compat tests --- .github/workflows/os-compatibility-test.yaml | 4 ++-- Makefile | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/os-compatibility-test.yaml b/.github/workflows/os-compatibility-test.yaml index a74f623b48abf..812877acda1c3 100644 --- a/.github/workflows/os-compatibility-test.yaml +++ b/.github/workflows/os-compatibility-test.yaml @@ -12,7 +12,7 @@ on: - 'docs/**' - 'web/**' - 'rfd/**' - - '**/*.md*' + - '**/*.md*' jobs: build: @@ -38,7 +38,7 @@ jobs: - name: Run make run: | - make binaries + make binaries FIDO2=static - name: Upload binaries uses: actions/upload-artifact@v3 diff --git a/Makefile b/Makefile index 07be034350309..28523c8d7e7a0 100644 --- a/Makefile +++ b/Makefile @@ -141,6 +141,10 @@ export C_ARCH # Eagerly enable if we detect the package, we want to test as much as possible. ifeq ("$(shell pkg-config libfido2 2>/dev/null; echo $$?)", "0") LIBFIDO2_TEST_TAG := libfido2 +ifeq ($(FIDO2),) +$(info libfido2 found, setting FIDO2=dynamic) +FIDO2 ?= dynamic +endif endif # Build tsh against libfido2? @@ -311,6 +315,9 @@ $(BUILDDIR)/teleport: ensure-webassets bpf-bytecode rdpclient $(BUILDDIR)/tsh: KUBECTL_VERSION ?= $(shell go run ./build.assets/kubectl-version/main.go) $(BUILDDIR)/tsh: KUBECTL_SETVERSION ?= -X k8s.io/component-base/version.gitVersion=$(KUBECTL_VERSION) $(BUILDDIR)/tsh: + @if [[ -z "$(LIBFIDO2_BUILD_TAG)" ]]; then \ + echo 'Warning: Building tsh without libfido2. Install libfido2 to have access to MFA.' >&2; \ + fi GOOS=$(OS) GOARCH=$(ARCH) $(CGOFLAG_TSH) go build -tags "$(FIPS_TAG) $(LIBFIDO2_BUILD_TAG) $(TOUCHID_TAG) $(PIV_BUILD_TAG)" -o $(BUILDDIR)/tsh $(BUILDFLAGS) ./tool/tsh .PHONY: $(BUILDDIR)/tbot