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
14 changes: 13 additions & 1 deletion cmd/tailcat/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"strings"
"syscall"

"github.com/tailscale/tailcat"
"tailscale.com/types/logger"
)

Expand Down Expand Up @@ -63,14 +64,25 @@ func clientSSHMode(logf logger.Logf) {
"-o", "StrictHostKeyChecking no",
"-o", "UserKnownHostsFile /dev/null",
"-o", "LogLevel ERROR",
"-o", fmt.Sprintf("ProxyCommand=%s --key=%q %s %s", exe, *flagKey, connBlobStr, portOrIPPort),
"-o", "ProxyCommand=" + sshProxyCommand(exe, *flagKey, *flagDERPMapURL, connBlobStr, portOrIPPort),
sshDst,
}
argv = append(argv, cmdArgs...)
err = syscall.Exec(sshExe, argv, os.Environ())
log.Fatalf("failed to exec: %v", err)
}

// sshProxyCommand returns the command passed to OpenSSH to connect the SSH
// client to a tailcat server. The command is run by OpenSSH, so values that
// can contain shell-special characters must be quoted.
func sshProxyCommand(exe, keyName, derpMapURL, connBlob, portOrIPPort string) string {
cmd := fmt.Sprintf("%s --key=%q", exe, keyName)
if derpMapURL != tailcat.DefaultDERPMapURL {
cmd += fmt.Sprintf(" --derpmap-url=%q", derpMapURL)
}
return fmt.Sprintf("%s %s %s", cmd, connBlob, portOrIPPort)
}

// sshDestHost returns the hostname to give the system ssh client as the
// connection destination for a tailcat ConnBlob. It is a short, deterministic
// function of blob rather than blob itself.
Expand Down
24 changes: 24 additions & 0 deletions cmd/tailcat/ssh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,32 @@ package main
import (
"strings"
"testing"

"github.com/tailscale/tailcat"
)

func TestSSHProxyCommandDERPMap(t *testing.T) {
const (
exe = "/path/to/tailcat"
key = "client-default"
blob = "tc-short-blob"
port = "22"
url = "https://derp.example.com/derpmap.json"
)

got := sshProxyCommand(exe, key, url, blob, port)
want := exe + ` --key="client-default" --derpmap-url="https://derp.example.com/derpmap.json" tc-short-blob 22`
if got != want {
t.Errorf("sshProxyCommand with custom DERP map = %q; want %q", got, want)
}

got = sshProxyCommand(exe, key, tailcat.DefaultDERPMapURL, blob, port)
want = exe + ` --key="client-default" tc-short-blob 22`
if got != want {
t.Errorf("sshProxyCommand with default DERP map = %q; want %q", got, want)
}
}

func TestSSHDestHost(t *testing.T) {
// A realistic ConnBlob, taken from an existing test fixture elsewhere
// in this package.
Expand Down
Loading