ssh/tailssh,tailcfg: add connID to ssheventnotifyrequest and castheader

This change adds a ConnectionID field to both SSHEventNotifyRequest and
CastHeader that identifies the ID of a connection to the SSH server.

Updates tailscale/corp#9967

Signed-off-by: Charlotte Brandhorst-Satzkorn <charlotte@tailscale.com>
pull/8072/head
Charlotte Brandhorst-Satzkorn 2 years ago committed by Charlotte Brandhorst-Satzkorn
parent 68307c1411
commit 29ded8f9f9

@ -1410,6 +1410,11 @@ type CastHeader struct {
// LocalUser is the effective username on the server. // LocalUser is the effective username on the server.
LocalUser string `json:"localUser"` LocalUser string `json:"localUser"`
// ConnectionID uniquely identifies a connection made to the SSH server.
// It may be shared across multiple sessions over the same connection in
// case of SSH multiplexing.
ConnectionID string `json:"connectionID"`
} }
// sessionRecordingClient returns an http.Client that uses srv.lb.Dialer() to // sessionRecordingClient returns an http.Client that uses srv.lb.Dialer() to
@ -1458,7 +1463,6 @@ func (ss *sshSession) connectToRecorder(ctx context.Context, recs []netip.AddrPo
if len(recs) == 0 { if len(recs) == 0 {
return nil, nil, nil, errors.New("no recorders configured") return nil, nil, nil, errors.New("no recorders configured")
} }
var attempts []*tailcfg.SSHRecordingAttempt
// We use a special context for dialing the recorder, so that we can // We use a special context for dialing the recorder, so that we can
// limit the time we spend dialing to 30 seconds and still have an // limit the time we spend dialing to 30 seconds and still have an
// unbounded context for the upload. // unbounded context for the upload.
@ -1466,12 +1470,11 @@ func (ss *sshSession) connectToRecorder(ctx context.Context, recs []netip.AddrPo
defer dialCancel() defer dialCancel()
hc, err := ss.sessionRecordingClient(dialCtx) hc, err := ss.sessionRecordingClient(dialCtx)
if err != nil { if err != nil {
attempts = append(attempts, &tailcfg.SSHRecordingAttempt{ return nil, nil, nil, err
FailureMessage: err.Error(),
})
return nil, attempts, nil, err
} }
var errs []error var errs []error
var attempts []*tailcfg.SSHRecordingAttempt
for _, ap := range recs { for _, ap := range recs {
attempt := &tailcfg.SSHRecordingAttempt{ attempt := &tailcfg.SSHRecordingAttempt{
Recorder: ap, Recorder: ap,
@ -1557,11 +1560,9 @@ func (ss *sshSession) openFileForRecording(now time.Time) (_ io.WriteCloser, err
func (ss *sshSession) startNewRecording() (_ *recording, err error) { func (ss *sshSession) startNewRecording() (_ *recording, err error) {
// We store the node key as soon as possible when creating // We store the node key as soon as possible when creating
// a new recording incase of FUS. // a new recording incase of FUS.
var nodeKey key.NodePublic nodeKey := ss.conn.srv.lb.NodeKey()
if nk := ss.conn.srv.lb.NodeKey(); nk.IsZero() { if nodeKey.IsZero() {
return nil, errors.New("ssh server is unavailable: no node key") return nil, errors.New("ssh server is unavailable: no node key")
} else {
nodeKey = nk
} }
recorders, onFailure := ss.recorders() recorders, onFailure := ss.recorders()
@ -1664,6 +1665,7 @@ func (ss *sshSession) startNewRecording() (_ *recording, err error) {
LocalUser: ss.conn.localUser.Username, LocalUser: ss.conn.localUser.Username,
SrcNode: strings.TrimSuffix(ss.conn.info.node.Name, "."), SrcNode: strings.TrimSuffix(ss.conn.info.node.Name, "."),
SrcNodeID: ss.conn.info.node.StableID, SrcNodeID: ss.conn.info.node.StableID,
ConnectionID: ss.conn.connID,
} }
if !ss.conn.info.node.IsTagged() { if !ss.conn.info.node.IsTagged() {
ch.SrcNodeUser = ss.conn.info.uprof.LoginName ch.SrcNodeUser = ss.conn.info.uprof.LoginName
@ -1694,6 +1696,7 @@ func (ss *sshSession) startNewRecording() (_ *recording, err error) {
func (ss *sshSession) notifyControl(ctx context.Context, nodeKey key.NodePublic, notifyType tailcfg.SSHEventType, attempts []*tailcfg.SSHRecordingAttempt, url string) { func (ss *sshSession) notifyControl(ctx context.Context, nodeKey key.NodePublic, notifyType tailcfg.SSHEventType, attempts []*tailcfg.SSHRecordingAttempt, url string) {
re := tailcfg.SSHEventNotifyRequest{ re := tailcfg.SSHEventNotifyRequest{
EventType: notifyType, EventType: notifyType,
ConnectionID: ss.conn.connID,
CapVersion: tailcfg.CurrentCapabilityVersion, CapVersion: tailcfg.CurrentCapabilityVersion,
NodeKey: nodeKey, NodeKey: nodeKey,
SrcNode: ss.conn.info.node.ID, SrcNode: ss.conn.info.node.ID,

@ -2080,6 +2080,12 @@ type SSHRecorderFailureAction struct {
type SSHEventNotifyRequest struct { type SSHEventNotifyRequest struct {
// EventType is the type of notify request being sent. // EventType is the type of notify request being sent.
EventType SSHEventType EventType SSHEventType
// ConnectionID uniquely identifies a connection made to the SSH server.
// It may be shared across multiple sessions over the same connection in
// case a single connection creates multiple sessions.
ConnectionID string
// CapVersion is the client's current CapabilityVersion. // CapVersion is the client's current CapabilityVersion.
CapVersion CapabilityVersion CapVersion CapabilityVersion

Loading…
Cancel
Save