diff --git a/k8s-operator/sessionrecording/hijacker.go b/k8s-operator/sessionrecording/hijacker.go index 1287a0a0c..f8ef951d4 100644 --- a/k8s-operator/sessionrecording/hijacker.go +++ b/k8s-operator/sessionrecording/hijacker.go @@ -15,6 +15,7 @@ "io" "net" "net/http" + "net/http/httptrace" "net/netip" "strings" @@ -137,8 +138,15 @@ func (h *Hijacker) setUpRecording(ctx context.Context, conn net.Conn) (net.Conn, errChan <-chan error ) h.log.Infof("kubectl exec session will be recorded, recorders: %v, fail open policy: %t", h.addrs, h.failOpen) - // TODO (irbekrm): send client a message that session will be recorded. - wc, _, errChan, err = h.connectToRecorder(ctx, h.addrs, h.ts.Dial) + qp := h.req.URL.Query() + container := strings.Join(qp[containerKey], "") + var recorderAddr net.Addr + trace := &httptrace.ClientTrace{ + GotConn: func(info httptrace.GotConnInfo) { + recorderAddr = info.Conn.RemoteAddr() + }, + } + wc, _, errChan, err = h.connectToRecorder(httptrace.WithClientTrace(ctx, trace), h.addrs, h.ts.Dial) if err != nil { msg := fmt.Sprintf("error connecting to session recorders: %v", err) if h.failOpen { @@ -151,13 +159,12 @@ func (h *Hijacker) setUpRecording(ctx context.Context, conn net.Conn) (net.Conn, return nil, multierr.New(errors.New(msg), err) } return nil, errors.New(msg) + } else { + h.log.Infof("exec session to container %q in Pod %q namespace %q will be recorded, the recording will be sent to a tsrecorder instance at %q", container, h.pod, h.ns, recorderAddr) } - // TODO (irbekrm): log which recorder - h.log.Info("successfully connected to a session recorder") cl := tstime.DefaultClock{} rec := tsrecorder.New(wc, cl, cl.Now(), h.failOpen, h.log) - qp := h.req.URL.Query() tty := strings.Join(qp[ttyKey], "") hasTerm := (tty == "true") // session has terminal attached ch := sessionrecording.CastHeader{ @@ -169,7 +176,7 @@ func (h *Hijacker) setUpRecording(ctx context.Context, conn net.Conn) (net.Conn, Kubernetes: &sessionrecording.Kubernetes{ PodName: h.pod, Namespace: h.ns, - Container: strings.Join(qp[containerKey], " "), + Container: container, }, } if !h.who.Node.IsTagged() { diff --git a/k8s-operator/sessionrecording/hijacker_test.go b/k8s-operator/sessionrecording/hijacker_test.go index 5c19d3a1d..440d9c942 100644 --- a/k8s-operator/sessionrecording/hijacker_test.go +++ b/k8s-operator/sessionrecording/hijacker_test.go @@ -78,7 +78,10 @@ func Test_Hijacker(t *testing.T) { tc := &fakes.TestConn{} ch := make(chan error) h := &Hijacker{ - connectToRecorder: func(context.Context, []netip.AddrPort, func(context.Context, string, string) (net.Conn, error)) (wc io.WriteCloser, rec []*tailcfg.SSHRecordingAttempt, _ <-chan error, err error) { + connectToRecorder: func(context.Context, + []netip.AddrPort, + func(context.Context, string, string) (net.Conn, error), + ) (wc io.WriteCloser, rec []*tailcfg.SSHRecordingAttempt, _ <-chan error, err error) { if tt.failRecorderConnect { err = errors.New("test") } diff --git a/sessionrecording/connect.go b/sessionrecording/connect.go index 12c5c8c01..db966ba2c 100644 --- a/sessionrecording/connect.go +++ b/sessionrecording/connect.go @@ -105,7 +105,7 @@ func ConnectToRecorder(ctx context.Context, recs []netip.AddrPort, dial func(con } attempt.FailureMessage = err.Error() errs = append(errs, err) - continue + continue // try the next recorder } return pw, attempts, errChan, nil }