io.Copy Error for closing tunnel
eleijonmarck opened this issue · 6 comments
I am experiencing a bit of error logs from the tunnel implementation.
// Start the server in the background. You will need to wait a
// small amount of time for it to bind to the localhost port
// before you can start sending connections.
go tunnel.Start()
defer func() {
time.Sleep(5 * time.Second)
tunnel.Close()
}()
Even if I wait 5 seconds for the tunnel to get any io.Copys out of the way (and I am not creating anything or so) I still get an error of io.Copy
2020/09/22 16:44:33.035361 accepted connection
2020/09/22 16:44:33.035380 listening for new connections...
2020/09/22 16:44:33.076055 connected to 11.11.11.11:22 (1 of 2)
2020/09/22 16:44:33.078677 connected to 22.22.22.22:22 (2 of 2)
2020/09/22 16:44:39.385185 close signal received, closing...
2020/09/22 16:44:39.385195 closing the netConn (1 of 2)
2020/09/22 16:44:39.385304 io.Copy error: read tcp 127.0.0.1:2000->127.0.0.1:40330: use of closed network connection
2020/09/22 16:44:39.385311 closing the netConn (2 of 2)
2020/09/22 16:44:39.385416 closing the serverConn (1 of 1)
2020/09/22 16:44:39.386128 tunnel closed
This is probably the interval workings of the tunnel implementation and closing before closing io.Copy implementations.
Can you provide the complete example?
@elliotchance
I am unable to setup a tunnel locally. Cannot give you a complete working example.
But maybe you could help out in creating a complete example setup w. docker and stuff as I am unable to create a tunnel and sftp locally.
here is the example I can give you. That should be working
https://github.com/eleijonmarck/go-ssh-tunnel
go code specifically:
package main
import (
"io/ioutil"
"log"
"os"
"time"
"github.com/elliotchance/sshtunnel"
"github.com/pkg/sftp"
"golang.org/x/crypto/ssh"
)
func main() {
client, err := startTunnel()
if err != nil {
log.Printf("errrrrorrr %s", err)
}
sftpclient, err := sftp.NewClient(client)
if err != nil {
log.Printf("unable to create sftp client with error: %s", err)
}
defer sftpclient.Close()
// check it's there
fi, err := sftpclient.Lstat("toSEB")
if err != nil {
log.Printf("seb upload test failed to upload a file w. error: %s", err)
}
log.Printf("fi %v", fi)
}
func startTunnel() (*ssh.Client, error) {
portForwarded := "2000"
sftpHostUser := "root"
// Connection settings
sftpHostServer := "localhost:2222"
sftpRemoteServer := "localhost:3333"
tunnel := sshtunnel.NewSSHTunnel(
// User and host of tunnel server, it will default to port 22
// if not specified.
sftpHostUser+"@"+sftpHostServer,
// authentication
// auth, // 1. private key
ssh.Password("root"), // 1. private key
// The destination host and port of the actual server.
sftpRemoteServer,
// The local port you want to bind the remote port to.
// Specifying "0" will lead to a random port.
portForwarded,
)
// You can provide a logger for debugging, or remove this line to
// make it silent.
tunnel.Log = log.New(os.Stdout, "", log.Ldate|log.Lmicroseconds)
// Start the server in the background. You will need to wait a
// small amount of time for it to bind to the localhost port
// before you can start sending connections.
go tunnel.Start()
// known io.Copy error:
// even if we wait 5 seconds we still get logs of io.Copy error use of closed connection
defer func() {
time.Sleep(5 * time.Second)
tunnel.Close()
}()
time.Sleep(100 * time.Millisecond)
log.Printf("started tunnel")
log.Printf("tunnel %+v", tunnel)
sftpHostUser = "docker"
keyPath := "./ssh_host_payout_staging_rsa_key"
buff, err := ioutil.ReadFile(keyPath)
if err != nil {
log.Printf("unable to read the privatye key %s, w. error: %s", keyPath, err)
}
signer, err := ssh.ParsePrivateKey(buff)
if err != nil {
log.Printf("unable to parse key")
}
auth := ssh.PublicKeys(signer)
config := &ssh.ClientConfig{
User: sftpHostUser,
Auth: []ssh.AuthMethod{
auth,
},
Timeout: 3 * time.Second,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
hostNetwork := "localhost"
log.Printf("dialing %s:%s", hostNetwork, portForwarded)
return ssh.Dial("tcp", hostNetwork+":"+portForwarded, config)
}
I suspect it's because ou're closing the sshtunnel in a defer from startTunnel. You will need to close the client in a defer in the parent scope:
func main() {
client, tunnel, err := startTunnel()
if err != nil {
log.Printf("errrrrorrr %s", err)
}
defer tunnel.Close()
// ...
}
func startTunnel() (*ssh.Client, *sshtunnel.SSHTunnel, error) {
go tunnel.Start()
client, err := ssh.Dial("tcp", hostNetwork+":"+portForwarded, config)
if err != nil {
return nil, nil, err
}
return client, tunnel, nil
}
okay that was maybe not representative of the functino I am calling:
func downloadFiles(s *service) error {
portForwarded := "3333"
tunnel, err := getTunnel(portForwarded)
if err != nil {
return fmt.Errorf("unable to setup tunnel for files: %w", err)
}
// Start the server in the background. You will need to wait a
// small amount of time for it to bind to the localhost port
// before you can start sending connections.
go tunnel.Start()
defer tunnel.Close()
time.Sleep(100 * time.Millisecond)
log.Print("tunnel connected")
// connect to sftp
sshClient, err := connectSFTP(portForwarded)
if err != nil {
return fmt.Errorf("error happened while connecting to SFTP: %w", err)
}
err = lookForFile(s, sshClient)
if err != nil {
return fmt.Errorf("error happened while downloading files: %w", err)
}
return nil
}
// ideally we would download these files first and then parse for the statuses of the files
func lookForFile(s *service, sshClient *ssh.Client) error {
client, err := sftp.NewClient(sshClient)
if err != nil {
return fmt.Errorf("unable to create sftp client with error: %w", err)
}
defer client.Close()
}
I am only using the tunnel inside this scope
Sorry, I'm not sure what going on. Perhaps there is something that is closing the connection on the other end?
@elliotchance I have updated the example with the lookForFile function that I am using.
But it is only closing the sftp client that is using the sshClient that is connected to the tunnel.
Maybe I have to reverse the order in which I am closing the connections.
now the defers are in this order when executing
client.Close()
sshClient.Close()
tunnel.Close()