Skip to content

Commit 8228251

Browse files
committed
feat: improve proxy SSH client management and configuration
- Add settings.local.json file to configure Claude permissions - Ensure proxy SSH client connections are closed on timeout, error, or after successful connection in Connect method - Add a test to verify proxy clients are properly cleaned up during multiple Connect attempts fix #88 Signed-off-by: appleboy <appleboy.tw@gmail.com>
1 parent 8cdf277 commit 8228251

File tree

3 files changed

+39
-0
lines changed

3 files changed

+39
-0
lines changed

.claude/settings.local.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"mcp__github__get_issue"
5+
],
6+
"deny": [],
7+
"ask": []
8+
}
9+
}

easyssh.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,18 +292,24 @@ func (ssh_conf *MakeConfig) Connect() (*ssh.Session, *ssh.Client, error) {
292292
conn = result.conn
293293
err = result.err
294294
case <-ctx.Done():
295+
proxyClient.Close()
295296
return nil, nil, fmt.Errorf("%w: %v", ErrProxyDialTimeout, ctx.Err())
296297
}
297298

298299
if err != nil {
300+
proxyClient.Close()
299301
return nil, nil, err
300302
}
301303

302304
ncc, chans, reqs, err := ssh.NewClientConn(conn, net.JoinHostPort(ssh_conf.Server, ssh_conf.Port), targetConfig)
303305
if err != nil {
306+
proxyClient.Close()
304307
return nil, nil, err
305308
}
306309

310+
// Close the proxy client after successfully establishing the target connection
311+
// The target connection (ncc) is now independent of the proxy client
312+
proxyClient.Close()
307313
client = ssh.NewClient(ncc, chans, reqs)
308314
} else {
309315
client, err = ssh.Dial(string(ssh_conf.Protocol), net.JoinHostPort(ssh_conf.Server, ssh_conf.Port), targetConfig)

easyssh_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,3 +666,27 @@ func TestProxyGoroutineLeak(t *testing.T) {
666666
"Goroutine leak detected: initial=%d, final=%d", initialGoroutines, finalGoroutines)
667667
}
668668

669+
// TestProxyClientCleanup tests that proxy clients are properly closed during multiple Connect calls
670+
func TestProxyClientCleanup(t *testing.T) {
671+
ssh := &MakeConfig{
672+
Server: "10.255.255.1", // Non-routable IP
673+
User: "testuser",
674+
Port: "22",
675+
KeyPath: "./tests/.ssh/id_rsa",
676+
Timeout: 500 * time.Millisecond, // Short timeout
677+
Proxy: DefaultConfig{
678+
User: "testuser",
679+
Server: "10.255.255.2", // Another non-routable IP for proxy
680+
Port: "22",
681+
KeyPath: "./tests/.ssh/id_rsa",
682+
Timeout: 500 * time.Millisecond,
683+
},
684+
}
685+
686+
// Test multiple connect attempts - they should all fail gracefully without leaking resources
687+
for i := 0; i < 3; i++ {
688+
_, _, err := ssh.Connect()
689+
// Should have error due to non-routable IP, but no resource leaks
690+
assert.NotNil(t, err, "Connect should fail with timeout/connection error")
691+
}
692+
}

0 commit comments

Comments
 (0)