Golang SFTP client examples
Building a Golang SFTP client is common when automating network systems. The
github.com/pkg/sftp
package is a fine tuned package that can get the job done. It works with SSH version 2 and is compatible with OpenSSH and other SSH spec servers.
Client setup may differ depending on your authentication requirements. In each example you’ll notice the HostKeyCallback
to setup a FixedHostKey
references the target host public key; this is used to prevent spoofing. If your host public key isn’t already archived in the your home directory OpenSSH known_hosts file, you can run ssh-keyscan
to pull it off the target host.
Overview
Copy File Local to Remote
Here’s a simple example that copies a local file to a remote SFTP location.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
package main import ( "github.com/pkg/sftp" "golang.org/x/crypto/ssh" "io" "log" "os" "strings" "bufio" "fmt" "path/filepath" ) func main() { user := "" pass := "" remote := "" port := ":22" // get host public key hostKey := getHostKey(remote) config := &ssh.ClientConfig{ User: user, Auth: []ssh.AuthMethod{ ssh.Password(pass), }, // HostKeyCallback: ssh.InsecureIgnoreHostKey(), HostKeyCallback: ssh.FixedHostKey(hostKey), } // connect conn, err := ssh.Dial("tcp", remote+port, config) if err != nil { log.Fatal(err) } defer conn.Close() // create new SFTP client client, err := sftp.NewClient(conn) if err != nil { log.Fatal(err) } defer client.Close() // create destination file dstFile, err := client.Create("./file.txt") if err != nil { log.Fatal(err) } defer dstFile.Close() // create source file srcFile, err := os.Open("./file.txt") if err != nil { log.Fatal(err) } // copy source file to destination file bytes, err := io.Copy(dstFile, srcFile) if err != nil { log.Fatal(err) } fmt.Printf("%d bytes copied\n", bytes) } func getHostKey(host string) ssh.PublicKey { // parse OpenSSH known_hosts file // ssh or use ssh-keyscan to get initial key file, err := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts")) if err != nil { log.Fatal(err) } defer file.Close() scanner := bufio.NewScanner(file) var hostKey ssh.PublicKey for scanner.Scan() { fields := strings.Split(scanner.Text(), " ") if len(fields) != 3 { continue } if strings.Contains(fields[0], host) { var err error hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes()) if err != nil { log.Fatalf("error parsing %q: %v", fields[2], err) } break } } if hostKey == nil { log.Fatalf("no hostkey found for %s", host) } return hostKey } |
Copy File Remote to Local
Here’s a simple example that copies a remote file from an SFTP local to a local location.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
package main import ( "github.com/pkg/sftp" "golang.org/x/crypto/ssh" "io" "log" "os" "strings" "bufio" "fmt" "path/filepath" ) func main() { user := "" pass := "" remote := "" port := ":22" // get host public key hostKey := getHostKey(remote) config := &ssh.ClientConfig{ User: user, Auth: []ssh.AuthMethod{ ssh.Password(pass), }, // HostKeyCallback: ssh.InsecureIgnoreHostKey(), HostKeyCallback: ssh.FixedHostKey(hostKey), } // connect conn, err := ssh.Dial("tcp", remote+port, config) if err != nil { log.Fatal(err) } defer conn.Close() // create new SFTP client client, err := sftp.NewClient(conn) if err != nil { log.Fatal(err) } defer client.Close() // create destination file dstFile, err := os.Create("./file.txt") if err != nil { log.Fatal(err) } defer dstFile.Close() // open source file srcFile, err := client.Open("./file.txt") if err != nil { log.Fatal(err) } // copy source file to destination file bytes, err := io.Copy(dstFile, srcFile) if err != nil { log.Fatal(err) } fmt.Printf("%d bytes copied\n", bytes) // flush in-memory copy err = dstFile.Sync() if err != nil { log.Fatal(err) } } func getHostKey(host string) ssh.PublicKey { // parse OpenSSH known_hosts file // ssh or use ssh-keyscan to get initial key file, err := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts")) if err != nil { log.Fatal(err) } defer file.Close() scanner := bufio.NewScanner(file) var hostKey ssh.PublicKey for scanner.Scan() { fields := strings.Split(scanner.Text(), " ") if len(fields) != 3 { continue } if strings.Contains(fields[0], host) { var err error hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes()) if err != nil { log.Fatalf("error parsing %q: %v", fields[2], err) } break } } if hostKey == nil { log.Fatalf("no hostkey found for %s", host) } return hostKey } |
See also:
Golang DNS Lookup
Golang IP Address Manipulation
Golang Regular Expression Match
Golang SSH Client: Multiple Commands