mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-18 02:48:40 +00:00
ssh/tailssh: add support for agent forwarding.
Updates #3802 Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
parent
6e86bbcb06
commit
98b45ef12c
@ -186,6 +186,10 @@ func (ss *sshSession) launchProcess(ctx context.Context) error {
|
|||||||
|
|
||||||
ss.cmd = cmd
|
ss.cmd = cmd
|
||||||
|
|
||||||
|
if ss.agentListener != nil {
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("SSH_AUTH_SOCK=%s", ss.agentListener.Addr()))
|
||||||
|
}
|
||||||
|
|
||||||
ptyReq, winCh, isPty := ss.Pty()
|
ptyReq, winCh, isPty := ss.Pty()
|
||||||
if !isPty {
|
if !isPty {
|
||||||
ss.logf("starting non-pty command: %+v", cmd.Args)
|
ss.logf("starting non-pty command: %+v", cmd.Args)
|
||||||
|
@ -20,6 +20,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -257,11 +259,12 @@ type sshSession struct {
|
|||||||
sharedID string // ID that's shared with control
|
sharedID string // ID that's shared with control
|
||||||
logf logger.Logf
|
logf logger.Logf
|
||||||
|
|
||||||
ctx *sshContext // implements context.Context
|
ctx *sshContext // implements context.Context
|
||||||
srv *server
|
srv *server
|
||||||
connInfo *sshConnInfo
|
connInfo *sshConnInfo
|
||||||
action *tailcfg.SSHAction
|
action *tailcfg.SSHAction
|
||||||
localUser *user.User
|
localUser *user.User
|
||||||
|
agentListener net.Listener // non-nil if agent-forwarding requested+allowed
|
||||||
|
|
||||||
// initialized by launchProcess:
|
// initialized by launchProcess:
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
@ -385,6 +388,47 @@ func (srv *server) endSession(ss *sshSession) {
|
|||||||
|
|
||||||
var errSessionDone = errors.New("session is done")
|
var errSessionDone = errors.New("session is done")
|
||||||
|
|
||||||
|
// handleSSHAgentForwarding starts a Unix socket listener and in the background
|
||||||
|
// forwards agent connections between the listenr and the ssh.Session.
|
||||||
|
// On success, it assigns ss.agentListener.
|
||||||
|
func (ss *sshSession) handleSSHAgentForwarding(s ssh.Session, lu *user.User) error {
|
||||||
|
if !ssh.AgentRequested(ss) || !ss.action.AllowAgentForwarding {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ss.logf("ssh: agent forwarding requested")
|
||||||
|
ln, err := ssh.NewAgentListener()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil && ln != nil {
|
||||||
|
ln.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
uid, err := strconv.ParseUint(lu.Uid, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
gid, err := strconv.ParseUint(lu.Gid, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
socket := ln.Addr().String()
|
||||||
|
dir := filepath.Dir(socket)
|
||||||
|
// Make sure the socket is accessible by the user.
|
||||||
|
if err := os.Chown(socket, int(uid), int(gid)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.Chmod(dir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go ssh.ForwardAgentConnections(ln, s)
|
||||||
|
ss.agentListener = ln
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// run is the entrypoint for a newly accepted SSH session.
|
// run is the entrypoint for a newly accepted SSH session.
|
||||||
//
|
//
|
||||||
// When ctx is done, the session is forcefully terminated. If its Err
|
// When ctx is done, the session is forcefully terminated. If its Err
|
||||||
@ -424,6 +468,12 @@ func (ss *sshSession) run() {
|
|||||||
// See https://github.com/tailscale/tailscale/issues/4146
|
// See https://github.com/tailscale/tailscale/issues/4146
|
||||||
ss.DisablePTYEmulation()
|
ss.DisablePTYEmulation()
|
||||||
|
|
||||||
|
if err := ss.handleSSHAgentForwarding(ss, lu); err != nil {
|
||||||
|
logf("ssh: 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()
|
||||||
|
}
|
||||||
err := ss.launchProcess(ss.ctx)
|
err := ss.launchProcess(ss.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logf("start failed: %v", err.Error())
|
logf("start failed: %v", err.Error())
|
||||||
|
@ -1615,6 +1615,10 @@ type SSHAction struct {
|
|||||||
// before being forcefully terminated.
|
// before being forcefully terminated.
|
||||||
SesssionDuration time.Duration `json:"sessionDuration,omitempty"`
|
SesssionDuration time.Duration `json:"sessionDuration,omitempty"`
|
||||||
|
|
||||||
|
// AllowAgentForwarding, if true, allows accepted connections to forward
|
||||||
|
// the ssh agent if requested.
|
||||||
|
AllowAgentForwarding bool `json:"allowAgentForwarding,omitempty"`
|
||||||
|
|
||||||
// HoldAndDelegate, if non-empty, is a URL that serves an
|
// HoldAndDelegate, if non-empty, is a URL that serves an
|
||||||
// outcome verdict. The connection will be accepted and will
|
// outcome verdict. The connection will be accepted and will
|
||||||
// block until the provided long-polling URL serves a new
|
// block until the provided long-polling URL serves a new
|
||||||
|
Loading…
x
Reference in New Issue
Block a user