- discovery: ping 探测存活 + ARP 解析 MAC,包级 Devices map,串行扫描 - stress: 脚本生成模式(BuildScript/ParseResults),ToolSet 工具检测,包级 Results map - report: 包级 Printed map,SaveAndPrintDeviceReport 保存+打印 - sshclient: 包级 RunCommand - workflow: 仅持有 config,Start() 固定循环,直接调用各包方法 - config: 移除 ScanConfig.Concurrency(串行扫描无需并发数)
93 lines
2.1 KiB
Go
93 lines
2.1 KiB
Go
package sshclient
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
// Config SSH 连接配置(不含连接状态)
|
|
type Config struct {
|
|
User string
|
|
Password string
|
|
KeyFile string
|
|
Port int
|
|
Timeout time.Duration
|
|
}
|
|
|
|
// NewConfig 创建配置
|
|
func NewConfig(user, password, keyFile string, port int, timeout time.Duration) *Config {
|
|
return &Config{
|
|
User: user,
|
|
Password: password,
|
|
KeyFile: keyFile,
|
|
Port: port,
|
|
Timeout: timeout,
|
|
}
|
|
}
|
|
|
|
// Connect 建立 SSH 连接,由调用方负责 Close
|
|
func Connect(ip string, cfg *Config) (*ssh.Client, error) {
|
|
sshCfg, err := cfg.buildSSHConfig()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
addr := net.JoinHostPort(ip, fmt.Sprintf("%d", cfg.Port))
|
|
client, err := ssh.Dial("tcp", addr, sshCfg)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("SSH 连接 %s 失败: %w", addr, err)
|
|
}
|
|
return client, nil
|
|
}
|
|
|
|
// RunCommand 在已有 SSH 连接上执行命令,返回输出(实现 stress.Executor 接口)
|
|
func RunCommand(client *ssh.Client, command string) (string, error) {
|
|
session, err := client.NewSession()
|
|
if err != nil {
|
|
return "", fmt.Errorf("创建会话失败: %w", err)
|
|
}
|
|
defer session.Close()
|
|
|
|
out, err := session.CombinedOutput(command)
|
|
if err != nil {
|
|
return string(out), fmt.Errorf("命令执行失败: %w", err)
|
|
}
|
|
return string(out), nil
|
|
}
|
|
|
|
// buildSSHConfig 构建认证配置
|
|
func (c *Config) buildSSHConfig() (*ssh.ClientConfig, error) {
|
|
var authMethods []ssh.AuthMethod
|
|
|
|
if c.Password != "" {
|
|
authMethods = append(authMethods, ssh.Password(c.Password))
|
|
}
|
|
|
|
if c.KeyFile != "" {
|
|
key, err := os.ReadFile(c.KeyFile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("读取密钥文件失败: %w", err)
|
|
}
|
|
signer, err := ssh.ParsePrivateKey(key)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("解析私钥失败: %w", err)
|
|
}
|
|
authMethods = append(authMethods, ssh.PublicKeys(signer))
|
|
}
|
|
|
|
if len(authMethods) == 0 {
|
|
return nil, fmt.Errorf("未提供认证方式(密码或密钥)")
|
|
}
|
|
|
|
return &ssh.ClientConfig{
|
|
User: c.User,
|
|
Auth: authMethods,
|
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
|
Timeout: c.Timeout,
|
|
}, nil
|
|
}
|