Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3e8233e
[v18][vnet] feat: TCP dial to SSH targets
nklaassen May 31, 2025
19f5dce
[v18][vnet] feat: accept incoming SSH connections
nklaassen May 31, 2025
4517ec5
[v18][vnet] feat: forward SSH connections to target
nklaassen May 31, 2025
ab0fb8c
[v18][vnet] feat: write VNet SSH keys to TELEPORT_HOME
nklaassen Jun 1, 2025
8708011
[v18][vnet] feat: write OpenSSH-compatible config file for VNet SSH
nklaassen Jun 1, 2025
6d698e6
[v18][vnet] fix: support <hostname>.<leaf-cluster> for VNet SSH
nklaassen Jun 13, 2025
67e9561
[v18][vnet] feat: add "Connect with VNet" button to SSH servers
nklaassen Jun 13, 2025
72b8daa
Merge branch 'branch/v18' into nklaassen/v18/vnet-ssh
nklaassen Jun 13, 2025
76232a3
fix test in backport
nklaassen Jun 13, 2025
75d1c5a
[v18][vnet] feat: support VNet SSH when cluster name does not match p…
nklaassen Jun 17, 2025
faf4202
Merge branch 'branch/v18' into nklaassen/v18/vnet-ssh
nklaassen Jun 18, 2025
440d564
[v18][vnet] feat: add SSH configuration diagnostic
nklaassen Jun 18, 2025
f2c9e0a
[v18][vnet] feat: show SSH status in VNet slider
nklaassen Jun 19, 2025
1669b77
[v18][vnet] feat: support proxy recording mode with VNet SSH
nklaassen Jun 20, 2025
90401fa
[v18][vnet] feat: support diag checks on windows
nklaassen Jun 20, 2025
94caa69
[v18] fix: data race in vnet.TestSSH
nklaassen Jun 22, 2025
6830541
[v18][vnet] feat: mention SSH on VNet info page
nklaassen Jun 23, 2025
11bd108
[v18][vnet] feat: serve DNS on IPv4
nklaassen Jun 23, 2025
11e8bc2
[v18][vnet] fix: close proxied channel only after data and requests a…
nklaassen Jun 24, 2025
fffa153
[v18][vnet] feat: automatic SSH client configuration
nklaassen Jun 25, 2025
5a1abdb
VNet diag notification: Do not show button to open report if there's …
ravicious Jun 25, 2025
37c0655
[v18][vnet] feat: automatic SSH client configuration in Connect
nklaassen Jun 26, 2025
8821752
[v18][vnet] fix: avoid empty host matchers in generated SSH config
nklaassen Jun 26, 2025
cd30650
Merge branch 'branch/v18' into nklaassen/v18/vnet-ssh
nklaassen Jun 26, 2025
1150f4c
[v18][docs] VNet SSH
nklaassen Jul 4, 2025
79bd83d
Merge branch 'branch/v18' into nklaassen/v18/vnet-ssh
nklaassen Jul 8, 2025
d55c6b5
[v18][docs] add VNet warnings
nklaassen Jul 9, 2025
8f44b37
[v18][vnet] feat: SSH usage reporting
nklaassen Jul 18, 2025
9aaaeb8
Merge branch 'branch/v18' into nklaassen/v18/vnet-ssh
nklaassen Jul 21, 2025
5b913a4
[v18][vnet] fix: mask default IP route on windows
nklaassen Jul 21, 2025
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
25 changes: 16 additions & 9 deletions api/profile/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,19 +317,26 @@ func FullProfilePath(dir string) string {

// defaultProfilePath retrieves the default path of the TSH profile.
func defaultProfilePath() string {
// start with UserHomeDir, which is the fastest option as it
// relies only on environment variables and does not perform
// a user lookup (which can be very slow on large AD environments)
home, err := os.UserHomeDir()
if err == nil && home != "" {
return filepath.Join(home, profileDir)
home, ok := UserHomeDir()
if !ok {
home = os.TempDir()
}
return filepath.Join(home, profileDir)
}

home = os.TempDir()
// UserHomeDir returns the current user's home directory if it can be found.
func UserHomeDir() (string, bool) {
// Start with os.UserHomeDir, which is the fastest option as it relies only
// on environment variables and does not perform a user lookup (which can be
// very slow on large AD environments).
if home, err := os.UserHomeDir(); err == nil && home != "" {
return home, true
}
// Fall back to the user lookup.
if u, err := user.Current(); err == nil && u.HomeDir != "" {
home = u.HomeDir
return u.HomeDir, true
}
return filepath.Join(home, profileDir)
return "", false
}

// FromDir reads the user profile from a given directory. If dir is empty,
Expand Down
37 changes: 37 additions & 0 deletions api/utils/keypaths/keypaths.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ const (
profileFileExt = ".yaml"
// oracleWalletDirSuffix is the suffix of the oracle wallet database directory.
oracleWalletDirSuffix = "-wallet"
// VNetClientSSHKey is the file name of the SSH key used by third-party SSH
// clients to connect to VNet SSH.
VNetClientSSHKey = "id_vnet"
// VNetClientSSHKeyPub is the file name of the SSH public key matching
// VNetClientSSHKey.
VNetClientSSHKeyPub = VNetClientSSHKey + fileExtPub
// vnetKnownHosts is the file name of the known_hosts file trusted by
// third-party SSH clients connecting to VNet SSH.
vnetKnownHosts = "vnet_known_hosts"
// VNetSSHConfig is the file name of the generated OpenSSH-compatible config
// file to be used by third-party SSH clients connecting to VNet SSH.
VNetSSHConfig = "vnet_ssh_config"
)

// Here's the file layout of all these keypaths.
Expand All @@ -81,6 +93,10 @@ const (
// ├── one.example.com.yaml --> file containing profile details for proxy "one.example.com"
// ├── two.example.com.yaml --> file containing profile details for proxy "two.example.com"
// ├── known_hosts --> trusted certificate authorities (their keys) in a format similar to known_hosts
// ├── id_vnet --> SSH Private Key for third-party clients of VNet SSH
// ├── id_vnet.pub --> SSH Public Key for third-party clients of VNet SSH
// ├── vnet_known_hosts --> trusted certificate authorities (their keys) for third-party clients of VNet SSH
// ├── vnet_ssh_config --> OpenSSH-compatible config file for third-party clients of VNet SSH
// └── keys --> session keys directory
// ├── one.example.com --> Proxy hostname
// │ ├── certs.pem --> TLS CA certs for the Teleport CA
Expand Down Expand Up @@ -429,6 +445,27 @@ func IdentitySSHCertPath(path string) string {
return path + fileExtSSHCert
}

// VNetClientSSHKeyPath returns the path to the VNet client SSH private key.
func VNetClientSSHKeyPath(baseDir string) string {
return filepath.Join(baseDir, VNetClientSSHKey)
}

// VNetClientSSHKeyPubPath returns the path to the VNet client SSH public key.
func VNetClientSSHKeyPubPath(baseDir string) string {
return filepath.Join(baseDir, VNetClientSSHKeyPub)
}

// VNetKnownHostsPath returns the path to the VNet known_hosts file.
func VNetKnownHostsPath(baseDir string) string {
return filepath.Join(baseDir, vnetKnownHosts)
}

// VNetSSHConfigPath returns the path to VNet's generated OpenSSH-compatible
// config file.
func VNetSSHConfigPath(baseDir string) string {
return filepath.Join(baseDir, VNetSSHConfig)
}

// TrimKeyPathSuffix returns the given path with any key suffix/extension trimmed off.
func TrimKeyPathSuffix(path string) string {
return strings.TrimSuffix(path, fileExtTLSKey)
Expand Down
Binary file added docs/img/vnet/configure-ssh-clients.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/img/vnet/how-it-works.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/vnet/ssh-connect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/vnet/start-vnet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/pages/connect-your-client/teleport-connect.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ A new tab will open with a shell session on the chosen server.

Alternatively, you can look for the server in the search bar and press `Enter` to connect to it.

If you'd prefer to connect to SSH servers with a third-party SSH client or your
editor's Remote Development feature, read the [VNet guide](./vnet.mdx) to learn how.

## Opening a local terminal

To open a terminal with a local shell session, either select "Open new terminal" from the additional
Expand Down
101 changes: 80 additions & 21 deletions docs/pages/connect-your-client/vnet.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,31 @@ title: Using VNet
description: Using VNet
---

This guide explains how to use VNet to connect to TCP applications available through Teleport.
This guide explains how to use VNet to connect to TCP applications and SSH
servers available through Teleport.

## How it works

VNet automatically proxies connections from your computer to TCP apps available
through Teleport.
A program on your device can securely connect to internal applications protected
VNet automatically proxies connections from your computer to TCP apps and SSH
servers available through Teleport.
A program on your device can securely connect to resources protected
by Teleport without having to know about Teleport authentication details.
Underneath, VNet authenticates the connection with your Teleport credentials and
securely tunnels the TCP connection to your application.
securely tunnels the connection.
This is all done client-side – VNet sets up a local DNS name server that
intercepts DNS requests for your internal apps and responds with a virtual IP
address managed by VNet that will forward the connection to your application.
intercepts DNS requests for your Teleport resources and responds with a virtual IP
address managed by VNet that will handle the connection.

VNet's SSH support enables third-party SSH clients to connect to Teleport SSH
servers with minimal configuration required, while still offering Teleport
access controls and features like [Per-session MFA](../admin-guides/access-controls/guides/per-session-mfa.mdx)
and [Hardware Key Support](../admin-guides/access-controls/guides/hardware-key-support.mdx).

![Diagram showing VNet architecture](../../img/vnet/how-it-works.svg)

VNet delivers an experience like a VPN for your TCP applications through this local virtual network, while maintaining all of Teleport's identity verification and zero trust features that traditional VPNs cannot provide.
VNet delivers an experience like a VPN through this local virtual network,
while maintaining all of Teleport's identity verification and zero trust
features that traditional VPNs cannot provide.

VNet is available on macOS and Windows in Teleport Connect and tsh, with plans
for Linux support in a future version.
Expand Down Expand Up @@ -56,17 +64,21 @@ following mitigations for DNS rebinding attacks:
</TabItem>
</Tabs>

## Step 1/3. Start Teleport Connect
## Step 1/3. Start VNet

Open Teleport Connect and log in to the cluster. Find the TCP app you want to connect to. TCP apps
have `tcp://` as the protocol in their addresses.
Open Teleport Connect and log in to your cluster.
See [Using Teleport Connect](./teleport-connect.mdx) if you haven't used the
Teleport Connect app before.

![Resource list in Teleport Connect with a TCP hovered over](../../img/use-teleport/vnet-resources-list@2x.png)
Open the **connection list** in the top left and click the icon to start VNet.
Or, skip this step and VNet will start automatically when you click "Connect"
on a TCP app or "Connect with VNet" on an SSH server.

## Step 2/3. Start VNet
![VNet shown in connection list](../../img/vnet/start-vnet.png)

Click "Connect" next to the TCP app. This starts VNet if it's not already running. Alternatively,
you can start VNet through the connection list in the top left.
After VNet has been started once it will automatically start every time
Teleport Connect is opened, unless you stop VNet before closing Teleport
Connect.

<details>
<summary>First launch on macOS</summary>
Expand All @@ -78,15 +90,28 @@ tsh.app under "Allow in the Background".
![VNet starting up](../../img/use-teleport/vnet-starting@2x.png)
</details>

## Step 3/3. Connect
## Step 2/3. Connect to a TCP app

Find the TCP app you want to connect to.
TCP apps have `tcp://` as the protocol in their address.

Once VNet is running, you can connect to the application using the application client you would
![Resource list in Teleport Connect with a TCP app hovered over](../../img/use-teleport/vnet-resources-list@2x.png)

Click "Connect" next to the TCP app.
This will start VNet if it's not already running, and then copy the app's
address to your clipboard.
You can now connect to the application using the application client you would
normally use to connect to it.

```code
$ psql postgres://postgres@tcp-app.teleport.example.com/postgres
```

As long as VNet is running in the background, clicking "Connect" next to each
app is not necessary.
You can directly connect to all of your TCP apps without any actions in
Teleport Connect.

<Admonition type="note" title="Support for multiple ports">
Unless the application specifies [multiple
ports](../enroll-resources/application-access/guides/tcp.mdx#configuring-access-to-multiple-ports),
Expand All @@ -98,19 +123,52 @@ If [per-session MFA](../admin-guides/access-controls/guides/per-session-mfa.mdx)
first connection over each port triggers an MFA check.
</Admonition>

VNet is going to automatically start on the next Teleport Connect launch, unless you stop VNet
before closing Teleport Connect.
## Step 3/3. Connect to an SSH server

Find the SSH server you want to connect to, open the menu next to the "Connect"
dropdown, and click "Connect with VNet".
This will start VNet if it's not already running, and then copy the VNet
address for the server to your clipboard.

![SSH server in Teleport Connect with "Connect with VNet" menu open](../../img/vnet/ssh-connect.png)

There is a one-time configuration step required before SSH clients will be able
to connect to Teleport SSH servers through VNet.
When you click "Connect with VNet" on an SSH server, Teleport Connect will
automatically check if this configuration is present and walk you through it if
necessary.

![SSH client configuration modal in Teleport Connect](../../img/vnet/configure-ssh-clients.png)

Once the configuration step is complete, any OpenSSH-compatible client that
reads configuration options from `~/.ssh/config` should be able to connect to
Teleport SSH servers.
Try connecting with the standard `ssh` client or the Remote Development feature
in editors like Visual Studio Code or Zed.

```code
$ ssh <username>@<hostname>.<clustername>
```

As long as VNet is running in the background, clicking "Connect with VNet" next
to each SSH server is not necessary, you can directly connect to all of your
Teleport SSH servers without any actions in Teleport Connect.

## `tsh` support

VNet is available in `tsh` as well. Using it involves logging into the cluster and executing the
command `tsh vnet`.
VNet is also available in `tsh` without running Teleport Connect.
To use it, log in and then run `tsh vnet`.

```code
$ tsh login --proxy=teleport.example.com
$ tsh vnet
```

While `tsh` support is available, Teleport Connect is the preferred application
for running VNet.
Teleport Connect offers better visibility for MFA prompts and cluster logins, and
automatically runs diagnostics that are useful for troubleshooting.

## Troubleshooting

### Conflicting IPv4 ranges
Expand Down Expand Up @@ -295,3 +353,4 @@ Before version 18.0.0, VNet logs were saved in `C:\Program Files\Teleport Connec
- Read our VNet configuration [guide](../enroll-resources/application-access/guides/vnet.mdx)
to learn how to configure VNet access to your applications.
- Read [RFD 163](https://github.com/gravitational/teleport/blob/master/rfd/0163-vnet.md) to learn how VNet works on a technical level.
- Read [RFD 207](https://github.com/gravitational/teleport/blob/master/rfd/0207-vnet-ssh.md) to learn how VNet SSH access works.
Loading
Loading