I am trying to make SSH connections using golang.org/x/crypto/ssh
and I am kinda surprised that I can't seem to find out how to timeout the NewSession
function (I actually don't seen any way to timeout anything). When I try to connect to a server that is having issues, this just hangs for a very long time. I have written something to use select
with a time.After
but it just feels like a hack. Something I haven't tried yet is to keep the underlying net.Conn
in my struct and just keep doing Conn.SetDeadline()
calls. Haven't tried this yet because I don't know if the crypto/ssh library overrides this or anything like that.
Anyone have a good way to timeout dead servers with this library? Or does anyone know of a better library?
The user will require to increase the SSH connection timeout to solve this problem; It can be done in two ways. One way is to set the keep-alive options in the server configuration file, and another way is to set the keep-alive option in the client configuration file.
This error message comes from the SSH client. The error indicates that the server didn't respond to the client and the client program gave up (timed out). The following are common causes for this error: The security group or network ACL doesn't allow access.
Some systems use a default as low as five seconds, and some go as high as two hours; the average is typically around three to five minutes. Your SSH connection, if it has not been modified to change the timeout on either the server or client end, will use this timeout.
Incorrect IP address or hostname – The error may be due to a simple typo. Before you check anything else, make sure you're trying to connect to the correct server. Incorrect SSH port number – By default, servers listen for SSH connections on port 22.
One way to handle this transparently with the ssh package, is to create a connection with an idle timeout via a custom net.Conn
which sets deadlines for you. However, this will cause the background Reads on a connection to timeout, so we need to use ssh keepalives to keep the connection open. Depending on your use case, simply using ssh keepalives as an alert for a dead connection may suffice.
// 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)
}
You can then use net.DialTimeout
or a net.Dialer
to get the connection, wrap it in your Conn
with timeouts, and pass it into 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("[email protected]", true, nil)
if err != nil {
return
}
}
}()
return client, nil
}
Set the timeout on the 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)
When you call ssh.Dial
, the timeout will be passed to net.DialTimeout
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With