SSH 连接超时

SSH Connection Timeout

我正在尝试使用 golang.org/x/crypto/ssh 建立 SSH 连接,我有点惊讶我似乎无法找到如何使 NewSession 函数超时(我实际上没有看到任何任何超时的方法)。当我尝试连接到有问题的服务器时,它会挂起很长时间。我已经写了一些东西来使用 selecttime.After 但感觉就像一个 hack。我还没有尝试过的是将底层 net.Conn 保留在我的结构中并继续进行 Conn.SetDeadline() 调用。还没有尝试过这个,因为我不知道 crypto/ssh 库是否会覆盖这个或类似的东西。

有没有人有使用这个库使死服务器超时的好方法?或者有人知道更好的图书馆吗?

使用 ssh 包透明地处理这个问题的一种方法是通过为您设置截止日期的自定义 net.Conn 创建一个空闲超时连接。但是,这会导致后台读取连接超时,因此我们需要使用 ssh keepalives 来保持连接打开。根据您的用例,只需使用 ssh keepalives 作为死连接警报就足够了。

// Conn wraps a net.Conn, and sets a deadline for every read
// and write operation.
type Conn struct {
    net.Conn
    ReadTimeout  time.Duration
    WriteTimeout time.Duration
}

func (c *Conn) Read(b []byte) (int, error) {
    err := c.Conn.SetReadDeadline(time.Now().Add(c.ReadTimeout))
    if err != nil {
        return 0, err
    }
    return c.Conn.Read(b)
}

func (c *Conn) Write(b []byte) (int, error) {
    err := c.Conn.SetWriteDeadline(time.Now().Add(c.WriteTimeout))
    if err != nil {
        return 0, err
    }
    return c.Conn.Write(b)
}

然后您可以使用 net.DialTimeoutnet.Dialer 获取连接,将其包装在您的 Conn 中并设置超时,然后将其传递给 ssh.NewClientConn

func SSHDialTimeout(network, addr string, config *ssh.ClientConfig, timeout time.Duration) (*ssh.Client, error) {
    conn, err := net.DialTimeout(network, addr, timeout)
    if err != nil {
        return nil, err
    }

    timeoutConn := &Conn{conn, timeout, timeout}
    c, chans, reqs, err := ssh.NewClientConn(timeoutConn, addr, config)
    if err != nil {
        return nil, err
    }
    client := ssh.NewClient(c, chans, reqs)

    // this sends keepalive packets every 2 seconds
    // there's no useful response from these, so we can just abort if there's an error
    go func() {
        t := time.NewTicker(2 * time.Second)
        defer t.Stop()
        for range t.C {
            _, _, err := client.Conn.SendRequest("keepalive@golang.org", true, nil)
            if err != nil {
                return
            }
        }
    }()
    return client, nil
}

ssh.ClientConfig 上设置超时。

cfg := ssh.ClientConfig{
    User: "root",
    Auth: []ssh.AuthMethod{
        ssh.PublicKeys(signer),
    },
    HostKeyCallback: ssh.FixedHostKey(hostKey),
    Timeout:         15 * time.Second, // max time to establish connection
}

ssh.Dial("tcp", ip+":22", &cfg)

当你打电话给 ssh.Dial, the timeout will be passed to net.DialTimeout.