ssh/tailssh: add support for sftp

Updates #3802

Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
Maisem Ali
2022-04-21 10:11:16 -07:00
committed by Maisem Ali
parent 53588f632d
commit 695f8a1d7e
3 changed files with 103 additions and 45 deletions

View File

@@ -221,10 +221,12 @@ func (c *conn) ServerConfig(ctx ssh.Context) *gossh.ServerConfig {
func (srv *server) newConn() (*conn, error) {
c := &conn{srv: srv, now: srv.now()}
c.Server = &ssh.Server{
Version: "Tailscale",
Handler: c.handleConnPostSSHAuth,
RequestHandlers: map[string]ssh.RequestHandler{},
SubsystemHandlers: map[string]ssh.SubsystemHandler{},
Version: "Tailscale",
Handler: c.handleConnPostSSHAuth,
RequestHandlers: map[string]ssh.RequestHandler{},
SubsystemHandlers: map[string]ssh.SubsystemHandler{
"sftp": c.handleConnPostSSHAuth,
},
// Note: the direct-tcpip channel handler and LocalPortForwardingCallback
// only adds support for forwarding ports from the local machine.
@@ -475,7 +477,7 @@ func (srv *server) fetchPublicKeysURL(url string) ([]string, error) {
// handleConnPostSSHAuth runs an SSH session after the SSH-level authentication,
// but not necessarily before all the Tailscale-level extra verification has
// completed.
// completed. It also handles SFTP requests.
func (c *conn) handleConnPostSSHAuth(s ssh.Session) {
sshUser := s.User()
action, err := c.resolveTerminalAction(s)
@@ -491,6 +493,15 @@ func (c *conn) handleConnPostSSHAuth(s ssh.Session) {
return
}
// Do this check after auth, but before starting the session.
switch s.Subsystem() {
case "sftp", "":
default:
fmt.Fprintf(s.Stderr(), "Unsupported subsystem %q \r\n", s.Subsystem())
s.Exit(1)
return
}
ss := c.newSSHSession(s, action)
ss.logf("handling new SSH connection from %v (%v) to ssh-user %q", c.info.uprof.LoginName, c.info.src.IP(), sshUser)
ss.logf("access granted to %v as ssh-user %q", c.info.uprof.LoginName, sshUser)
@@ -813,27 +824,29 @@ func (ss *sshSession) run() {
// See https://github.com/tailscale/tailscale/issues/4146
ss.DisablePTYEmulation()
if err := ss.handleSSHAgentForwarding(ss, lu); err != nil {
ss.logf("agent forwarding failed: %v", err)
} else if ss.agentListener != nil {
// TODO(maisem/bradfitz): add a way to close all session resources
defer ss.agentListener.Close()
}
var rec *recording // or nil if disabled
if ss.shouldRecord() {
var err error
rec, err = ss.startNewRecording()
if err != nil {
fmt.Fprintf(ss, "can't start new recording\r\n")
ss.logf("startNewRecording: %v", err)
ss.Exit(1)
return
if ss.Subsystem() != "sftp" {
if err := ss.handleSSHAgentForwarding(ss, lu); err != nil {
ss.logf("agent forwarding failed: %v", err)
} else if ss.agentListener != nil {
// TODO(maisem/bradfitz): add a way to close all session resources
defer ss.agentListener.Close()
}
if ss.shouldRecord() {
var err error
rec, err = ss.startNewRecording()
if err != nil {
fmt.Fprintf(ss, "can't start new recording\r\n")
ss.logf("startNewRecording: %v", err)
ss.Exit(1)
return
}
defer rec.Close()
}
defer rec.Close()
}
err := ss.launchProcess(ss.ctx)
err := ss.launchProcess()
if err != nil {
logf("start failed: %v", err.Error())
ss.Exit(1)