Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 90 additions & 79 deletions docs/pages/includes/config-reference/ssh-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,90 +2,101 @@ ssh_service:
# Turns 'ssh' role on. Default is true
enabled: true

# IP and the port for SSH service to bind to.
listen_addr: 0.0.0.0:3022
# IP and the port for SSH service to bind to.
listen_addr: 0.0.0.0:3022

# The optional public address the SSH service. This is useful if
# administrators want to allow users to connect to nodes directly,
# bypassing a Teleport proxy.
public_addr: node.example.com:3022
# The optional public address the SSH service. This is useful if
# administrators want to allow users to connect to nodes directly,
# bypassing a Teleport proxy.
public_addr: node.example.com:3022

labels:
role: leader
type: postgres
labels:
role: leader
type: postgres

# List of the commands to periodically execute. Their output will be used
# as node labels.
commands:
# List of the commands to periodically execute. Their output will be used
# as node labels.
commands:
# this command will add a label 'arch=x86_64' to a node
- name: arch
command: ['/bin/uname', '-p']
period: 1h0m0s

# Enables reading ~/.tsh/environment on the server before creating a session.
# Disabled by default. Can be enabled here or via the `--permit-user-env` flag.
permit_user_env: false

# Disables automatic creation of host users on this SSH node.
# Set to false by default.
disable_create_host_user: true

# Enhanced Session Recording
enhanced_recording:
# Enable or disable enhanced auditing for this node. Default value:
# false.
enabled: false

# command_buffer_size is optional with a default value of 8 pages.
command_buffer_size: 8

# disk_buffer_size is optional with default value of 128 pages.
disk_buffer_size: 128

# network_buffer_size is optional with default value of 8 pages.
network_buffer_size: 8

# Controls where cgroupv2 hierarchy is mounted. Default value:
# /cgroup2.
cgroup_path: /cgroup2

# Optional: Controls the path inside cgroupv2 hierarchy where Teleport
# cgroups will be placed. Default value: /teleport
root_path: /teleport

# Configures the PAM integration.
pam:
# "no" by default
enabled: yes
# use /etc/pam.d/sshd configuration (the default)
service_name: "sshd"
# use the "auth" modules in the PAM config
# "false" by default
use_pam_auth: true

# Enables/disables TCP forwarding. Default is 'true'
port_forwarding: true

# When x11.enabled is set to yes, users with the "permit_x11_forwarding"
# role option will be able to request X11 forwarding sessions with
# "tsh ssh -X".
#
# X11 forwarding will only work if the server has the "xauth" binary
# installed and the Teleport Node can open Unix sockets.
# e.g. "$TEMP/.X11-unix/X[display_number]."
x11:
# no by default
enabled: yes
# display_offset can be used to specify the start of the range of X11
# displays the server will use when granting X11 forwarding sessions
# 10 by default
display_offset: 10
# max_display can be set to specify the end of the range of X11 displays
# to use when granting X11 forwarding sessions
# display_offset + 1000 by default
max_display: 1010

# Enables/disables remote file operations via SCP/SFTP for this Node. Default
# value: true
ssh_file_copy: true

# Enables reading ~/.tsh/environment on the server before creating a session.
# Disabled by default. Can be enabled here or via the `--permit-user-env` flag.
permit_user_env: false

# Disables automatic creation of host users on this SSH node.
# Set to false by default.
disable_create_host_user: true

# Enables listening on the configured listen_addr when connected
# to the cluster via a reverse tunnel. If no listen_addr is
# configured, the default address is used.
#
# This allows the service to be connectable by users with direct network access.
# All connections still require a valid user certificate to be presented and will
# not permit any additional access. This is intended to provide an optional connection
# path to reduce latency if the Proxy is not co-located with the user and service.
#
# Set to false by default.
force_listen: false

# Enhanced Session Recording
enhanced_recording:
# Enable or disable enhanced auditing for this node. Default value:
# false.
enabled: false

# command_buffer_size is optional with a default value of 8 pages.
command_buffer_size: 8

# disk_buffer_size is optional with default value of 128 pages.
disk_buffer_size: 128

# network_buffer_size is optional with default value of 8 pages.
network_buffer_size: 8

# Controls where cgroupv2 hierarchy is mounted. Default value:
# /cgroup2.
cgroup_path: /cgroup2

# Optional: Controls the path inside cgroupv2 hierarchy where Teleport
# cgroups will be placed. Default value: /teleport
root_path: /teleport

# Configures the PAM integration.
pam:
# "no" by default
enabled: yes
# use /etc/pam.d/sshd configuration (the default)
service_name: 'sshd'
# use the "auth" modules in the PAM config
# "false" by default
use_pam_auth: true

# Enables/disables TCP forwarding. Default is 'true'
port_forwarding: true

# When x11.enabled is set to yes, users with the "permit_x11_forwarding"
# role option will be able to request X11 forwarding sessions with
# "tsh ssh -X".
#
# X11 forwarding will only work if the server has the "xauth" binary
# installed and the Teleport Node can open Unix sockets.
# e.g. "$TEMP/.X11-unix/X[display_number]."
x11:
# no by default
enabled: yes
# display_offset can be used to specify the start of the range of X11
# displays the server will use when granting X11 forwarding sessions
# 10 by default
display_offset: 10
# max_display can be set to specify the end of the range of X11 displays
# to use when granting X11 forwarding sessions
# display_offset + 1000 by default
max_display: 1010

# Enables/disables remote file operations via SCP/SFTP for this Node. Default
# value: true
ssh_file_copy: true
138 changes: 138 additions & 0 deletions integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ func TestIntegrations(t *testing.T) {
t.Run("EscapeSequenceTriggers", suite.bind(testEscapeSequenceTriggers))
t.Run("ExecEvents", suite.bind(testExecEvents))
t.Run("ExternalClient", suite.bind(testExternalClient))
t.Run("ForceListenerInTunnelMode", suite.bind(testForceListenerInTunnelMode))
t.Run("HA", suite.bind(testHA))
t.Run("Interactive (Regular)", suite.bind(testInteractiveRegular))
t.Run("Interactive (Reverse Tunnel)", suite.bind(testInteractiveReverseTunnel))
Expand Down Expand Up @@ -9198,3 +9199,140 @@ func testNegotiatedALPNProtocols(t *testing.T, suite *integrationTestSuite) {
})
}
}

func testForceListenerInTunnelMode(t *testing.T, suite *integrationTestSuite) {
// InsecureDevMode needed for IoT node handshake
lib.SetInsecureDevMode(true)
defer lib.SetInsecureDevMode(false)
Comment on lines +9203 to +9206
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm always scared of global state manipulation in tests.

Suggested change
func testForceListenerInTunnelMode(t *testing.T, suite *integrationTestSuite) {
// InsecureDevMode needed for IoT node handshake
lib.SetInsecureDevMode(true)
defer lib.SetInsecureDevMode(false)
func testForceListenerInTunnelMode(t *testing.T, suite *integrationTestSuite) {
// InsecureDevMode needed for IoT node handshake
defer lib.SetInsecureDevMode(lib.IsInsecureDevMode())
lib.SetInsecureDevMode(true)
// fail the test if it's accidentally made parallel
t.SetEnv("_testForceListenerInTunnelMode_SetInsecureDevMode", "1")


// Create a Teleport instance with Auth/Proxy.
mainConfig := func() *servicecfg.Config {
tconf := suite.defaultServiceConfig()
tconf.Auth.Enabled = true

tconf.Proxy.Enabled = true
tconf.Proxy.DisableWebService = false
tconf.Proxy.DisableWebInterface = true

tconf.SSH.Enabled = false

return tconf
}
main := suite.NewTeleportWithConfig(t, nil, nil, mainConfig())

// Create a Teleport ssh instance.
nodeConfig := func(tunnel, forceListen bool) *servicecfg.Config {
tconf := suite.defaultServiceConfig()
tconf.Hostname = Host
tconf.SetToken("token")

if tunnel {
tconf.SetAuthServerAddress(utils.NetAddr{
AddrNetwork: "tcp",
Addr: main.Web,
})
} else {
tconf.SetAuthServerAddress(utils.NetAddr{
AddrNetwork: "tcp",
Addr: main.Auth,
})
}

tconf.Auth.Enabled = false

tconf.Proxy.Enabled = false

tconf.SSH.Enabled = true

if forceListen {
tconf.SSH.Addr = utils.NetAddr{
Addr: helpers.NewListenerOn(t, Host, service.ListenerNodeSSH, &tconf.FileDescriptors),
}
tconf.SSH.ForceListen = true
}

return tconf
}

forceListenNode, err := main.StartReverseTunnelNode(nodeConfig(true, true))
require.NoError(t, err)

tunnelOnlyNode, err := main.StartReverseTunnelNode(nodeConfig(true, false))
require.NoError(t, err)

directNode, err := main.StartNode(nodeConfig(false, true))
require.NoError(t, err)

forceListenDirectNode, err := main.StartNode(nodeConfig(false, true))
require.NoError(t, err)

require.NoError(t, main.WaitForNodeCount(context.Background(), helpers.Site, 4))

creds, err := helpers.GenerateUserCreds(helpers.UserCredsRequest{
Process: main.Process,
Username: suite.Me.Username,
})
require.NoError(t, err)

signer, err := creds.KeyRing.SSHSigner()
require.NoError(t, err)

t.Run("tunnel node", func(t *testing.T) {
t.Run("forced listen node", func(t *testing.T) {
clt, err := ssh.Dial("tcp", forceListenNode.Config.SSH.Addr.Addr, &ssh.ClientConfig{
User: suite.Me.Username,
Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: 15 * time.Second,
})
require.NoError(t, err)

ok, resp, err := clt.SendRequest(teleport.VersionRequest, true, nil)
require.NoError(t, err)
require.True(t, ok)
require.Equal(t, teleport.Version, string(resp))
})

t.Run("tunnel only node", func(t *testing.T) {
_, err := ssh.Dial("tcp", tunnelOnlyNode.Config.SSH.Addr.Addr, &ssh.ClientConfig{
User: suite.Me.Username,
Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: 15 * time.Second,
})
require.Error(t, err)
})
})

t.Run("direct node", func(t *testing.T) {
t.Run("forced listen node", func(t *testing.T) {
clt, err := ssh.Dial("tcp", forceListenDirectNode.Config.SSH.Addr.Addr, &ssh.ClientConfig{
User: suite.Me.Username,
Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: 15 * time.Second,
})
require.NoError(t, err)

ok, resp, err := clt.SendRequest(teleport.VersionRequest, true, nil)
require.NoError(t, err)
require.True(t, ok)
require.Equal(t, teleport.Version, string(resp))
})

t.Run("direct node", func(t *testing.T) {
clt, err := ssh.Dial("tcp", directNode.Config.SSH.Addr.Addr, &ssh.ClientConfig{
User: suite.Me.Username,
Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: 15 * time.Second,
})
require.NoError(t, err)

ok, resp, err := clt.SendRequest(teleport.VersionRequest, true, nil)
require.NoError(t, err)
require.True(t, ok)
require.Equal(t, teleport.Version, string(resp))
})
})
}
2 changes: 2 additions & 0 deletions lib/config/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -1454,6 +1454,8 @@ func applySSHConfig(fc *FileConfig, cfg *servicecfg.Config) (err error) {

cfg.SSH.AllowFileCopying = fc.SSH.SSHFileCopy()

cfg.SSH.ForceListen = fc.SSH.ForceListen

return nil
}

Expand Down
10 changes: 10 additions & 0 deletions lib/config/fileconf.go
Original file line number Diff line number Diff line change
Expand Up @@ -1453,6 +1453,16 @@ type SSH struct {
// DisableCreateHostUser disables automatic user provisioning on this
// SSH node.
DisableCreateHostUser bool `yaml:"disable_create_host_user,omitempty"`

// ForceListen enables listening on the configured ListenAddress
// when connected to the cluster via a reverse tunnel. If no ListenAddress is
// configured, the default address is used.
//
// This allows the service to be connectable by users with direct network access.
// All connections still require a valid user certificate to be presented and will
// not permit any additional access. This is intended to provide an optional connection
// path to reduce latency if the Proxy is not co-located with the user and service.
ForceListen bool `yaml:"force_listen,omitempty"`
}

// AllowTCPForwarding checks whether the config file allows TCP forwarding or not.
Expand Down
Loading
Loading