fix: 修复 SSH 扫描 goroutine 泄漏

ssh.NewClientConn 不接受 context,context 取消后底层 TCP 连接未关闭,
导致 readLoop goroutine 永久阻塞在 conn.Read 上。大规模扫描时泄漏数万
goroutine。

- doSSHAuth 新增 goroutine 监听 context 取消并关闭底层连接
- TestSingleCredential 移除 5 秒超时放弃逻辑,改为持续等待清理
This commit is contained in:
ZacharyZcR
2026-06-12 03:59:02 +08:00
parent 4198c1abc8
commit b94e8bc4ca
2 changed files with 23 additions and 11 deletions
+3 -9
View File
@@ -79,19 +79,13 @@ func TestSingleCredential(ctx context.Context, cred Credential, authFn AuthFunc)
case result := <-resultChan: case result := <-resultChan:
return result return result
case <-ctx.Done(): case <-ctx.Done():
// context 被取消, authFn goroutine 可能还阻塞在第三方库 IO 上 // context 被取消,等待 authFn goroutine 返回并清理连接
// 限时等待:超过 5 秒直接放弃,避免 goroutine 无限泄漏 // 各插件的 authFn 应在 context 取消时关闭底层连接使 goroutine 快速退出
go func() { go func() {
timer := time.NewTimer(5 * time.Second) result := <-resultChan
defer timer.Stop()
select {
case result := <-resultChan:
if result != nil && result.Conn != nil { if result != nil && result.Conn != nil {
_ = result.Conn.Close() _ = result.Conn.Close()
} }
case <-timer.C:
// 第三方库不响应取消,放弃等待
}
}() }()
return &AuthResult{ return &AuthResult{
Success: false, Success: false,
+18
View File
@@ -122,10 +122,28 @@ func (p *SSHPlugin) doSSHAuth(ctx context.Context, info *common.HostInfo, cred C
} }
} }
// 监听 context 取消,强制关闭底层连接以中断 SSH 握手和 readLoop
done := make(chan struct{})
defer close(done)
go func() {
select {
case <-ctx.Done():
_ = conn.Close()
case <-done:
}
}()
// 在TCP连接上创建SSH客户端 // 在TCP连接上创建SSH客户端
sshConn, chans, reqs, err := ssh.NewClientConn(conn, target, sshConfig) sshConn, chans, reqs, err := ssh.NewClientConn(conn, target, sshConfig)
if err != nil { if err != nil {
_ = conn.Close() _ = conn.Close()
if ctx.Err() != nil {
return &AuthResult{
Success: false,
ErrorType: ErrorTypeNetwork,
Error: ctx.Err(),
}
}
return &AuthResult{ return &AuthResult{
Success: false, Success: false,
ErrorType: classifySSHErrorType(err), ErrorType: classifySSHErrorType(err),