diff --git a/api/utils/retryutils/retry.go b/api/utils/retryutils/retry.go index 8bd93853f96..98adb685863 100644 --- a/api/utils/retryutils/retry.go +++ b/api/utils/retryutils/retry.go @@ -215,7 +215,7 @@ func (e *permanentRetryError) Error() string { return e.err.Error() } -// RetryFastFor retries a function repeatedly for a set amount of +// RetryStaticFor retries a function repeatedly for a set amount of // time before returning an error. // // Intended mostly for tests. diff --git a/lib/auth/auth.go b/lib/auth/auth.go index 5a10eb2a9e9..d6685bbd1a0 100644 --- a/lib/auth/auth.go +++ b/lib/auth/auth.go @@ -82,7 +82,6 @@ import ( "github.com/gravitational/teleport/entitlements" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/auth/keystore" - "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/auth/userloginstate" wanlib "github.com/gravitational/teleport/lib/auth/webauthn" wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" @@ -375,8 +374,6 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (*Server, error) { if !modules.GetModules().Features().GetEntitlement(entitlements.HSM).Enabled { return nil, fmt.Errorf("AWS KMS support requires a license with the HSM feature enabled: %w", ErrRequiresEnterprise) } - } else { - native.PrecomputeKeys() } keyStore, err := keystore.NewManager(context.Background(), &cfg.KeyStoreConfig, keystoreOpts) if err != nil { diff --git a/lib/auth/sessions.go b/lib/auth/sessions.go index baed051de71..0db81b775ab 100644 --- a/lib/auth/sessions.go +++ b/lib/auth/sessions.go @@ -21,6 +21,7 @@ package auth import ( "context" "crypto" + "crypto/rsa" "time" "github.com/gravitational/trace" @@ -36,6 +37,7 @@ import ( apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/entitlements" + "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/defaults" dtconfig "github.com/gravitational/teleport/lib/devicetrust/config" @@ -253,6 +255,14 @@ func (a *Server) newWebSession( if err != nil { return nil, nil, trace.Wrap(err) } + if _, isRSA := sshKey.Public().(*rsa.PublicKey); isRSA { + // Ensure the native package is precomputing RSA keys if we ever + // generate one. [native.PrecomputeKeys] is idempotent. + // Doing this lazily easily handles changing signature algorithm + // suites and won't start precomputing keys if they are never needed + // (a major benefit in tests). + native.PrecomputeKeys() + } } sessionTTL := req.SessionTTL diff --git a/lib/config/openssh/openssh.go b/lib/config/openssh/openssh.go index 6c8e51224ef..29132cf3377 100644 --- a/lib/config/openssh/openssh.go +++ b/lib/config/openssh/openssh.go @@ -19,20 +19,14 @@ package openssh import ( - "bytes" - "os/exec" - "regexp" - "strconv" + "io" "strings" "text/template" - "github.com/coreos/go-semver/semver" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" "github.com/gravitational/teleport" "github.com/gravitational/teleport/lib/defaults" - "github.com/gravitational/teleport/lib/utils" ) // proxyCommandQuote prepares a string for insertion into the ssh_config @@ -57,7 +51,6 @@ Host *.{{ $clusterName }} {{ $dot.ProxyHost }} UserKnownHostsFile "{{ $dot.KnownHostsPath }}" IdentityFile "{{ $dot.IdentityFilePath }}" CertificateFile "{{ $dot.CertificateFilePath }}" - HostKeyAlgorithms {{ if $dot.NewerHostKeyAlgorithmsSupported }}rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,{{ end }}ssh-rsa-cert-v01@openssh.com {{- if ne $dot.Username "" }} User "{{ $dot.Username }}" {{- end }} @@ -110,16 +103,8 @@ type SSHConfigParameters struct { type sshTmplParams struct { SSHConfigParameters - sshConfigOptions } -// openSSHVersionRegex is a regex used to parse OpenSSH version strings. -var openSSHVersionRegex = regexp.MustCompile(`^OpenSSH_(?P\d+)\.(?P\d+)(?:p(?P\d+))?`) - -// openSSHMinVersionForHostAlgos is the first version that understands all host keys required by us. -// HostKeyAlgorithms will be added to ssh config if the version is above listed here. -var openSSHMinVersionForHostAlgos = semver.New("7.8.0") - // SSHConfigApps represent apps that support ssh config generation. type SSHConfigApps string @@ -128,130 +113,14 @@ const ( TbotApp SSHConfigApps = teleport.ComponentTBot ) -// parseSSHVersion attempts to parse the local SSH version, used to determine -// certain config template parameters for client version compatibility. -func parseSSHVersion(versionString string) (*semver.Version, error) { - versionTokens := strings.Split(versionString, " ") - if len(versionTokens) == 0 { - return nil, trace.BadParameter("invalid version string: %s", versionString) - } - - versionID := versionTokens[0] - matches := openSSHVersionRegex.FindStringSubmatch(versionID) - if matches == nil { - return nil, trace.BadParameter("cannot parse version string: %q", versionID) - } - - major, err := strconv.Atoi(matches[1]) - if err != nil { - return nil, trace.Wrap(err, "invalid major version number: %s", matches[1]) - } - - minor, err := strconv.Atoi(matches[2]) - if err != nil { - return nil, trace.Wrap(err, "invalid minor version number: %s", matches[2]) - } - - patch := 0 - if matches[3] != "" { - patch, err = strconv.Atoi(matches[3]) - if err != nil { - return nil, trace.Wrap(err, "invalid patch version number: %s", matches[3]) - } - } - - return &semver.Version{ - Major: int64(major), - Minor: int64(minor), - Patch: int64(patch), - }, nil -} - -// GetSystemSSHVersion attempts to query the system SSH for its current version. -func GetSystemSSHVersion() (*semver.Version, error) { - var out bytes.Buffer - - cmd := exec.Command("ssh", "-V") - cmd.Stderr = &out - - err := cmd.Run() - if err != nil { - return nil, trace.Wrap(err) - } - - return parseSSHVersion(out.String()) -} - -type sshConfigOptions struct { - // NewerHostKeyAlgorithmsSupported when true sets HostKeyAlgorithms OpenSSH configuration option - // to SHA256/512 compatible algorithms. Otherwise, SHA-1 is being used. - NewerHostKeyAlgorithmsSupported bool -} - -func (c *sshConfigOptions) String() string { - sb := &strings.Builder{} - sb.WriteString("sshConfigOptions: ") - - if c.NewerHostKeyAlgorithmsSupported { - sb.WriteString("HostKeyAlgorithms will include SHA-256, SHA-512 and SHA-1") - } else { - sb.WriteString("HostKeyAlgorithms will include SHA-1") - } - - return sb.String() -} - -func isNewerHostKeyAlgorithmsSupported(ver *semver.Version) bool { - return !ver.LessThan(*openSSHMinVersionForHostAlgos) -} - -func getSSHConfigOptions(sshVer *semver.Version) *sshConfigOptions { - return &sshConfigOptions{ - NewerHostKeyAlgorithmsSupported: isNewerHostKeyAlgorithmsSupported(sshVer), - } -} - -func getDefaultSSHConfigOptions() *sshConfigOptions { - return &sshConfigOptions{ - NewerHostKeyAlgorithmsSupported: true, - } -} - -type SSHConfig struct { - getSSHVersion func() (*semver.Version, error) - log logrus.FieldLogger -} - -// NewSSHConfig creates a SSHConfig initialized with provided values or defaults otherwise. -func NewSSHConfig(getSSHVersion func() (*semver.Version, error), log logrus.FieldLogger) *SSHConfig { - if getSSHVersion == nil { - getSSHVersion = GetSystemSSHVersion - } - if log == nil { - log = utils.NewLogger() - } - return &SSHConfig{getSSHVersion: getSSHVersion, log: log} -} - -func (c *SSHConfig) GetSSHConfig(sb *strings.Builder, config *SSHConfigParameters) error { - var sshOptions *sshConfigOptions - version, err := c.getSSHVersion() - if err != nil { - c.log.WithError(err).Debugf("Could not determine SSH version, using default SSH config") - sshOptions = getDefaultSSHConfigOptions() - } else { - c.log.Debugf("Found OpenSSH version %s", version) - sshOptions = getSSHConfigOptions(version) - } +// WriteSSHConfig generates an ssh_config file for OpenSSH clients. +func WriteSSHConfig(w io.Writer, config *SSHConfigParameters) error { if config.Port == 0 { config.Port = defaults.SSHServerListenPort } - c.log.Debugf("Using SSH options: %s", sshOptions) - - if err := sshConfigTemplate.Execute(sb, sshTmplParams{ + if err := sshConfigTemplate.Execute(w, sshTmplParams{ SSHConfigParameters: *config, - sshConfigOptions: *sshOptions, }); err != nil { return trace.Wrap(err) } @@ -268,9 +137,8 @@ var muxedSSHConfigTemplate = template.Must(template.New("muxed-ssh-config").Func Host *.{{ $clusterName }} Port {{ $dot.Port }} UserKnownHostsFile {{ proxyCommandQuote $dot.KnownHostsPath }} - HostKeyAlgorithms {{ if $dot.NewerHostKeyAlgorithmsSupported }}rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,{{ end }}ssh-rsa-cert-v01@openssh.com IdentityFile none - IdentityAgent {{ proxyCommandQuote $dot.AgentSocketPath }} + IdentityAgent {{ proxyCommandQuote $dot.AgentSocketPath }} ProxyCommand {{range $v := $dot.ProxyCommand}}{{ proxyCommandQuote $v }} {{end}}{{ proxyCommandQuote $dot.MuxSocketPath }} '%h:%p|{{ $clusterName }}' ProxyUseFDPass yes {{- end }} @@ -292,29 +160,16 @@ type MuxedSSHConfigParameters struct { type muxedSSHTmplParams struct { MuxedSSHConfigParameters - sshConfigOptions } -// GetMuxedSSHConfig generates a ssh_config file for the ssh-multiplexer service. -func (c *SSHConfig) GetMuxedSSHConfig(sb *strings.Builder, config *MuxedSSHConfigParameters) error { - var sshOptions *sshConfigOptions - version, err := c.getSSHVersion() - if err != nil { - c.log.WithError(err).Debugf("Could not determine SSH version, using default SSH config") - sshOptions = getDefaultSSHConfigOptions() - } else { - c.log.Debugf("Found OpenSSH version %s", version) - sshOptions = getSSHConfigOptions(version) - } +// WriteMuxedSSHConfig generates a ssh_config file for the ssh-multiplexer service. +func WriteMuxedSSHConfig(w io.Writer, config *MuxedSSHConfigParameters) error { if config.Port == 0 { config.Port = defaults.SSHServerListenPort } - c.log.Debugf("Using SSH options: %s", sshOptions) - - if err := muxedSSHConfigTemplate.Execute(sb, muxedSSHTmplParams{ + if err := muxedSSHConfigTemplate.Execute(w, muxedSSHTmplParams{ MuxedSSHConfigParameters: *config, - sshConfigOptions: *sshOptions, }); err != nil { return trace.Wrap(err) } diff --git a/lib/config/openssh/openssh_test.go b/lib/config/openssh/openssh_test.go index 3b9f06447cd..01f888b5e3b 100644 --- a/lib/config/openssh/openssh_test.go +++ b/lib/config/openssh/openssh_test.go @@ -22,58 +22,12 @@ import ( "strings" "testing" - "github.com/coreos/go-semver/semver" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" "github.com/gravitational/teleport/lib/utils/golden" ) -func TestParseSSHVersion(t *testing.T) { - tests := []struct { - str string - version *semver.Version - err bool - }{ - { - str: "OpenSSH_8.2p1 Ubuntu-4ubuntu0.4, OpenSSL 1.1.1f 31 Mar 2020", - version: semver.New("8.2.1"), - }, - { - str: "OpenSSH_8.8p1, OpenSSL 1.1.1m 14 Dec 2021", - version: semver.New("8.8.1"), - }, - { - str: "OpenSSH_7.5p1, OpenSSL 1.0.2s-freebsd 28 May 2019", - version: semver.New("7.5.1"), - }, - { - str: "OpenSSH_7.9p1 Raspbian-10+deb10u2, OpenSSL 1.1.1d 10 Sep 2019", - version: semver.New("7.9.1"), - }, - { - // Couldn't find a full example but in theory patch is optional: - str: "OpenSSH_8.1 foo", - version: semver.New("8.1.0"), - }, - { - str: "Teleport v8.0.0-dev.40 git:v8.0.0-dev.40-0-ge9194c256 go1.17.2", - err: true, - }, - } - - for _, test := range tests { - version, err := parseSSHVersion(test.str) - if test.err { - require.Error(t, err) - } else { - require.NoError(t, err) - require.True(t, version.Equal(*test.version), "got version = %v, want = %v", version, test.version) - } - } -} - -func TestSSHConfig_GetSSHConfig(t *testing.T) { +func TestWriteSSHConfig(t *testing.T) { tests := []struct { name string sshVersion string @@ -157,15 +111,8 @@ func TestSSHConfig_GetSSHConfig(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - c := &SSHConfig{ - getSSHVersion: func() (*semver.Version, error) { - return semver.New(tt.sshVersion), nil - }, - log: logrus.New(), - } - sb := &strings.Builder{} - err := c.GetSSHConfig(sb, tt.config) + err := WriteSSHConfig(sb, tt.config) if golden.ShouldSet() { golden.Set(t, []byte(sb.String())) } @@ -175,7 +122,7 @@ func TestSSHConfig_GetSSHConfig(t *testing.T) { } } -func TestSSHConfig_GetMuxedSSHConfig(t *testing.T) { +func TestWriteMuxedSSHConfig(t *testing.T) { tests := []struct { name string sshVersion string @@ -221,15 +168,8 @@ func TestSSHConfig_GetMuxedSSHConfig(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - c := &SSHConfig{ - getSSHVersion: func() (*semver.Version, error) { - return semver.New(tt.sshVersion), nil - }, - log: logrus.New(), - } - sb := &strings.Builder{} - err := c.GetMuxedSSHConfig(sb, tt.config) + err := WriteMuxedSSHConfig(sb, tt.config) if golden.ShouldSet() { golden.Set(t, []byte(sb.String())) } diff --git a/lib/config/openssh/testdata/TestSSHConfig_GetMuxedSSHConfig/legacy_OpenSSH_-_single_cluster.golden b/lib/config/openssh/testdata/TestWriteMuxedSSHConfig/legacy_OpenSSH_-_single_cluster.golden similarity index 77% rename from lib/config/openssh/testdata/TestSSHConfig_GetMuxedSSHConfig/legacy_OpenSSH_-_single_cluster.golden rename to lib/config/openssh/testdata/TestWriteMuxedSSHConfig/legacy_OpenSSH_-_single_cluster.golden index 78237dfb739..f79ddf43658 100644 --- a/lib/config/openssh/testdata/TestSSHConfig_GetMuxedSSHConfig/legacy_OpenSSH_-_single_cluster.golden +++ b/lib/config/openssh/testdata/TestWriteMuxedSSHConfig/legacy_OpenSSH_-_single_cluster.golden @@ -3,9 +3,8 @@ Host *.example.com Port 3022 UserKnownHostsFile '/opt/machine-id/known_hosts' - HostKeyAlgorithms ssh-rsa-cert-v01@openssh.com IdentityFile none - IdentityAgent '/opt/machine-id/agent.sock' + IdentityAgent '/opt/machine-id/agent.sock' ProxyCommand '/bin/fdpass-teleport' 'foo' '/opt/machine-id/v1.sock' '%h:%p|example.com' ProxyUseFDPass yes # End generated Teleport configuration diff --git a/lib/config/openssh/testdata/TestSSHConfig_GetMuxedSSHConfig/modern_OpenSSH_-_multiple_clusters.golden b/lib/config/openssh/testdata/TestWriteMuxedSSHConfig/modern_OpenSSH_-_multiple_clusters.golden similarity index 62% rename from lib/config/openssh/testdata/TestSSHConfig_GetMuxedSSHConfig/modern_OpenSSH_-_multiple_clusters.golden rename to lib/config/openssh/testdata/TestWriteMuxedSSHConfig/modern_OpenSSH_-_multiple_clusters.golden index 39469eb92c5..7ce0d4ea5e4 100644 --- a/lib/config/openssh/testdata/TestSSHConfig_GetMuxedSSHConfig/modern_OpenSSH_-_multiple_clusters.golden +++ b/lib/config/openssh/testdata/TestWriteMuxedSSHConfig/modern_OpenSSH_-_multiple_clusters.golden @@ -3,17 +3,15 @@ Host *.example.com Port 3022 UserKnownHostsFile '/opt/machine-id/known_hosts' - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com IdentityFile none - IdentityAgent '/opt/machine-id/agent.sock' + IdentityAgent '/opt/machine-id/agent.sock' ProxyCommand '/bin/fdpass-teleport' 'foo' '/opt/machine-id/v1.sock' '%h:%p|example.com' ProxyUseFDPass yes Host *.example.org Port 3022 UserKnownHostsFile '/opt/machine-id/known_hosts' - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com IdentityFile none - IdentityAgent '/opt/machine-id/agent.sock' + IdentityAgent '/opt/machine-id/agent.sock' ProxyCommand '/bin/fdpass-teleport' 'foo' '/opt/machine-id/v1.sock' '%h:%p|example.org' ProxyUseFDPass yes # End generated Teleport configuration diff --git a/lib/config/openssh/testdata/TestSSHConfig_GetMuxedSSHConfig/modern_OpenSSH_-_single_cluster.golden b/lib/config/openssh/testdata/TestWriteMuxedSSHConfig/modern_OpenSSH_-_single_cluster.golden similarity index 66% rename from lib/config/openssh/testdata/TestSSHConfig_GetMuxedSSHConfig/modern_OpenSSH_-_single_cluster.golden rename to lib/config/openssh/testdata/TestWriteMuxedSSHConfig/modern_OpenSSH_-_single_cluster.golden index a454c0a28e0..f79ddf43658 100644 --- a/lib/config/openssh/testdata/TestSSHConfig_GetMuxedSSHConfig/modern_OpenSSH_-_single_cluster.golden +++ b/lib/config/openssh/testdata/TestWriteMuxedSSHConfig/modern_OpenSSH_-_single_cluster.golden @@ -3,9 +3,8 @@ Host *.example.com Port 3022 UserKnownHostsFile '/opt/machine-id/known_hosts' - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com IdentityFile none - IdentityAgent '/opt/machine-id/agent.sock' + IdentityAgent '/opt/machine-id/agent.sock' ProxyCommand '/bin/fdpass-teleport' 'foo' '/opt/machine-id/v1.sock' '%h:%p|example.com' ProxyUseFDPass yes # End generated Teleport configuration diff --git a/lib/config/openssh/testdata/TestSSHConfig_GetSSHConfig/legacy_OpenSSH_-_single_cluster.golden b/lib/config/openssh/testdata/TestWriteSSHConfig/legacy_OpenSSH_-_single_cluster.golden similarity index 92% rename from lib/config/openssh/testdata/TestSSHConfig_GetSSHConfig/legacy_OpenSSH_-_single_cluster.golden rename to lib/config/openssh/testdata/TestWriteSSHConfig/legacy_OpenSSH_-_single_cluster.golden index 15da15c68c9..f54974b1001 100644 --- a/lib/config/openssh/testdata/TestSSHConfig_GetSSHConfig/legacy_OpenSSH_-_single_cluster.golden +++ b/lib/config/openssh/testdata/TestWriteSSHConfig/legacy_OpenSSH_-_single_cluster.golden @@ -5,7 +5,6 @@ Host *.example.com proxy.example.com UserKnownHostsFile "/home/alice/.tsh/known_hosts" IdentityFile "/home/alice/.tsh/keys/example.com/bob" CertificateFile "/home/alice/.tsh/keys/example.com/bob-ssh/example.com-cert.pub" - HostKeyAlgorithms ssh-rsa-cert-v01@openssh.com # Flags for all example.com hosts except the proxy Host *.example.com !proxy.example.com diff --git a/lib/config/openssh/testdata/TestSSHConfig_GetSSHConfig/modern_OpenSSH_-_multiple_clusters.golden b/lib/config/openssh/testdata/TestWriteSSHConfig/modern_OpenSSH_-_multiple_clusters.golden similarity index 80% rename from lib/config/openssh/testdata/TestSSHConfig_GetSSHConfig/modern_OpenSSH_-_multiple_clusters.golden rename to lib/config/openssh/testdata/TestWriteSSHConfig/modern_OpenSSH_-_multiple_clusters.golden index 9875d49528a..1ab112bb5a7 100644 --- a/lib/config/openssh/testdata/TestSSHConfig_GetSSHConfig/modern_OpenSSH_-_multiple_clusters.golden +++ b/lib/config/openssh/testdata/TestWriteSSHConfig/modern_OpenSSH_-_multiple_clusters.golden @@ -5,7 +5,6 @@ Host *.root proxy.example.com UserKnownHostsFile "/home/alice/.tsh/known_hosts" IdentityFile "/home/alice/.tsh/keys/example.com/bob" CertificateFile "/home/alice/.tsh/keys/example.com/bob-ssh/example.com-cert.pub" - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com # Flags for all root hosts except the proxy Host *.root !proxy.example.com @@ -16,7 +15,6 @@ Host *.leaf proxy.example.com UserKnownHostsFile "/home/alice/.tsh/known_hosts" IdentityFile "/home/alice/.tsh/keys/example.com/bob" CertificateFile "/home/alice/.tsh/keys/example.com/bob-ssh/example.com-cert.pub" - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com # Flags for all leaf hosts except the proxy Host *.leaf !proxy.example.com diff --git a/lib/config/openssh/testdata/TestSSHConfig_GetSSHConfig/modern_OpenSSH_-_single_cluster.golden b/lib/config/openssh/testdata/TestWriteSSHConfig/modern_OpenSSH_-_single_cluster.golden similarity index 83% rename from lib/config/openssh/testdata/TestSSHConfig_GetSSHConfig/modern_OpenSSH_-_single_cluster.golden rename to lib/config/openssh/testdata/TestWriteSSHConfig/modern_OpenSSH_-_single_cluster.golden index f2cab475b57..f54974b1001 100644 --- a/lib/config/openssh/testdata/TestSSHConfig_GetSSHConfig/modern_OpenSSH_-_single_cluster.golden +++ b/lib/config/openssh/testdata/TestWriteSSHConfig/modern_OpenSSH_-_single_cluster.golden @@ -5,7 +5,6 @@ Host *.example.com proxy.example.com UserKnownHostsFile "/home/alice/.tsh/known_hosts" IdentityFile "/home/alice/.tsh/keys/example.com/bob" CertificateFile "/home/alice/.tsh/keys/example.com/bob-ssh/example.com-cert.pub" - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com # Flags for all example.com hosts except the proxy Host *.example.com !proxy.example.com diff --git a/lib/config/openssh/testdata/TestSSHConfig_GetSSHConfig/modern_OpenSSH_-_single_cluster_with_username_and_custom_port.golden b/lib/config/openssh/testdata/TestWriteSSHConfig/modern_OpenSSH_-_single_cluster_with_username_and_custom_port.golden similarity index 84% rename from lib/config/openssh/testdata/TestSSHConfig_GetSSHConfig/modern_OpenSSH_-_single_cluster_with_username_and_custom_port.golden rename to lib/config/openssh/testdata/TestWriteSSHConfig/modern_OpenSSH_-_single_cluster_with_username_and_custom_port.golden index ba174bff3f8..9e4be7509d4 100644 --- a/lib/config/openssh/testdata/TestSSHConfig_GetSSHConfig/modern_OpenSSH_-_single_cluster_with_username_and_custom_port.golden +++ b/lib/config/openssh/testdata/TestWriteSSHConfig/modern_OpenSSH_-_single_cluster_with_username_and_custom_port.golden @@ -5,7 +5,6 @@ Host *.example.com proxy.example.com UserKnownHostsFile "/home/alice/.tsh/known_hosts" IdentityFile "/home/alice/.tsh/keys/example.com/bob" CertificateFile "/home/alice/.tsh/keys/example.com/bob-ssh/example.com-cert.pub" - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com User "testuser" # Flags for all example.com hosts except the proxy diff --git a/lib/config/openssh/testdata/TestSSHConfig_GetSSHConfig/test_shellQuote.golden b/lib/config/openssh/testdata/TestWriteSSHConfig/test_shellQuote.golden similarity index 86% rename from lib/config/openssh/testdata/TestSSHConfig_GetSSHConfig/test_shellQuote.golden rename to lib/config/openssh/testdata/TestWriteSSHConfig/test_shellQuote.golden index 74e9a0bdd7a..8409856c914 100644 --- a/lib/config/openssh/testdata/TestSSHConfig_GetSSHConfig/test_shellQuote.golden +++ b/lib/config/openssh/testdata/TestWriteSSHConfig/test_shellQuote.golden @@ -5,7 +5,6 @@ Host *.example.com proxy.example.com UserKnownHostsFile "/home/alice/.tsh/known_hosts" IdentityFile "/home/alice/.tsh/keys/example.com/bob" CertificateFile "/home/alice/.tsh/keys/example.com/bob-ssh/example.com-cert.pub" - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com # Flags for all example.com hosts except the proxy Host *.example.com !proxy.example.com diff --git a/lib/cryptosuites/suites.go b/lib/cryptosuites/suites.go index 3e342b16ee9..24e2dfe5f19 100644 --- a/lib/cryptosuites/suites.go +++ b/lib/cryptosuites/suites.go @@ -89,6 +89,9 @@ const ( // UserDatabase represents a user Database key. UserDatabase + // HostSSH represents a host SSH key. + HostSSH + // TODO(nklaassen): define remaining key purposes. // keyPurposeMax is 1 greater than the last valid key purpose, used to test that all values less than this @@ -149,6 +152,7 @@ var ( UserSSH: RSA2048, UserTLS: RSA2048, UserDatabase: RSA2048, + HostSSH: RSA2048, // We could consider updating these algorithms even in the legacy suite, // only teleport agents need to accept these connections and they have // never restricted algorithm support. @@ -176,6 +180,7 @@ var ( UserSSH: Ed25519, UserTLS: ECDSAP256, UserDatabase: RSA2048, + HostSSH: Ed25519, ProxyToDatabaseAgent: ECDSAP256, ProxyKubeClient: ECDSAP256, // TODO(nklaassen): define remaining key purposes. @@ -200,6 +205,7 @@ var ( UserSSH: ECDSAP256, UserTLS: ECDSAP256, UserDatabase: RSA2048, + HostSSH: ECDSAP256, ProxyToDatabaseAgent: ECDSAP256, ProxyKubeClient: ECDSAP256, // TODO(nklaassen): define remaining key purposes. @@ -226,6 +232,7 @@ var ( UserSSH: Ed25519, UserTLS: ECDSAP256, UserDatabase: RSA2048, + HostSSH: Ed25519, ProxyToDatabaseAgent: ECDSAP256, ProxyKubeClient: ECDSAP256, // TODO(nklaassen): define remaining key purposes. diff --git a/lib/proxy/router_test.go b/lib/proxy/router_test.go index f846151aec1..fd9e52afa97 100644 --- a/lib/proxy/router_test.go +++ b/lib/proxy/router_test.go @@ -34,7 +34,7 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/agentless" "github.com/gravitational/teleport/lib/auth/authclient" - "github.com/gravitational/teleport/lib/auth/native" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/observability/tracing" "github.com/gravitational/teleport/lib/reversetunnelclient" "github.com/gravitational/teleport/lib/services" @@ -652,7 +652,7 @@ func TestRouter_DialHost(t *testing.T) { return nil, nil } createSigner := func(_ context.Context, _ agentless.CertGenerator) (ssh.Signer, error) { - key, err := native.GeneratePrivateKey() + key, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.Ed25519) if err != nil { return nil, err } diff --git a/lib/reversetunnel/agent_dialer_test.go b/lib/reversetunnel/agent_dialer_test.go index e5511ccb61d..2293c7b7a26 100644 --- a/lib/reversetunnel/agent_dialer_test.go +++ b/lib/reversetunnel/agent_dialer_test.go @@ -20,8 +20,6 @@ package reversetunnel import ( "context" - "crypto/rand" - "crypto/rsa" "testing" "github.com/gravitational/trace" @@ -32,6 +30,7 @@ import ( "github.com/gravitational/teleport/api/types" apisshutils "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/lib/auth/authclient" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/sshutils" "github.com/gravitational/teleport/lib/utils" ) @@ -82,7 +81,7 @@ func TestAgentCertChecker(t *testing.T) { t.Cleanup(func() { require.NoError(t, sshServer.Close()) }) require.NoError(t, sshServer.Start()) - priv, err := rsa.GenerateKey(rand.Reader, 2048) + priv, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.Ed25519) require.NoError(t, err) signer, err := ssh.NewSignerFromKey(priv) diff --git a/lib/reversetunnel/cache.go b/lib/reversetunnel/cache.go index 390b69de5cc..cef856c961e 100644 --- a/lib/reversetunnel/cache.go +++ b/lib/reversetunnel/cache.go @@ -20,6 +20,7 @@ package reversetunnel import ( "context" + "crypto/rsa" "strings" "sync" "time" @@ -34,28 +35,32 @@ import ( "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/auth/native" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/defaults" ) type certificateCache struct { mu sync.Mutex - cache *ttlmap.TTLMap - authClient authclient.ClientI + cache *ttlmap.TTLMap + authClient authclient.ClientI + suiteGetter cryptosuites.GetSuiteFunc } // newHostCertificateCache creates a shared host certificate cache that is -// used by the forwarding server. -func newHostCertificateCache(authClient authclient.ClientI) (*certificateCache, error) { - native.PrecomputeKeys() // ensure native package is set to precompute keys +// used by the forwarding server. [authPrefGetter] technically offers only a +// subset of [authClient], but it allows for using a cached view of the auth +// preference. +func newHostCertificateCache(authClient authclient.ClientI, authPrefGetter cryptosuites.AuthPreferenceGetter) (*certificateCache, error) { cache, err := ttlmap.New(defaults.HostCertCacheSize) if err != nil { return nil, trace.Wrap(err) } return &certificateCache{ - cache: cache, - authClient: authClient, + cache: cache, + authClient: authClient, + suiteGetter: cryptosuites.GetCurrentSuiteFromAuthPreference(authPrefGetter), }, nil } @@ -130,17 +135,34 @@ func (c *certificateCache) generateHostCert(ctx context.Context, principals []st } // Generate public/private keypair. - privBytes, pubBytes, err := native.GenerateKeyPair() + hostKey, err := cryptosuites.GenerateKey(ctx, c.suiteGetter, cryptosuites.HostSSH) if err != nil { return nil, trace.Wrap(err) } + if _, isRSA := hostKey.Public().(*rsa.PublicKey); isRSA { + // Ensure the native package is precomputing RSA keys if we ever + // generate one. [native.PrecomputeKeys] is idempotent. + // Doing this lazily easily handles changing signature algorithm suites + // and won't start precomputing keys if they are never needed (a major + // benefit in tests). + native.PrecomputeKeys() + } + + sshPub, err := ssh.NewPublicKey(hostKey.Public()) + if err != nil { + return nil, trace.Wrap(err) + } + pubBytes := ssh.MarshalAuthorizedKey(sshPub) + // Generate a SSH host certificate. clusterName, err := c.authClient.GetDomainName(context.TODO()) if err != nil { return nil, trace.Wrap(err) } + // TODO(nklaassen): request only an SSH cert, we don't need TLS here. + // GenerateHostCert needs support for this. res, err := c.authClient.TrustClient().GenerateHostCert(ctx, &trustpb.GenerateHostCertRequest{ Key: pubBytes, HostId: principals[0], @@ -156,7 +178,7 @@ func (c *certificateCache) generateHostCert(ctx context.Context, principals []st certBytes := res.SshCertificate // create a *ssh.Certificate - privateKey, err := ssh.ParsePrivateKey(privBytes) + sshSigner, err := ssh.NewSignerFromSigner(hostKey) if err != nil { return nil, trace.Wrap(err) } @@ -166,7 +188,7 @@ func (c *certificateCache) generateHostCert(ctx context.Context, principals []st } // return a ssh.Signer - s, err := ssh.NewCertSigner(cert, privateKey) + s, err := ssh.NewCertSigner(cert, sshSigner) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/reversetunnel/localsite.go b/lib/reversetunnel/localsite.go index 3f81379178c..88834e78a23 100644 --- a/lib/reversetunnel/localsite.go +++ b/lib/reversetunnel/localsite.go @@ -77,30 +77,30 @@ func withProxySyncInterval(interval time.Duration) func(site *localSite) { } } -// withCertificateCache sets the certificateCache of the site. This is particularly -// helpful for tests because construction of the default cache will -// call [native.PrecomputeKeys] which will consume a decent amount of CPU -// to generate keys. -func withCertificateCache(cache *certificateCache) func(site *localSite) { - return func(site *localSite) { - site.certificateCache = cache - } -} - func newLocalSite(srv *server, domainName string, authServers []string, opts ...func(*localSite)) (*localSite, error) { err := metrics.RegisterPrometheusCollectors(localClusterCollectors...) if err != nil { return nil, trace.Wrap(err) } + // instantiate a cache of host certificates for the forwarding server. the + // certificate cache is created in each site (instead of creating it in + // reversetunnel.server and passing it along) so that the host certificate + // is signed by the correct certificate authority. + certificateCache, err := newHostCertificateCache(srv.localAuthClient, srv.localAccessPoint) + if err != nil { + return nil, trace.Wrap(err) + } + s := &localSite{ - srv: srv, - client: srv.localAuthClient, - accessPoint: srv.LocalAccessPoint, - domainName: domainName, - authServers: authServers, - remoteConns: make(map[connKey][]*remoteConn), - clock: srv.Clock, + srv: srv, + client: srv.localAuthClient, + accessPoint: srv.LocalAccessPoint, + certificateCache: certificateCache, + domainName: domainName, + authServers: authServers, + remoteConns: make(map[connKey][]*remoteConn), + clock: srv.Clock, log: log.WithFields(log.Fields{ teleport.ComponentKey: teleport.ComponentReverseTunnelServer, teleport.ComponentFields: map[string]string{ @@ -117,19 +117,6 @@ func newLocalSite(srv *server, domainName string, authServers []string, opts ... opt(s) } - if s.certificateCache == nil { - // instantiate a cache of host certificates for the forwarding server. the - // certificate cache is created in each site (instead of creating it in - // reversetunnel.server and passing it along) so that the host certificate - // is signed by the correct certificate authority. - certificateCache, err := newHostCertificateCache(srv.localAuthClient) - if err != nil { - return nil, trace.Wrap(err) - } - - s.certificateCache = certificateCache - } - // Start periodic functions for the local cluster in the background. go s.periodicFunctions() diff --git a/lib/reversetunnel/localsite_test.go b/lib/reversetunnel/localsite_test.go index 00c591101bd..3c1d8f3cfe3 100644 --- a/lib/reversetunnel/localsite_test.go +++ b/lib/reversetunnel/localsite_test.go @@ -38,14 +38,12 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/lib/auth/authclient" - "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/utils" ) func TestMain(m *testing.M) { utils.InitLoggerForTests() - native.PrecomputeTestKeys(m) os.Exit(m.Run()) } @@ -85,7 +83,6 @@ func TestRemoteConnCleanup(t *testing.T) { site, err := newLocalSite(srv, "clustername", nil, withPeriodicFunctionInterval(time.Hour), withProxySyncInterval(time.Hour), - withCertificateCache(&certificateCache{}), ) require.NoError(t, err) @@ -156,7 +153,6 @@ func TestLocalSiteOverlap(t *testing.T) { site, err := newLocalSite(srv, "clustername", nil, withPeriodicFunctionInterval(time.Hour), - withCertificateCache(&certificateCache{}), ) require.NoError(t, err) @@ -280,7 +276,6 @@ func TestProxyResync(t *testing.T) { site, err := newLocalSite(srv, "clustername", nil, withProxySyncInterval(time.Second), withPeriodicFunctionInterval(24*time.Hour), - withCertificateCache(&certificateCache{}), ) require.NoError(t, err) diff --git a/lib/reversetunnel/srv.go b/lib/reversetunnel/srv.go index d4557a2c759..40124d36498 100644 --- a/lib/reversetunnel/srv.go +++ b/lib/reversetunnel/srv.go @@ -1226,7 +1226,7 @@ func newRemoteSite(srv *server, domainName string, sconn ssh.Conn) (*remoteSite, // certificate cache is created in each site (instead of creating it in // reversetunnel.server and passing it along) so that the host certificate // is signed by the correct certificate authority. - certificateCache, err := newHostCertificateCache(srv.localAuthClient) + certificateCache, err := newHostCertificateCache(srv.localAuthClient, srv.localAccessPoint) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/service/service.go b/lib/service/service.go index e858165f817..f23caee6b0a 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -92,7 +92,6 @@ import ( "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/auth/keygen" "github.com/gravitational/teleport/lib/auth/machineid/machineidv1" - "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/auth/state" "github.com/gravitational/teleport/lib/auth/storage" "github.com/gravitational/teleport/lib/authz" @@ -1053,13 +1052,6 @@ func waitAndReload(ctx context.Context, sigC <-chan os.Signal, cfg servicecfg.Co func NewTeleport(cfg *servicecfg.Config) (*TeleportProcess, error) { var err error - // auth and proxy benefit from precomputing keys since they can experience spikes in key - // generation due to web session creation and recorded session creation respectively. - // for all other agents precomputing keys consumes excess resources. - if cfg.Auth.Enabled || cfg.Proxy.Enabled { - native.PrecomputeKeys() - } - // Before we do anything reset the SIGINT handler back to the default. system.ResetInterruptSignalHandler() diff --git a/lib/tbot/service_identity_output.go b/lib/tbot/service_identity_output.go index 546903d1a91..d5ae5585589 100644 --- a/lib/tbot/service_identity_output.go +++ b/lib/tbot/service_identity_output.go @@ -25,7 +25,6 @@ import ( "path/filepath" "strings" - "github.com/coreos/go-semver/semver" "github.com/gravitational/trace" "github.com/gravitational/teleport/api/client/proto" @@ -41,14 +40,6 @@ import ( "github.com/gravitational/teleport/lib/utils" ) -// sshConfigProxyModeEnv is the environment variable that controls whether or -// not to use the new proxy command. -// It supports: -// - "legacy" (default in v15): use the legacy proxy command -// - "new" (default in v16): use the new proxy command -// In v17, it will be removed. -const sshConfigProxyModeEnv = "TBOT_SSH_CONFIG_PROXY_COMMAND_MODE" - // IdentityOutputService produces credentials which can be used to connect to // Teleport's API or SSH. type IdentityOutputService struct { @@ -66,7 +57,6 @@ type IdentityOutputService struct { // executablePath is called to get the path to the tbot executable. // Usually this is os.Executable executablePath func() (string, error) - getEnv func(key string) string alpnUpgradeCache *alpnProxyConnUpgradeRequiredCache } @@ -191,8 +181,6 @@ func (s *IdentityOutputService) generate(ctx context.Context) error { s.cfg.Destination, s.botAuthClient, s.executablePath, - openssh.GetSystemSSHVersion, - s.getEnv, s.alpnUpgradeCache, s.botCfg, ); err != nil { @@ -254,8 +242,6 @@ func renderSSHConfig( dest bot.Destination, certAuthGetter certAuthGetter, getExecutablePath func() (string, error), - getOpenSSHVersion func() (*semver.Version, error), - getEnv func(key string) string, alpnTester alpnTester, botCfg *config.BotConfig, ) error { @@ -317,63 +303,42 @@ func renderSSHConfig( identityFilePath := filepath.Join(absDestPath, identity.PrivateKeyKey) certificateFilePath := filepath.Join(absDestPath, identity.SSHCertKey) - sshConf := openssh.NewSSHConfig(getOpenSSHVersion, nil) - - if getEnv(sshConfigProxyModeEnv) == "legacy" { - // Deprecated: this block will be removed in v17. It exists so users can - // revert to the old behavior if necessary. - // TODO(strideynet) DELETE IN 17.0.0 - if err := sshConf.GetSSHConfig(&sshConfigBuilder, &openssh.SSHConfigParameters{ - AppName: openssh.TbotApp, - ClusterNames: clusterNames, - KnownHostsPath: knownHostsPath, - IdentityFilePath: identityFilePath, - CertificateFilePath: certificateFilePath, - ProxyHost: proxyHost, - ProxyPort: proxyPort, - ExecutablePath: executablePath, - DestinationDir: absDestPath, - }); err != nil { - return trace.Wrap(err) - } - } else { - // Test if ALPN upgrade is required, this will only be necessary if we - // are using TLS routing. - connUpgradeRequired := false - if proxyPing.Proxy.TLSRoutingEnabled { - connUpgradeRequired, err = alpnTester.isUpgradeRequired( - ctx, proxyPing.Proxy.SSH.PublicAddr, botCfg.Insecure, - ) - if err != nil { - return trace.Wrap(err, "determining if ALPN upgrade is required") - } + // Test if ALPN upgrade is required, this will only be necessary if we + // are using TLS routing. + connUpgradeRequired := false + if proxyPing.Proxy.TLSRoutingEnabled { + connUpgradeRequired, err = alpnTester.isUpgradeRequired( + ctx, proxyPing.Proxy.SSH.PublicAddr, botCfg.Insecure, + ) + if err != nil { + return trace.Wrap(err, "determining if ALPN upgrade is required") } + } - // Generate SSH config - if err := sshConf.GetSSHConfig(&sshConfigBuilder, &openssh.SSHConfigParameters{ - AppName: openssh.TbotApp, - ClusterNames: clusterNames, - KnownHostsPath: knownHostsPath, - IdentityFilePath: identityFilePath, - CertificateFilePath: certificateFilePath, - ProxyHost: proxyHost, - ProxyPort: proxyPort, - ExecutablePath: executablePath, - DestinationDir: absDestPath, + // Generate SSH config + if err := openssh.WriteSSHConfig(&sshConfigBuilder, &openssh.SSHConfigParameters{ + AppName: openssh.TbotApp, + ClusterNames: clusterNames, + KnownHostsPath: knownHostsPath, + IdentityFilePath: identityFilePath, + CertificateFilePath: certificateFilePath, + ProxyHost: proxyHost, + ProxyPort: proxyPort, + ExecutablePath: executablePath, + DestinationDir: absDestPath, - PureTBotProxyCommand: true, - Insecure: botCfg.Insecure, - FIPS: botCfg.FIPS, - TLSRouting: proxyPing.Proxy.TLSRoutingEnabled, - ConnectionUpgrade: connUpgradeRequired, + PureTBotProxyCommand: true, + Insecure: botCfg.Insecure, + FIPS: botCfg.FIPS, + TLSRouting: proxyPing.Proxy.TLSRoutingEnabled, + ConnectionUpgrade: connUpgradeRequired, - // Session resumption is enabled by default, this can be - // configurable at a later date if we discover reasons for this to - // be disabled. - Resume: true, - }); err != nil { - return trace.Wrap(err) - } + // Session resumption is enabled by default, this can be + // configurable at a later date if we discover reasons for this to + // be disabled. + Resume: true, + }); err != nil { + return trace.Wrap(err) } if err := destDirectory.Write(ctx, ssh.ConfigName, []byte(sshConfigBuilder.String())); err != nil { diff --git a/lib/tbot/service_identity_output_test.go b/lib/tbot/service_identity_output_test.go index 3bb63286007..5bbfd4cbfd9 100644 --- a/lib/tbot/service_identity_output_test.go +++ b/lib/tbot/service_identity_output_test.go @@ -26,7 +26,6 @@ import ( "slices" "testing" - "github.com/coreos/go-semver/semver" "github.com/gravitational/trace" "github.com/stretchr/testify/require" @@ -117,39 +116,28 @@ func (p *mockALPNConnTester) isUpgradeRequired(ctx context.Context, addr string, func Test_renderSSHConfig(t *testing.T) { tests := []struct { Name string - Version string - Env map[string]string TLSRouting bool ALPNUpgrade bool }{ { - Name: "legacy OpenSSH", - Version: "6.5.0", - TLSRouting: true, + Name: "no tls routing, no alpn upgrade", + TLSRouting: false, + ALPNUpgrade: false, }, { - Name: "latest OpenSSH", - Version: "9.0.0", - TLSRouting: true, - }, - { - Name: "latest OpenSSH no tls routing", - Version: "9.0.0", - TLSRouting: false, - }, - { - Name: "latest OpenSSH with alpn upgrade", - Version: "9.0.0", + Name: "no tls routing, alpn upgrade", + TLSRouting: false, ALPNUpgrade: true, - TLSRouting: true, }, { - Name: "latest OpenSSH with legacy proxycommand", - Version: "9.0.0", - Env: map[string]string{ - sshConfigProxyModeEnv: "legacy", - }, - TLSRouting: true, + Name: "tls routing, no alpn upgrade", + TLSRouting: true, + ALPNUpgrade: false, + }, + { + Name: "tls routing, alpn upgrade", + TLSRouting: true, + ALPNUpgrade: true, }, } @@ -182,15 +170,6 @@ func Test_renderSSHConfig(t *testing.T) { clusterName: mockClusterName, }, fakeGetExecutablePath, - func() (*semver.Version, error) { - return semver.New(tc.Version), nil - }, - func(key string) string { - if tc.Env == nil { - return "" - } - return tc.Env[key] - }, &mockALPNConnTester{ isALPNUpgradeRequired: tc.ALPNUpgrade, }, diff --git a/lib/tbot/service_ssh_multiplexer.go b/lib/tbot/service_ssh_multiplexer.go index ea5fab6adf2..4cbfdaf34ef 100644 --- a/lib/tbot/service_ssh_multiplexer.go +++ b/lib/tbot/service_ssh_multiplexer.go @@ -192,8 +192,7 @@ func (s *SSHMultiplexerService) writeArtifacts( } var sshConfigBuilder strings.Builder - sshConf := openssh.NewSSHConfig(openssh.GetSystemSSHVersion, nil) - err = sshConf.GetMuxedSSHConfig(&sshConfigBuilder, &openssh.MuxedSSHConfigParameters{ + err = openssh.WriteMuxedSSHConfig(&sshConfigBuilder, &openssh.MuxedSSHConfigParameters{ AppName: openssh.TbotApp, ClusterNames: clusterNames, KnownHostsPath: filepath.Join(absPath, ssh.KnownHostsName), diff --git a/lib/tbot/tbot.go b/lib/tbot/tbot.go index e3025e8eaea..63c923aeea2 100644 --- a/lib/tbot/tbot.go +++ b/lib/tbot/tbot.go @@ -434,7 +434,6 @@ func (b *Bot) Run(ctx context.Context) (err error) { reloadBroadcaster: reloadBroadcaster, resolver: resolver, executablePath: os.Executable, - getEnv: os.Getenv, alpnUpgradeCache: alpnUpgradeCache, proxyPingCache: proxyPingCache, } diff --git a/lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH_with_legacy_proxycommand/ssh_config.golden b/lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH_with_legacy_proxycommand/ssh_config.golden deleted file mode 100644 index d10583bc2b0..00000000000 --- a/lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH_with_legacy_proxycommand/ssh_config.golden +++ /dev/null @@ -1,26 +0,0 @@ -# Begin generated Teleport configuration for tele.blackmesa.gov by tbot - -# Common flags for all tele.blackmesa.gov hosts -Host *.tele.blackmesa.gov tele.blackmesa.gov - UserKnownHostsFile "/test/dir/known_hosts" - IdentityFile "/test/dir/key" - CertificateFile "/test/dir/key-cert.pub" - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com - -# Flags for all tele.blackmesa.gov hosts except the proxy -Host *.tele.blackmesa.gov !tele.blackmesa.gov - Port 3022 - ProxyCommand "/path/to/tbot" proxy --destination-dir=/test/dir --proxy-server=tele.blackmesa.gov:443 ssh --cluster=tele.blackmesa.gov %r@%h:%p -# Common flags for all tele.aperture.labs hosts -Host *.tele.aperture.labs tele.blackmesa.gov - UserKnownHostsFile "/test/dir/known_hosts" - IdentityFile "/test/dir/key" - CertificateFile "/test/dir/key-cert.pub" - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com - -# Flags for all tele.aperture.labs hosts except the proxy -Host *.tele.aperture.labs !tele.blackmesa.gov - Port 3022 - ProxyCommand "/path/to/tbot" proxy --destination-dir=/test/dir --proxy-server=tele.blackmesa.gov:443 ssh --cluster=tele.aperture.labs %r@%h:%p - -# End generated Teleport configuration diff --git a/lib/tbot/testdata/Test_renderSSHConfig/legacy_OpenSSH/known_hosts.golden b/lib/tbot/testdata/Test_renderSSHConfig/legacy_OpenSSH/known_hosts.golden deleted file mode 100644 index 8b414dc2bc1..00000000000 --- a/lib/tbot/testdata/Test_renderSSHConfig/legacy_OpenSSH/known_hosts.golden +++ /dev/null @@ -1,4 +0,0 @@ -@cert-authority tele.blackmesa.gov,tele.blackmesa.gov,*.tele.blackmesa.gov ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8kYdyZA1ZSNjZ4pqybDXvWplHQHkU6fPL+cAYHUkAT5CiQV4GOjwaSTcvZNK5U2fQ0jm6jknCnsZi1t9JujCjXUT3bYHCnSwWhXN55QzIu530Q/MeXz5W8TxYRrWULgPhqqtq8B9N554+s40higG21fmhhdDtpmQzw3vJLspY05mnL1+fW+RIKkM4rb150sdZXKINxfNQvERteE8WX0vL2yG4RuqJzYtGCDEGeHd+HLne7xfmqPxun7bUYaxAlplhm1z2J41hqaj8pBwDSEV9SBOZXvh6FjS9nvJCT7Z1bbZwWrAO/7E2ac0eV+5iEc0J+TyufO3F9uod+J+AICtB type=host -@cert-authority tele.blackmesa.gov,tele.blackmesa.gov,*.tele.blackmesa.gov ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8kYdyZA1ZSNjZ4pqybDXvWplHQHkU6fPL+cAYHUkAT5CiQV4GOjwaSTcvZNK5U2fQ0jm6jknCnsZi1t9JujCjXUT3bYHCnSwWhXN55QzIu530Q/MeXz5W8TxYRrWULgPhqqtq8B9N554+s40higG21fmhhdDtpmQzw3vJLspY05mnL1+fW+RIKkM4rb150sdZXKINxfNQvERteE8WX0vL2yG4RuqJzYtGCDEGeHd+HLne7xfmqPxun7bUYaxAlplhm1z2J41hqaj8pBwDSEV9SBOZXvh6FjS9nvJCT7Z1bbZwWrAO/7E2ac0eV+5iEc0J+TyufO3F9uod+J+AICtB type=host -@cert-authority tele.blackmesa.gov,tele.aperture.labs,*.tele.aperture.labs ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8kYdyZA1ZSNjZ4pqybDXvWplHQHkU6fPL+cAYHUkAT5CiQV4GOjwaSTcvZNK5U2fQ0jm6jknCnsZi1t9JujCjXUT3bYHCnSwWhXN55QzIu530Q/MeXz5W8TxYRrWULgPhqqtq8B9N554+s40higG21fmhhdDtpmQzw3vJLspY05mnL1+fW+RIKkM4rb150sdZXKINxfNQvERteE8WX0vL2yG4RuqJzYtGCDEGeHd+HLne7xfmqPxun7bUYaxAlplhm1z2J41hqaj8pBwDSEV9SBOZXvh6FjS9nvJCT7Z1bbZwWrAO/7E2ac0eV+5iEc0J+TyufO3F9uod+J+AICtB type=host -@cert-authority tele.blackmesa.gov,tele.aperture.labs,*.tele.aperture.labs ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8kYdyZA1ZSNjZ4pqybDXvWplHQHkU6fPL+cAYHUkAT5CiQV4GOjwaSTcvZNK5U2fQ0jm6jknCnsZi1t9JujCjXUT3bYHCnSwWhXN55QzIu530Q/MeXz5W8TxYRrWULgPhqqtq8B9N554+s40higG21fmhhdDtpmQzw3vJLspY05mnL1+fW+RIKkM4rb150sdZXKINxfNQvERteE8WX0vL2yG4RuqJzYtGCDEGeHd+HLne7xfmqPxun7bUYaxAlplhm1z2J41hqaj8pBwDSEV9SBOZXvh6FjS9nvJCT7Z1bbZwWrAO/7E2ac0eV+5iEc0J+TyufO3F9uod+J+AICtB type=host diff --git a/lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH/known_hosts.golden b/lib/tbot/testdata/Test_renderSSHConfig/no_tls_routing,_alpn_upgrade/known_hosts.golden similarity index 100% rename from lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH/known_hosts.golden rename to lib/tbot/testdata/Test_renderSSHConfig/no_tls_routing,_alpn_upgrade/known_hosts.golden diff --git a/lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH_no_tls_routing/ssh_config.golden b/lib/tbot/testdata/Test_renderSSHConfig/no_tls_routing,_alpn_upgrade/ssh_config.golden similarity index 84% rename from lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH_no_tls_routing/ssh_config.golden rename to lib/tbot/testdata/Test_renderSSHConfig/no_tls_routing,_alpn_upgrade/ssh_config.golden index 0f10cea42eb..7a95fe43dec 100644 --- a/lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH_no_tls_routing/ssh_config.golden +++ b/lib/tbot/testdata/Test_renderSSHConfig/no_tls_routing,_alpn_upgrade/ssh_config.golden @@ -5,7 +5,6 @@ Host *.tele.blackmesa.gov tele.blackmesa.gov UserKnownHostsFile "/test/dir/known_hosts" IdentityFile "/test/dir/key" CertificateFile "/test/dir/key-cert.pub" - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com # Flags for all tele.blackmesa.gov hosts except the proxy Host *.tele.blackmesa.gov !tele.blackmesa.gov @@ -16,7 +15,6 @@ Host *.tele.aperture.labs tele.blackmesa.gov UserKnownHostsFile "/test/dir/known_hosts" IdentityFile "/test/dir/key" CertificateFile "/test/dir/key-cert.pub" - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com # Flags for all tele.aperture.labs hosts except the proxy Host *.tele.aperture.labs !tele.blackmesa.gov diff --git a/lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH_no_tls_routing/known_hosts.golden b/lib/tbot/testdata/Test_renderSSHConfig/no_tls_routing,_no_alpn_upgrade/known_hosts.golden similarity index 100% rename from lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH_no_tls_routing/known_hosts.golden rename to lib/tbot/testdata/Test_renderSSHConfig/no_tls_routing,_no_alpn_upgrade/known_hosts.golden diff --git a/lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH/ssh_config.golden b/lib/tbot/testdata/Test_renderSSHConfig/no_tls_routing,_no_alpn_upgrade/ssh_config.golden similarity index 70% rename from lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH/ssh_config.golden rename to lib/tbot/testdata/Test_renderSSHConfig/no_tls_routing,_no_alpn_upgrade/ssh_config.golden index 0168ff3c9e6..7a95fe43dec 100644 --- a/lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH/ssh_config.golden +++ b/lib/tbot/testdata/Test_renderSSHConfig/no_tls_routing,_no_alpn_upgrade/ssh_config.golden @@ -5,22 +5,20 @@ Host *.tele.blackmesa.gov tele.blackmesa.gov UserKnownHostsFile "/test/dir/known_hosts" IdentityFile "/test/dir/key" CertificateFile "/test/dir/key-cert.pub" - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com # Flags for all tele.blackmesa.gov hosts except the proxy Host *.tele.blackmesa.gov !tele.blackmesa.gov Port 3022 - ProxyCommand '/path/to/tbot' ssh-proxy-command --destination-dir='/test/dir' --proxy-server='tele.blackmesa.gov:443' --cluster='tele.blackmesa.gov' --tls-routing --no-connection-upgrade --resume --user=%r --host=%h --port=%p + ProxyCommand '/path/to/tbot' ssh-proxy-command --destination-dir='/test/dir' --proxy-server='tele.blackmesa.gov:443' --cluster='tele.blackmesa.gov' --no-tls-routing --no-connection-upgrade --resume --user=%r --host=%h --port=%p # Common flags for all tele.aperture.labs hosts Host *.tele.aperture.labs tele.blackmesa.gov UserKnownHostsFile "/test/dir/known_hosts" IdentityFile "/test/dir/key" CertificateFile "/test/dir/key-cert.pub" - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com # Flags for all tele.aperture.labs hosts except the proxy Host *.tele.aperture.labs !tele.blackmesa.gov Port 3022 - ProxyCommand '/path/to/tbot' ssh-proxy-command --destination-dir='/test/dir' --proxy-server='tele.blackmesa.gov:443' --cluster='tele.aperture.labs' --tls-routing --no-connection-upgrade --resume --user=%r --host=%h --port=%p + ProxyCommand '/path/to/tbot' ssh-proxy-command --destination-dir='/test/dir' --proxy-server='tele.blackmesa.gov:443' --cluster='tele.aperture.labs' --no-tls-routing --no-connection-upgrade --resume --user=%r --host=%h --port=%p # End generated Teleport configuration diff --git a/lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH_with_alpn_upgrade/known_hosts.golden b/lib/tbot/testdata/Test_renderSSHConfig/tls_routing,_alpn_upgrade/known_hosts.golden similarity index 100% rename from lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH_with_alpn_upgrade/known_hosts.golden rename to lib/tbot/testdata/Test_renderSSHConfig/tls_routing,_alpn_upgrade/known_hosts.golden diff --git a/lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH_with_alpn_upgrade/ssh_config.golden b/lib/tbot/testdata/Test_renderSSHConfig/tls_routing,_alpn_upgrade/ssh_config.golden similarity index 83% rename from lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH_with_alpn_upgrade/ssh_config.golden rename to lib/tbot/testdata/Test_renderSSHConfig/tls_routing,_alpn_upgrade/ssh_config.golden index e5667228991..97bb9d64f6f 100644 --- a/lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH_with_alpn_upgrade/ssh_config.golden +++ b/lib/tbot/testdata/Test_renderSSHConfig/tls_routing,_alpn_upgrade/ssh_config.golden @@ -5,7 +5,6 @@ Host *.tele.blackmesa.gov tele.blackmesa.gov UserKnownHostsFile "/test/dir/known_hosts" IdentityFile "/test/dir/key" CertificateFile "/test/dir/key-cert.pub" - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com # Flags for all tele.blackmesa.gov hosts except the proxy Host *.tele.blackmesa.gov !tele.blackmesa.gov @@ -16,7 +15,6 @@ Host *.tele.aperture.labs tele.blackmesa.gov UserKnownHostsFile "/test/dir/known_hosts" IdentityFile "/test/dir/key" CertificateFile "/test/dir/key-cert.pub" - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com # Flags for all tele.aperture.labs hosts except the proxy Host *.tele.aperture.labs !tele.blackmesa.gov diff --git a/lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH_with_legacy_proxycommand/known_hosts.golden b/lib/tbot/testdata/Test_renderSSHConfig/tls_routing,_no_alpn_upgrade/known_hosts.golden similarity index 100% rename from lib/tbot/testdata/Test_renderSSHConfig/latest_OpenSSH_with_legacy_proxycommand/known_hosts.golden rename to lib/tbot/testdata/Test_renderSSHConfig/tls_routing,_no_alpn_upgrade/known_hosts.golden diff --git a/lib/tbot/testdata/Test_renderSSHConfig/legacy_OpenSSH/ssh_config.golden b/lib/tbot/testdata/Test_renderSSHConfig/tls_routing,_no_alpn_upgrade/ssh_config.golden similarity index 92% rename from lib/tbot/testdata/Test_renderSSHConfig/legacy_OpenSSH/ssh_config.golden rename to lib/tbot/testdata/Test_renderSSHConfig/tls_routing,_no_alpn_upgrade/ssh_config.golden index 9dbc98377e8..7c1999bf545 100644 --- a/lib/tbot/testdata/Test_renderSSHConfig/legacy_OpenSSH/ssh_config.golden +++ b/lib/tbot/testdata/Test_renderSSHConfig/tls_routing,_no_alpn_upgrade/ssh_config.golden @@ -5,7 +5,6 @@ Host *.tele.blackmesa.gov tele.blackmesa.gov UserKnownHostsFile "/test/dir/known_hosts" IdentityFile "/test/dir/key" CertificateFile "/test/dir/key-cert.pub" - HostKeyAlgorithms ssh-rsa-cert-v01@openssh.com # Flags for all tele.blackmesa.gov hosts except the proxy Host *.tele.blackmesa.gov !tele.blackmesa.gov @@ -16,7 +15,6 @@ Host *.tele.aperture.labs tele.blackmesa.gov UserKnownHostsFile "/test/dir/known_hosts" IdentityFile "/test/dir/key" CertificateFile "/test/dir/key-cert.pub" - HostKeyAlgorithms ssh-rsa-cert-v01@openssh.com # Flags for all tele.aperture.labs hosts except the proxy Host *.tele.aperture.labs !tele.blackmesa.gov diff --git a/tool/tsh/common/config.go b/tool/tsh/common/config.go index 295dfa3e248..817d01d9c09 100644 --- a/tool/tsh/common/config.go +++ b/tool/tsh/common/config.go @@ -19,11 +19,9 @@ package common import ( - "fmt" + "io" "net" - "strings" - "github.com/coreos/go-semver/semver" "github.com/gravitational/trace" "github.com/gravitational/teleport/api/profile" @@ -33,9 +31,8 @@ import ( // writeSSHConfig generates an OpenSSH config block from the `sshConfigTemplate` // template string. -func writeSSHConfig(sb *strings.Builder, params *openssh.SSHConfigParameters, getSSHVersion func() (*semver.Version, error)) error { - sshConf := openssh.NewSSHConfig(getSSHVersion, log) - if err := sshConf.GetSSHConfig(sb, params); err != nil { +func writeSSHConfig(w io.Writer, params *openssh.SSHConfigParameters) error { + if err := openssh.WriteSSHConfig(w, params); err != nil { return trace.Wrap(err) } @@ -87,8 +84,7 @@ func onConfig(cf *CLIConf) error { leafClustersNames = append(leafClustersNames, leafCluster.GetName()) } - var sb strings.Builder - if err := writeSSHConfig(&sb, &openssh.SSHConfigParameters{ + if err := writeSSHConfig(cf.Stdout(), &openssh.SSHConfigParameters{ AppName: openssh.TshApp, ClusterNames: append([]string{clusterClient.RootClusterName()}, leafClustersNames...), KnownHostsPath: knownHostsPath, @@ -99,11 +95,9 @@ func onConfig(cf *CLIConf) error { ExecutablePath: cf.executablePath, Username: cf.NodeLogin, Port: int(cf.NodePort), - }, nil); err != nil { + }); err != nil { return trace.Wrap(err) } - stdout := cf.Stdout() - fmt.Fprint(stdout, sb.String()) return nil } diff --git a/tool/tsh/common/config_test.go b/tool/tsh/common/config_test.go index fc4004e6b6e..6728461bd79 100644 --- a/tool/tsh/common/config_test.go +++ b/tool/tsh/common/config_test.go @@ -22,7 +22,6 @@ import ( "strings" "testing" - "github.com/coreos/go-semver/semver" "github.com/stretchr/testify/require" "github.com/gravitational/teleport/lib/config/openssh" @@ -39,7 +38,6 @@ Host *.test-cluster localhost UserKnownHostsFile "/tmp/know_host" IdentityFile "/tmp/alice" CertificateFile "/tmp/localhost-cert.pub" - HostKeyAlgorithms rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com # Flags for all test-cluster hosts except the proxy Host *.test-cluster !localhost @@ -59,8 +57,6 @@ Host *.test-cluster !localhost ProxyHost: "localhost", ProxyPort: "3080", ExecutablePath: "/bin/tsh", - }, func() (*semver.Version, error) { - return semver.New("9.0.0"), nil }) require.NoError(t, err) require.Equal(t, want, sb.String()) diff --git a/tool/tsh/common/proxy_test.go b/tool/tsh/common/proxy_test.go index 1224604a81b..b49d4a9aba3 100644 --- a/tool/tsh/common/proxy_test.go +++ b/tool/tsh/common/proxy_test.go @@ -715,7 +715,7 @@ Host * } // TestTSHConfigConnectWithOpenSSHClient tests OpenSSH configuration generated by tsh config command and -// connects to ssh node using native OpenSSH client with different session recording modes and proxy listener modes. +// connects to ssh node using native OpenSSH client with different session recording modes and proxy listener modes. func TestTSHConfigConnectWithOpenSSHClient(t *testing.T) { // Only run this test if we have access to the external SSH binary. _, err := exec.LookPath("ssh") @@ -748,6 +748,18 @@ func TestTSHConfigConnectWithOpenSSHClient(t *testing.T) { }), }, }, + { + // Test with the legacy signature algorithm suite which generates + // RSA keys. + name: "proxy recording mode with TLS routing enabled legacy", + opts: []testSuiteOptionFunc{ + withRootConfigFunc(func(cfg *servicecfg.Config) { + cfg.Auth.SessionRecordingConfig.SetMode(types.RecordAtProxySync) + cfg.Auth.NetworkingConfig.SetProxyListenerMode(types.ProxyListenerMode_Multiplex) + cfg.Auth.Preference.SetSignatureAlgorithmSuite(types.SignatureAlgorithmSuite_SIGNATURE_ALGORITHM_SUITE_LEGACY) + }), + }, + }, { name: "node recording mode with TLS routing disabled", opts: []testSuiteOptionFunc{ @@ -1069,7 +1081,7 @@ func mustFailToRunOpenSSHCommand(t *testing.T, configFile string, sshConnString } func mustFindFailedNodeLoginAttempt(t *testing.T, s *suite, nodeLogin string) { - require.EventuallyWithT(t, func(t *assert.CollectT) { + err := retryutils.RetryStaticFor(5*time.Second, 50*time.Millisecond, func() error { now := time.Now() ctx := context.Background() es, _, err := s.root.GetAuthServer().SearchEvents(ctx, events.SearchEventsRequest{ @@ -1077,16 +1089,18 @@ func mustFindFailedNodeLoginAttempt(t *testing.T, s *suite, nodeLogin string) { To: now.Add(time.Hour), Order: types.EventOrderDescending, }) - assert.NoError(t, err) + if err != nil { + return trace.Wrap(err) + } for _, e := range es { - if e.GetCode() == events.AuthAttemptFailureCode { - assert.Equal(t, e.(*apievents.AuthAttempt).Login, nodeLogin) - return + if e.GetCode() == events.AuthAttemptFailureCode && e.(*apievents.AuthAttempt).Login == nodeLogin { + return nil } } - t.Errorf("failed to find AuthAttemptFailureCode event (0/%d events matched)", len(es)) - }, 5*time.Second, 500*time.Millisecond) + return fmt.Errorf("failed to find AuthAttemptFailureCode event (0/%d events matched)", len(es)) + }) + require.NoError(t, err) } func TestFormatCommand(t *testing.T) { diff --git a/tool/tsh/common/tsh_helper_test.go b/tool/tsh/common/tsh_helper_test.go index 0a620358c7b..2c419ef64af 100644 --- a/tool/tsh/common/tsh_helper_test.go +++ b/tool/tsh/common/tsh_helper_test.go @@ -89,6 +89,9 @@ func (s *suite) setupRootCluster(t *testing.T, options testSuiteOptions) { }, ClusterName: "root", SessionRecording: "node-sync", + Authentication: &config.AuthenticationConfig{ + SignatureAlgorithmSuite: types.SignatureAlgorithmSuite_SIGNATURE_ALGORITHM_SUITE_BALANCED_V1, + }, }, }