Skip to content

Commit 7948ab5

Browse files
committed
Implement port process inspection
1 parent 76d3a24 commit 7948ab5

File tree

5 files changed

+58
-10
lines changed

5 files changed

+58
-10
lines changed

agent/agent_test.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,13 @@ func TestAgent_Stats_Magic(t *testing.T) {
192192
require.NoError(t, err)
193193
})
194194

195-
// This test name being "Jetbrains" is required to be a certain string.
196-
// It must match the regex check in the agent for Jetbrains.
197-
t.Run("Jetbrains", func(t *testing.T) {
195+
// This test name must contain the string checked for by the agent, since it
196+
// looks for this string in the process name.
197+
t.Run("TracksIdea.vendor.name=JetBrains", func(t *testing.T) {
198198
t.Parallel()
199+
if runtime.GOOS != "linux" {
200+
t.Skip("JetBrains tracking is only supported on Linux")
201+
}
199202
ctx := testutil.Context(t, testutil.WaitLong)
200203

201204
rl, err := net.Listen("tcp", "127.0.0.1:0")

agent/agentssh/agentssh.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prom
113113
ChannelHandlers: map[string]ssh.ChannelHandler{
114114
"direct-tcpip": func(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx ssh.Context) {
115115
// wrapper is designed to find and track jetbrains gateway connections.
116-
wrapped := NewChannelAcceptWatcher(s.logger, newChan, &s.connCountJetBrains)
116+
wrapped := NewChannelAcceptWatcher(ctx, s.logger, newChan, &s.connCountJetBrains)
117117
ssh.DirectTCPIPHandler(srv, conn, wrapped, ctx)
118118
},
119119
"direct-streamlocal@openssh.com": directStreamLocalHandler,

agent/agentssh/jetbrainstrack.go

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package agentssh
22

33
import (
4+
"strings"
45
"sync"
56

67
"cdr.dev/slog"
8+
"github.com/gliderlabs/ssh"
79
"go.uber.org/atomic"
810
gossh "golang.org/x/crypto/ssh"
911
)
@@ -21,17 +23,31 @@ type ChannelAcceptWatcher struct {
2123
jetbrainsCounter *atomic.Int64
2224
}
2325

24-
func NewChannelAcceptWatcher(logger slog.Logger, newChannel gossh.NewChannel, counter *atomic.Int64) gossh.NewChannel {
26+
func NewChannelAcceptWatcher(ctx ssh.Context, logger slog.Logger, newChannel gossh.NewChannel, counter *atomic.Int64) gossh.NewChannel {
2527
d := localForwardChannelData{}
2628
if err := gossh.Unmarshal(newChannel.ExtraData(), &d); err != nil {
27-
// If the data fails to unmarshal, do nothing
29+
// If the data fails to unmarshal, do nothing.
2830
return newChannel
2931
}
3032

31-
//if !jetbrains {
32-
// If this isn't jetbrains, then we don't need to do anything special.
33-
//return newChannel
34-
//}
33+
// If we do get a port, we should be able to get the matching PID and from
34+
// there look up the name
35+
name, err := getListeningPortProcessName(d.DestPort)
36+
if err != nil {
37+
logger.Warn(ctx, "port inspection failed",
38+
slog.F("destination_port", d.DestPort),
39+
slog.Error(err))
40+
return newChannel
41+
}
42+
43+
// If this isn't JetBrains, then we don't need to do anything special. We
44+
// attempt to match on something that appears unique to JetBrains software.
45+
if strings.Contains(strings.ToLower(name), "idea.vendor.name=jetbrains") {
46+
return newChannel
47+
}
48+
49+
logger.Debug(ctx, "discovered forwarded JetBrains process",
50+
slog.F("destination_port", d.DestPort))
3551

3652
return &ChannelAcceptWatcher{
3753
NewChannel: newChannel,
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//go:build linux || (windows && amd64)
2+
3+
package agentssh
4+
5+
import (
6+
"github.com/cakturk/go-netstat/netstat"
7+
)
8+
9+
func getListeningPortProcessName(port uint32) (string, error) {
10+
tabs, err := netstat.TCPSocks(func(s *netstat.SockTabEntry) bool {
11+
return s.LocalAddr != nil && uint32(s.LocalAddr.Port) == port
12+
})
13+
if err != nil {
14+
return "", err
15+
}
16+
if len(tabs) == 0 {
17+
return "", nil
18+
}
19+
return tabs[0].Process.Name, nil
20+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//go:build !linux && !(windows && amd64)
2+
3+
package agentssh
4+
5+
func getListeningPortProcessCmdline(port uint32) (string, error) {
6+
// We are not worrying about other platforms at the moment because Gateway
7+
// only supports Linux anyway.
8+
return "", nil
9+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy