Skip to content

Commit c9a226f

Browse files
committed
feat: implement jetbrains agentssh tracking
Based on tcp forwarding instead of ssh connections
1 parent 329aa45 commit c9a226f

File tree

2 files changed

+68
-1
lines changed

2 files changed

+68
-1
lines changed

agent/agentssh/agentssh.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,11 @@ func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prom
111111

112112
srv := &ssh.Server{
113113
ChannelHandlers: map[string]ssh.ChannelHandler{
114-
"direct-tcpip": ssh.DirectTCPIPHandler,
114+
"direct-tcpip": func(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx ssh.Context) {
115+
// wrapper is designed to find and track jetbrains gateway connections.
116+
wrapped := NewChannelAcceptWatcher(s.logger, newChan, &s.connCountJetBrains)
117+
ssh.DirectTCPIPHandler(srv, conn, wrapped, ctx)
118+
},
115119
"direct-streamlocal@openssh.com": directStreamLocalHandler,
116120
"session": ssh.DefaultSessionHandler,
117121
},

agent/agentssh/jetbrainstrack.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package agentssh
2+
3+
import (
4+
"cdr.dev/slog"
5+
"go.uber.org/atomic"
6+
gossh "golang.org/x/crypto/ssh"
7+
)
8+
9+
type localForwardChannelData struct {
10+
DestAddr string
11+
DestPort uint32
12+
13+
OriginAddr string
14+
OriginPort uint32
15+
}
16+
17+
type ChannelAcceptWatcher struct {
18+
gossh.NewChannel
19+
jetbrainsCounter *atomic.Int64
20+
}
21+
22+
func NewChannelAcceptWatcher(logger slog.Logger, newChannel gossh.NewChannel, counter *atomic.Int64) gossh.NewChannel {
23+
d := localForwardChannelData{}
24+
if err := gossh.Unmarshal(newChannel.ExtraData(), &d); err != nil {
25+
// If the data fails to unmarshal, do nothing
26+
return newChannel
27+
}
28+
29+
//if !jetbrains {
30+
// If this isn't jetbrains, then we don't need to do anything special.
31+
//return newChannel
32+
//}
33+
34+
return &ChannelAcceptWatcher{
35+
NewChannel: newChannel,
36+
jetbrainsCounter: counter,
37+
}
38+
}
39+
40+
func (w *ChannelAcceptWatcher) Accept() (gossh.Channel, <-chan *gossh.Request, error) {
41+
c, r, err := w.NewChannel.Accept()
42+
if err != nil {
43+
return c, r, err
44+
}
45+
w.jetbrainsCounter.Add(1)
46+
47+
return &ChannelOnClose{
48+
Channel: c,
49+
done: func() {
50+
w.jetbrainsCounter.Add(-1)
51+
},
52+
}, r, err
53+
}
54+
55+
type ChannelOnClose struct {
56+
gossh.Channel
57+
done func()
58+
}
59+
60+
func (c *ChannelOnClose) Close() error {
61+
c.done()
62+
return c.Channel.Close()
63+
}

0 commit comments

Comments
 (0)