mirror of
https://github.com/gravitational/teleport.git
synced 2026-11-02 14:59:53 +08:00
Machine ID: Switch to using new tbot ssh-proxy-command by default. (#41482)
* Add warning to previous command * Hash out basics of new tbot proxying command use * Avoid passing full botConfig into `tbot ssh-proxy-command` * wip * simplify impl * Set up test suite for ssh config generation * Add support for checking if ALPNConnUpgrade is required * Cache the alpn upgrade result * Americanize Spellings * Update lib/tbot/config/template_ssh_client.go Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com> * Add CHANGELOG.md entry for breaking change * Update tool/tbot/proxy.go Co-authored-by: Edoardo Spadolini <edoardo.spadolini@goteleport.com> * Update lib/tbot/tbot.go Co-authored-by: Edoardo Spadolini <edoardo.spadolini@goteleport.com> * Switch to `--no` variants rather than `={bool}` * Use SingleFlight on alpn proxy upgrade cache * Improve singleflight * Add rudimentary shell quoting * Uniformly pad the templating * Rename shellQuote -> proxyCommandQuote * Fix imports --------- Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com> Co-authored-by: Edoardo Spadolini <edoardo.spadolini@goteleport.com>
This commit is contained in:
co-authored by
rosstimothy
Edoardo Spadolini
parent
120efb9bd7
commit
bd4dcab802
@@ -27,6 +27,13 @@ policy [example](docs/pages/includes/dynamodb-iam-policy.mdx).
|
||||
|
||||
Support for disabling second factor authentication has been removed
|
||||
|
||||
#### Machine ID and OpenSSH client config changes
|
||||
|
||||
Users with custom `ssh_config` should modify their ProxyCommand to use the new,
|
||||
more performant, `tbot ssh-proxy-command`. See
|
||||
https://goteleport.com/docs/machine-id/reference/v16-upgrade-guide/ for more
|
||||
details.
|
||||
|
||||
## 15.0.0 (xx/xx/24)
|
||||
|
||||
### New features
|
||||
|
||||
@@ -35,7 +35,18 @@ import (
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
)
|
||||
|
||||
var sshConfigTemplate = template.Must(template.New("ssh-config").Parse(
|
||||
var sshConfigTemplate = template.Must(template.New("ssh-config").Funcs(template.FuncMap{
|
||||
// proxyCommandQuote prepares a string for insertion into the ssh_config
|
||||
"proxyCommandQuote": func(s string) string {
|
||||
s = `'` + strings.ReplaceAll(s, `'`, `'"'"'`) + `'`
|
||||
// escape any percent signs which could trigger the percent expansion
|
||||
// for ProxyCommand.
|
||||
s = strings.ReplaceAll(s, `%`, `%%`)
|
||||
// escape any newlines which could impact the parsing of ssh_config
|
||||
s = strings.ReplaceAll(s, "\n", `'"\n"'`)
|
||||
return s
|
||||
},
|
||||
}).Parse(
|
||||
`# Begin generated Teleport configuration for {{ .ProxyHost }} by {{ .AppName }}
|
||||
{{$dot := . }}
|
||||
{{- range $clusterName := .ClusterNames }}
|
||||
@@ -52,11 +63,16 @@ Host *.{{ $clusterName }} {{ $dot.ProxyHost }}
|
||||
# Flags for all {{ $clusterName }} hosts except the proxy
|
||||
Host *.{{ $clusterName }} !{{ $dot.ProxyHost }}
|
||||
Port {{ $dot.Port }}
|
||||
{{- if eq $dot.AppName "tsh" }}
|
||||
{{- if eq $dot.AppName "tsh" }}
|
||||
ProxyCommand "{{ $dot.ExecutablePath }}" proxy ssh --cluster={{ $clusterName }} --proxy={{ $dot.ProxyHost }}:{{ $dot.ProxyPort }} %r@%h:%p
|
||||
{{- end }}{{- if eq $dot.AppName "tbot" }}
|
||||
{{- end }}
|
||||
{{- if eq $dot.AppName "tbot" }}
|
||||
{{- if $dot.PureTBotProxyCommand }}
|
||||
ProxyCommand {{ proxyCommandQuote $dot.ExecutablePath }} ssh-proxy-command --destination-dir={{ proxyCommandQuote $dot.DestinationDir }} --proxy-server={{ proxyCommandQuote (print $dot.ProxyHost ":" $dot.ProxyPort) }} --cluster={{ proxyCommandQuote $clusterName }} {{ if $dot.TLSRouting }}--tls-routing{{ else }}--no-tls-routing{{ end }} {{ if $dot.ConnectionUpgrade }}--connection-upgrade{{ else }}--no-connection-upgrade{{ end }} {{ if $dot.Resume }}--resume{{ else }}--no-resume{{ end }} --user=%r --host=%h --port=%p
|
||||
{{- else }}
|
||||
ProxyCommand "{{ $dot.ExecutablePath }}" proxy --destination-dir={{ $dot.DestinationDir }} --proxy-server={{ $dot.ProxyHost }}:{{ $dot.ProxyPort }} ssh --cluster={{ $clusterName }} %r@%h:%p
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if ne $dot.Username "" }}
|
||||
User "{{ $dot.Username }}"
|
||||
@@ -79,6 +95,15 @@ type SSHConfigParameters struct {
|
||||
DestinationDir string
|
||||
// Port is the node port to use, defaulting to 3022, if not specified by flag
|
||||
Port int
|
||||
|
||||
// PureTBotProxyCommand enables the new `ssh-proxy-command` operating mode
|
||||
// when generating the ssh_config for tbot.
|
||||
PureTBotProxyCommand bool
|
||||
ConnectionUpgrade bool
|
||||
TLSRouting bool
|
||||
Insecure bool
|
||||
FIPS bool
|
||||
Resume bool
|
||||
}
|
||||
|
||||
type sshTmplParams struct {
|
||||
|
||||
@@ -137,6 +137,22 @@ func TestSSHConfig_GetSSHConfig(t *testing.T) {
|
||||
Port: 3232,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "test shellQuote",
|
||||
sshVersion: "9.0.0",
|
||||
config: &SSHConfigParameters{
|
||||
AppName: TbotApp,
|
||||
PureTBotProxyCommand: true,
|
||||
ClusterNames: []string{"example.com"},
|
||||
KnownHostsPath: "/home/alice/.tsh/known_hosts",
|
||||
IdentityFilePath: "/home/alice/.tsh/keys/example.com/bob",
|
||||
CertificateFilePath: "/home/alice/.tsh/keys/example.com/bob-ssh/example.com-cert.pub",
|
||||
ProxyHost: "proxy.example.com",
|
||||
ProxyPort: "443",
|
||||
ExecutablePath: "/home/edoardo/$( sudo rm -rf / )/tbot",
|
||||
DestinationDir: "/home/edo\nardo/$( sudo rm -rf / )/tbot-ou'tput",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# Begin generated Teleport configuration for proxy.example.com by tbot
|
||||
|
||||
# Common flags for all example.com hosts
|
||||
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
|
||||
Port 3022
|
||||
ProxyCommand '/home/edoardo/$( sudo rm -rf / )/tbot' ssh-proxy-command --destination-dir='/home/edo'"\n"'ardo/$( sudo rm -rf / )/tbot-ou'"'"'tput' --proxy-server='proxy.example.com:443' --cluster='example.com' --no-tls-routing --no-connection-upgrade --no-resume --user=%r --host=%h --port=%p
|
||||
|
||||
# End generated Teleport configuration
|
||||
@@ -61,4 +61,10 @@ type provider interface {
|
||||
|
||||
// SignX509SVIDs uses the impersonatedClient to call SignX509SVIDs.
|
||||
SignX509SVIDs(ctx context.Context, in *machineidv1pb.SignX509SVIDsRequest, opts ...grpc.CallOption) (*machineidv1pb.SignX509SVIDsResponse, error)
|
||||
|
||||
// IsALPNConnUpgradeRequired returns a (possibly cached) test of whether ALPN
|
||||
// routing is required.
|
||||
IsALPNConnUpgradeRequired(
|
||||
ctx context.Context, addr string, insecure bool,
|
||||
) (bool, error)
|
||||
}
|
||||
|
||||
@@ -63,10 +63,12 @@ func fakeGetExecutablePath() (string, error) {
|
||||
|
||||
// mockProvider is a minimal Bot impl that can be used in tests
|
||||
type mockProvider struct {
|
||||
cfg *BotConfig
|
||||
proxyAddr string
|
||||
remoteClusterName string
|
||||
clusterName string
|
||||
cfg *BotConfig
|
||||
proxyAddr string
|
||||
remoteClusterName string
|
||||
clusterName string
|
||||
isTLSRouting bool
|
||||
isALPNUpgradeRequired bool
|
||||
}
|
||||
|
||||
func newMockProvider(cfg *BotConfig) *mockProvider {
|
||||
@@ -75,9 +77,16 @@ func newMockProvider(cfg *BotConfig) *mockProvider {
|
||||
proxyAddr: mockProxyAddr,
|
||||
clusterName: mockClusterName,
|
||||
remoteClusterName: mockRemoteClusterName,
|
||||
isTLSRouting: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *mockProvider) IsALPNConnUpgradeRequired(
|
||||
ctx context.Context, addr string, insecure bool,
|
||||
) (bool, error) {
|
||||
return p.isALPNUpgradeRequired, nil
|
||||
}
|
||||
|
||||
func (p *mockProvider) GetRemoteClusters(ctx context.Context) ([]types.RemoteCluster, error) {
|
||||
rc, err := types.NewRemoteCluster(p.remoteClusterName)
|
||||
if err != nil {
|
||||
@@ -167,7 +176,7 @@ func (p *mockProvider) ProxyPing(ctx context.Context) (*webclient.PingResponse,
|
||||
return &webclient.PingResponse{
|
||||
ClusterName: p.clusterName,
|
||||
Proxy: webclient.ProxySettings{
|
||||
TLSRoutingEnabled: true,
|
||||
TLSRoutingEnabled: p.isTLSRouting,
|
||||
SSH: webclient.SSHProxySettings{
|
||||
PublicAddr: p.proxyAddr,
|
||||
},
|
||||
|
||||
@@ -89,6 +89,7 @@ func (o *IdentityOutput) templates() []template {
|
||||
templates = append(templates, &templateSSHClient{
|
||||
getSSHVersion: openssh.GetSystemSSHVersion,
|
||||
executablePathGetter: os.Executable,
|
||||
getEnv: os.Getenv,
|
||||
destPath: o.destPath,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -36,10 +36,19 @@ 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"
|
||||
|
||||
// templateSSHClient contains parameters for the ssh_config config
|
||||
// template
|
||||
type templateSSHClient struct {
|
||||
getSSHVersion func() (*semver.Version, error)
|
||||
getEnv func(key string) string
|
||||
executablePathGetter executablePathGetter
|
||||
// destPath controls whether or not to write the SSH config file.
|
||||
// This is lets this be skipped on non-directory destinations where this
|
||||
@@ -159,18 +168,63 @@ func (c *templateSSHClient) render(
|
||||
certificateFilePath := filepath.Join(absDestPath, identity.SSHCertKey)
|
||||
|
||||
sshConf := openssh.NewSSHConfig(c.getSSHVersion, nil)
|
||||
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)
|
||||
botConfig := bot.Config()
|
||||
|
||||
if c.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 ping.Proxy.TLSRoutingEnabled {
|
||||
connUpgradeRequired, err = bot.IsALPNConnUpgradeRequired(
|
||||
ctx, ping.Proxy.SSH.PublicAddr, botConfig.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,
|
||||
|
||||
PureTBotProxyCommand: true,
|
||||
Insecure: botConfig.Insecure,
|
||||
FIPS: botConfig.FIPS,
|
||||
TLSRouting: ping.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)
|
||||
}
|
||||
}
|
||||
|
||||
if err := destination.Write(ctx, sshConfigName, []byte(sshConfigBuilder.String())); err != nil {
|
||||
|
||||
@@ -35,16 +35,40 @@ import (
|
||||
|
||||
func TestTemplateSSHClient_Render(t *testing.T) {
|
||||
tests := []struct {
|
||||
Name string
|
||||
Version string
|
||||
Name string
|
||||
Version string
|
||||
Env map[string]string
|
||||
TLSRouting bool
|
||||
ALPNUpgrade bool
|
||||
}{
|
||||
{
|
||||
Name: "legacy OpenSSH",
|
||||
Version: "6.5.0",
|
||||
Name: "legacy OpenSSH",
|
||||
Version: "6.5.0",
|
||||
TLSRouting: true,
|
||||
},
|
||||
{
|
||||
Name: "latest OpenSSH",
|
||||
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",
|
||||
ALPNUpgrade: true,
|
||||
TLSRouting: true,
|
||||
},
|
||||
{
|
||||
Name: "latest OpenSSH with legacy proxycommand",
|
||||
Version: "9.0.0",
|
||||
Env: map[string]string{
|
||||
sshConfigProxyModeEnv: "legacy",
|
||||
},
|
||||
TLSRouting: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -64,12 +88,20 @@ func TestTemplateSSHClient_Render(t *testing.T) {
|
||||
}
|
||||
|
||||
mockBot := newMockProvider(cfg)
|
||||
mockBot.isALPNUpgradeRequired = tc.ALPNUpgrade
|
||||
mockBot.isTLSRouting = tc.TLSRouting
|
||||
tmpl := templateSSHClient{
|
||||
getSSHVersion: func() (*semver.Version, error) {
|
||||
return semver.New(tc.Version), nil
|
||||
},
|
||||
executablePathGetter: fakeGetExecutablePath,
|
||||
destPath: dest.Path,
|
||||
getEnv: func(key string) string {
|
||||
if tc.Env == nil {
|
||||
return ""
|
||||
}
|
||||
return tc.Env[key]
|
||||
},
|
||||
}
|
||||
|
||||
err = tmpl.render(context.Background(), mockBot, ident, dest)
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ Host *.tele.blackmesa.gov tele.blackmesa.gov
|
||||
# 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
|
||||
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
|
||||
# Common flags for all tele.aperture.labs hosts
|
||||
Host *.tele.aperture.labs tele.blackmesa.gov
|
||||
UserKnownHostsFile "/test/dir/known_hosts"
|
||||
@@ -21,6 +21,6 @@ Host *.tele.aperture.labs tele.blackmesa.gov
|
||||
# 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
|
||||
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
|
||||
|
||||
# End generated Teleport configuration
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
@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
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
# 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' 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' --no-tls-routing --no-connection-upgrade --resume --user=%r --host=%h --port=%p
|
||||
|
||||
# End generated Teleport configuration
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
@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
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
# 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' ssh-proxy-command --destination-dir='/test/dir' --proxy-server='tele.blackmesa.gov:443' --cluster='tele.blackmesa.gov' --tls-routing --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 --connection-upgrade --resume --user=%r --host=%h --port=%p
|
||||
|
||||
# End generated Teleport configuration
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
@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
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
# 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
|
||||
+2
-2
@@ -10,7 +10,7 @@ Host *.tele.blackmesa.gov tele.blackmesa.gov
|
||||
# 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
|
||||
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
|
||||
# Common flags for all tele.aperture.labs hosts
|
||||
Host *.tele.aperture.labs tele.blackmesa.gov
|
||||
UserKnownHostsFile "/test/dir/known_hosts"
|
||||
@@ -21,6 +21,6 @@ Host *.tele.aperture.labs tele.blackmesa.gov
|
||||
# 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
|
||||
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
|
||||
|
||||
# End generated Teleport configuration
|
||||
|
||||
@@ -63,6 +63,7 @@ type outputsService struct {
|
||||
reloadBroadcaster *channelBroadcaster
|
||||
proxyPingCache *proxyPingCache
|
||||
authPingCache *authPingCache
|
||||
alpnUpgradeCache *alpnProxyConnUpgradeRequiredCache
|
||||
botClient *authclient.Client
|
||||
getBotIdentity getBotIdentityFn
|
||||
cfg *config.BotConfig
|
||||
@@ -93,10 +94,11 @@ func (s *outputsService) renewOutputs(
|
||||
// create a cache shared across outputs so they don't hammer the auth
|
||||
// server with similar requests
|
||||
drc := &outputRenewalCache{
|
||||
proxyPingCache: s.proxyPingCache,
|
||||
authPingCache: s.authPingCache,
|
||||
client: s.botClient,
|
||||
cfg: s.cfg,
|
||||
proxyPingCache: s.proxyPingCache,
|
||||
authPingCache: s.authPingCache,
|
||||
alpnUpgradeCache: s.alpnUpgradeCache,
|
||||
client: s.botClient,
|
||||
cfg: s.cfg,
|
||||
}
|
||||
|
||||
// Determine the default role list based on the bot role. The role's
|
||||
@@ -658,10 +660,11 @@ func fetchDefaultRoles(ctx context.Context, roleGetter services.RoleGetter, iden
|
||||
// requests for the same information. This is shared between all of the
|
||||
// outputs.
|
||||
type outputRenewalCache struct {
|
||||
client *authclient.Client
|
||||
cfg *config.BotConfig
|
||||
proxyPingCache *proxyPingCache
|
||||
authPingCache *authPingCache
|
||||
client *authclient.Client
|
||||
cfg *config.BotConfig
|
||||
proxyPingCache *proxyPingCache
|
||||
authPingCache *authPingCache
|
||||
alpnUpgradeCache *alpnProxyConnUpgradeRequiredCache
|
||||
|
||||
mu sync.Mutex
|
||||
// These are protected by getter/setters with mutex locks
|
||||
@@ -716,6 +719,18 @@ func (orc *outputRenewalCache) ProxyPing(ctx context.Context) (*webclient.PingRe
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// IsALPNConnUpgradeRequired returns a (possibly cached) test of whether ALPN
|
||||
// routing is required.
|
||||
func (orc *outputRenewalCache) IsALPNConnUpgradeRequired(
|
||||
ctx context.Context, addr string, insecure bool,
|
||||
) (bool, error) {
|
||||
res, err := orc.alpnUpgradeCache.isUpgradeRequired(ctx, addr, insecure)
|
||||
if err != nil {
|
||||
return false, trace.Wrap(err)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Config returns the bots config.
|
||||
func (orc *outputRenewalCache) Config() *config.BotConfig {
|
||||
return orc.cfg
|
||||
|
||||
+29
-21
@@ -39,14 +39,15 @@ import (
|
||||
"github.com/gravitational/teleport/lib/resumption"
|
||||
"github.com/gravitational/teleport/lib/tbot/config"
|
||||
"github.com/gravitational/teleport/lib/tbot/identity"
|
||||
"github.com/gravitational/teleport/lib/tbot/tshwrap"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
)
|
||||
|
||||
// ProxySSHConfig contains configuration parameters required
|
||||
// to initialize the local ssh proxy.
|
||||
type ProxySSHConfig struct {
|
||||
BotConfig *config.BotConfig
|
||||
DestinationPath string
|
||||
Insecure bool
|
||||
FIPS bool
|
||||
TSHConfigPath string
|
||||
ProxyServer string
|
||||
Cluster string
|
||||
@@ -62,17 +63,27 @@ type ProxySSHConfig struct {
|
||||
// ProxySSH creates a local ssh proxy, dialing a node and transferring data through
|
||||
// stdin and stdout, to be used as an OpenSSH/PuTTY proxy command.
|
||||
func ProxySSH(ctx context.Context, proxyConfig ProxySSHConfig) error {
|
||||
tshConfig, err := libclient.LoadTSHConfig(proxyConfig.TSHConfigPath)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
tshConfig := &libclient.TSHConfig{}
|
||||
if proxyConfig.TSHConfigPath != "" {
|
||||
var err error
|
||||
tshConfig, err = libclient.LoadTSHConfig(proxyConfig.TSHConfigPath)
|
||||
if err != nil {
|
||||
return trace.Wrap(err, "loading proxy templates")
|
||||
}
|
||||
}
|
||||
|
||||
proxy := proxyConfig.ProxyServer
|
||||
cluster := proxyConfig.Cluster
|
||||
targetHost := proxyConfig.Host
|
||||
expanded, matched := tshConfig.ProxyTemplates.Apply(net.JoinHostPort(proxyConfig.Host, proxyConfig.Port))
|
||||
expanded, matched := tshConfig.ProxyTemplates.Apply(
|
||||
net.JoinHostPort(proxyConfig.Host, proxyConfig.Port),
|
||||
)
|
||||
if matched {
|
||||
proxyConfig.Log.DebugContext(ctx, "proxy templated matched", "populated_template", expanded)
|
||||
proxyConfig.Log.DebugContext(
|
||||
ctx,
|
||||
"proxy templated matched",
|
||||
"populated_template", expanded,
|
||||
)
|
||||
if expanded.Cluster != "" {
|
||||
cluster = expanded.Cluster
|
||||
}
|
||||
@@ -89,7 +100,13 @@ func ProxySSH(ctx context.Context, proxyConfig ProxySSHConfig) error {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
facade, keyring, err := parseIdentity(proxyConfig.BotConfig, proxyHost, cluster)
|
||||
facade, keyring, err := parseIdentity(
|
||||
proxyConfig.DestinationPath,
|
||||
proxyHost,
|
||||
cluster,
|
||||
proxyConfig.Insecure,
|
||||
proxyConfig.FIPS,
|
||||
)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
@@ -125,7 +142,7 @@ func ProxySSH(ctx context.Context, proxyConfig ProxySSHConfig) error {
|
||||
UnaryInterceptors: []grpc.UnaryClientInterceptor{interceptors.GRPCClientUnaryErrorInterceptor},
|
||||
StreamInterceptors: []grpc.StreamClientInterceptor{interceptors.GRPCClientStreamErrorInterceptor},
|
||||
SSHConfig: sshConfig,
|
||||
InsecureSkipVerify: proxyConfig.BotConfig.Insecure,
|
||||
InsecureSkipVerify: proxyConfig.Insecure,
|
||||
ALPNConnUpgradeRequired: proxyConfig.ConnectionUpgradeRequired,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -217,17 +234,8 @@ func resolveTargetHost(ctx context.Context, cfg client.Config, search, query str
|
||||
return nodes[0], nil
|
||||
}
|
||||
|
||||
func parseIdentity(botConfig *config.BotConfig, proxy, cluster string) (*identity.Facade, agent.ExtendedAgent, error) {
|
||||
destination, err := tshwrap.GetDestinationDirectory(botConfig)
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
identityPath := filepath.Join(destination.Path, config.IdentityFilePath)
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
func parseIdentity(destPath, proxy, cluster string, insecure, fips bool) (*identity.Facade, agent.ExtendedAgent, error) {
|
||||
identityPath := filepath.Join(destPath, config.IdentityFilePath)
|
||||
key, err := identityfile.KeyFromIdentityFile(identityPath, proxy, cluster)
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
@@ -258,7 +266,7 @@ func parseIdentity(botConfig *config.BotConfig, proxy, cluster string) (*identit
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return identity.NewFacade(botConfig.FIPS, botConfig.Insecure, i), keyring, nil
|
||||
return identity.NewFacade(fips, insecure, i), keyring, nil
|
||||
}
|
||||
|
||||
func cleanTargetHost(targetHost, proxyHost, siteName string) string {
|
||||
|
||||
+67
-6
@@ -21,6 +21,7 @@ package tbot
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -29,9 +30,11 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opentelemetry.io/otel"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/sync/singleflight"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/gravitational/teleport"
|
||||
"github.com/gravitational/teleport/api/client"
|
||||
"github.com/gravitational/teleport/api/client/proto"
|
||||
"github.com/gravitational/teleport/api/client/webclient"
|
||||
"github.com/gravitational/teleport/api/metadata"
|
||||
@@ -208,6 +211,10 @@ func (b *Bot) Run(ctx context.Context) (err error) {
|
||||
botCfg: b.cfg,
|
||||
log: b.log,
|
||||
}
|
||||
alpnUpgradeCache := &alpnProxyConnUpgradeRequiredCache{
|
||||
botCfg: b.cfg,
|
||||
log: b.log,
|
||||
}
|
||||
|
||||
// Setup all other services
|
||||
if b.cfg.DiagAddr != "" {
|
||||
@@ -220,12 +227,13 @@ func (b *Bot) Run(ctx context.Context) (err error) {
|
||||
})
|
||||
}
|
||||
services = append(services, &outputsService{
|
||||
authPingCache: authPingCache,
|
||||
proxyPingCache: proxyPingCache,
|
||||
getBotIdentity: b.botIdentitySvc.GetIdentity,
|
||||
botClient: b.botIdentitySvc.GetClient(),
|
||||
cfg: b.cfg,
|
||||
resolver: resolver,
|
||||
authPingCache: authPingCache,
|
||||
proxyPingCache: proxyPingCache,
|
||||
alpnUpgradeCache: alpnUpgradeCache,
|
||||
getBotIdentity: b.botIdentitySvc.GetIdentity,
|
||||
botClient: b.botIdentitySvc.GetClient(),
|
||||
cfg: b.cfg,
|
||||
resolver: resolver,
|
||||
log: b.log.With(
|
||||
teleport.ComponentKey, teleport.Component(componentTBot, "outputs"),
|
||||
),
|
||||
@@ -579,3 +587,56 @@ func (p *proxyPingCache) ping(ctx context.Context) (*webclient.PingResponse, err
|
||||
|
||||
return p.cachedValue, nil
|
||||
}
|
||||
|
||||
type alpnProxyConnUpgradeRequiredCache struct {
|
||||
botCfg *config.BotConfig
|
||||
log *slog.Logger
|
||||
|
||||
mu sync.Mutex
|
||||
cache map[string]bool
|
||||
group singleflight.Group
|
||||
}
|
||||
|
||||
func (a *alpnProxyConnUpgradeRequiredCache) isUpgradeRequired(ctx context.Context, addr string, insecure bool) (bool, error) {
|
||||
key := fmt.Sprintf("%s-%t", addr, insecure)
|
||||
|
||||
a.mu.Lock()
|
||||
if a.cache == nil {
|
||||
a.cache = make(map[string]bool)
|
||||
}
|
||||
v, ok := a.cache[key]
|
||||
if ok {
|
||||
a.mu.Unlock()
|
||||
return v, nil
|
||||
}
|
||||
a.mu.Unlock()
|
||||
|
||||
val, err, _ := a.group.Do(key, func() (interface{}, error) {
|
||||
// Recheck the cache in case we've just missed a previous group
|
||||
// completing
|
||||
a.mu.Lock()
|
||||
v, ok := a.cache[key]
|
||||
if ok {
|
||||
a.mu.Unlock()
|
||||
return v, nil
|
||||
}
|
||||
a.mu.Unlock()
|
||||
|
||||
// Ok, now we know for sure that the work hasn't already been done or
|
||||
// isn't in flight, we can complete it.
|
||||
a.log.DebugContext(ctx, "Testing ALPN upgrade necessary", "addr", addr, "insecure", insecure)
|
||||
v = client.IsALPNConnUpgradeRequired(ctx, addr, insecure)
|
||||
a.log.DebugContext(ctx, "Tested ALPN upgrade necessary", "addr", addr, "insecure", insecure, "result", v)
|
||||
if err := ctx.Err(); err != nil {
|
||||
// Check for case where false is returned because client canceled ctx.
|
||||
// We don't want to cache this result.
|
||||
return v, trace.Wrap(err)
|
||||
}
|
||||
|
||||
a.mu.Lock()
|
||||
a.cache[key] = v
|
||||
a.mu.Unlock()
|
||||
return v, nil
|
||||
})
|
||||
return val.(bool), err
|
||||
}
|
||||
|
||||
+2
-2
@@ -310,9 +310,9 @@ func Run(args []string, stdout io.Writer) error {
|
||||
case dbCmd.FullCommand():
|
||||
err = onDBCommand(botConfig, &cf)
|
||||
case proxyCmd.FullCommand():
|
||||
err = onProxyCommand(botConfig, &cf)
|
||||
err = onProxyCommand(ctx, botConfig, &cf)
|
||||
case sshProxyCmd.FullCommand():
|
||||
err = onSSHProxyCommand(botConfig, &cf)
|
||||
err = onSSHProxyCommand(ctx, &cf)
|
||||
case kubeCredentialsCmd.FullCommand():
|
||||
err = onKubeCredentialsCommand(ctx, botConfig)
|
||||
case spiffeInspectCmd.FullCommand():
|
||||
|
||||
+7
-4
@@ -19,6 +19,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
@@ -29,7 +30,9 @@ import (
|
||||
"github.com/gravitational/teleport/lib/tbot/tshwrap"
|
||||
)
|
||||
|
||||
func onProxyCommand(botConfig *config.BotConfig, cf *config.CLIConf) error {
|
||||
func onProxyCommand(
|
||||
ctx context.Context, botConfig *config.BotConfig, cf *config.CLIConf,
|
||||
) error {
|
||||
wrapper, err := tshwrap.New()
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
@@ -46,9 +49,6 @@ func onProxyCommand(botConfig *config.BotConfig, cf *config.CLIConf) error {
|
||||
}
|
||||
|
||||
identityPath := filepath.Join(destination.Path, config.IdentityFilePath)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
// TODO(timothyb89): We could consider supporting a --cluster passthrough
|
||||
// here as in `tbot db ...`.
|
||||
@@ -75,6 +75,9 @@ func onProxyCommand(botConfig *config.BotConfig, cf *config.CLIConf) error {
|
||||
destination.Path, "kubeconfig-proxied.yaml",
|
||||
)
|
||||
}
|
||||
if slices.Contains(cf.RemainingArgs, "ssh") {
|
||||
log.WarnContext(ctx, "`tbot proxy ssh` is deprecated and will stop working in v17. See https://goteleport.com/docs/machine-id/reference/v16-upgrade-guide/")
|
||||
}
|
||||
|
||||
return trace.Wrap(wrapper.Exec(env, args...), "executing `tsh proxy`")
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ import (
|
||||
// `tsh proxy ssh` which results in much less memory and cpu consumption. This will
|
||||
// eventually supersede `tbot proxy ssh` as it becomes more feature rich and supports
|
||||
// all the edge cases.
|
||||
func onSSHProxyCommand(botConfig *config.BotConfig, cf *config.CLIConf) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func onSSHProxyCommand(ctx context.Context, cf *config.CLIConf) error {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
if cf.Port == "" {
|
||||
@@ -39,7 +39,9 @@ func onSSHProxyCommand(botConfig *config.BotConfig, cf *config.CLIConf) error {
|
||||
}
|
||||
|
||||
proxySSHConfig := tbot.ProxySSHConfig{
|
||||
BotConfig: botConfig,
|
||||
DestinationPath: cf.DestinationDir,
|
||||
Insecure: cf.Insecure,
|
||||
FIPS: cf.FIPS,
|
||||
ProxyServer: cf.ProxyServer,
|
||||
Cluster: cf.Cluster,
|
||||
User: cf.User,
|
||||
|
||||
Reference in New Issue
Block a user