SFTP (Secure File Transfer Protocol) est un protocole de transfert de fichiers qui exploite un ensemble d'utilitaires qui fournissent un accès sécurisé à un ordinateur distant pour fournir des communications sécurisées. Il s'appuie sur SSH.
Contenu associé
- Comment travailler avec le client SFTP sous Linux – 10 commandes sftp
- Comment configurer un serveur SFTP sur le serveur Debian 11
- Télécharger des fichiers à partir du serveur SFTP à l'aide d'un script python
- Répertorier, charger et télécharger des fichiers depuis un serveur SFTP à l'aide de golang
- Comment configurer un serveur SFTP sur le serveur OpenSUSE Leap 15.3
- Comment installer et configurer le serveur sftp dans Ubuntu 20.04
- Comment configurer un serveur SFTP sur le serveur CentOS 8/RHEL 8
Prérequis
A suivre :
- Assurez-vous que golang est installé localement.
- Assurez-vous d'avoir accès à un serveur SFTP – Nom d'utilisateur et mot de passe
- Assurez-vous de bien connaître le terminal
Table des matières
- Création de la structure du répertoire et initialisation du module golang
- Création du script :importations
- Création du script :Fonction pour lister les fichiers
- Création du script :fonction de téléchargement de fichiers
- Création du script :code complet
- Construire et tester le code
1. Création de la structure du répertoire et initialisation du module golang
Nous avons besoin d'un répertoire qui aura notre contenu. Créez-le avec cette commande :
mkdir gosftp
Basculez vers le répertoire et initialisez un module golang :
➜ cd gosftp
➜ go mod init gosftp
go: creating new go.mod: module gosftp
go: to add module requirements and sums:
go mod tidy
Cela créera un fichier go.mod
avec ce contenu :
module gosftp
go 1.17
2. Création du script :Importations
Ne créons pas le script. Créez un fichier nommé main.go
et ajoutez ces importations :
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"github.com/pkg/sftp"
)
2. Création du script :Connexion au serveur
Maintenant que nous avons les importations, utilisons ce code pour initialiser la connexion au serveur sftp :
// Create a url
rawurl := fmt.Sprintf("sftp://%v:%[email protected]%v", sftpUser, sftpPass, sftpHost)
// Parse the URL
parsedUrl, err := url.Parse(rawurl)
if err != nil {
log.Fatalf("Failed to parse SFTP To Go URL: %s", err)
}
// Get user name and pass
user := parsedUrl.User.Username()
pass, _ := parsedUrl.User.Password()
// Parse Host and Port
host := parsedUrl.Host
// Get hostkey
hostKey := getHostKey(host)
log.Printf("Connecting to %s ...\n", host)
var auths []ssh.AuthMethod
// Try to use $SSH_AUTH_SOCK which contains the path of the unix file socket that the sshd agent uses
// for communication with other processes.
if aconn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
auths = append(auths, ssh.PublicKeysCallback(agent.NewClient(aconn).Signers))
}
// Use password authentication if provided
if pass != "" {
auths = append(auths, ssh.Password(pass))
}
// Initialize client configuration
config := ssh.ClientConfig{
User: user,
Auth: auths,
// Auth: []ssh.AuthMethod{
// ssh.KeyboardInteractive(SshInteractive),
// },
// Uncomment to ignore host key check
// HostKeyCallback: ssh.InsecureIgnoreHostKey(),
HostKeyCallback: ssh.FixedHostKey(hostKey),
// HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
// return nil
// },
Timeout: 30 * time.Second,
}
addr := fmt.Sprintf("%s:%s", host, sftpPort)
// Connect to server
conn, err := ssh.Dial("tcp", addr, &config)
if err != nil {
log.Fatalf("Failed to connec to host [%s]: %v", addr, err)
}
defer conn.Close()
// Create new SFTP client
sc, err := sftp.NewClient(conn)
if err != nil {
log.Fatalf("Unable to start SFTP subsystem: %v", err)
}
defer sc.Close()
3. Création du script :Fonction pour lister les fichiers
Créons maintenant une fonction pour lister les fichiers. Nous utilisons la connexion au serveur sftp pour lire le contenu du répertoire distant puis l'ajouter à une liste de structures à renvoyer.
func listFiles(sc sftp.Client, remoteDir string) (theFiles []remoteFiles, err error) {
files, err := sc.ReadDir(remoteDir)
if err != nil {
return theFiles, fmt.Errorf("Unable to list remote dir: %v", err)
}
for _, f := range files {
var name, modTime, size string
name = f.Name()
modTime = f.ModTime().Format("2006-01-02 15:04:05")
size = fmt.Sprintf("%12d", f.Size())
if f.IsDir() {
name = name + "/"
modTime = ""
size = "PRE"
}
theFiles = append(theFiles, remoteFiles{
Name: name,
Size: size,
ModTime: modTime,
})
}
return theFiles, nil
}
4. Création du script :Fonction pour télécharger des fichiers
Créons une fonction pour télécharger des fichiers sur le serveur sftp. Nous allons utiliser la connexion pour ouvrir un fichier, puis créer des répertoires distants de manière récursive puis copier les données du fichier local
// Upload file to sftp server
func uploadFile(sc sftp.Client, localFile, remoteFile string) (err error) {
log.Printf("Uploading [%s] to [%s] ...", localFile, remoteFile)
srcFile, err := os.Open(localFile)
if err != nil {
return fmt.Errorf("Unable to open local file: %v", err)
}
defer srcFile.Close()
// Make remote directories recursion
parent := filepath.Dir(remoteFile)
path := string(filepath.Separator)
dirs := strings.Split(parent, path)
for _, dir := range dirs {
path = filepath.Join(path, dir)
sc.Mkdir(path)
}
// Note: SFTP Go doesn't support O_RDWR mode
dstFile, err := sc.OpenFile(remoteFile, (os.O_WRONLY | os.O_CREATE | os.O_TRUNC))
if err != nil {
return fmt.Errorf("Unable to open remote file: %v", err)
}
defer dstFile.Close()
bytes, err := io.Copy(dstFile, srcFile)
if err != nil {
return fmt.Errorf("Unable to upload local file: %v", err)
}
log.Printf("%d bytes copied", bytes)
return nil
}
5. Création du script :Fonction de téléchargement de fichiers
Cette fonction télécharge un fichier depuis le serveur distant en fonction du chemin distant. Dans cette fonction, nous créons un fichier dans le répertoire tmp puis copions les données du chemin du fichier distant.
// Download file from sftp server
func downloadFile(sc sftp.Client, remoteFile, localFile string) (err error) {
localPath := "/tmp/" + localFile
log.Printf("Downloading [%s] to [%s] ...", remoteFile, localFile)
// Note: SFTP To Go doesn't support O_RDWR mode
srcFile, err := sc.OpenFile(remoteFile, (os.O_RDONLY))
if err != nil {
return fmt.Errorf("unable to open remote file: %v", err)
}
defer srcFile.Close()
dstFile, err := os.Create(localPath)
if err != nil {
return fmt.Errorf("unable to open local file: %v", err)
}
defer dstFile.Close()
bytes, err := io.Copy(dstFile, srcFile)
if err != nil {
return fmt.Errorf("unable to download remote file: %v", err)
}
log.Printf("%d bytes copied to %v", bytes, localPath)
return nil
}
5. Création du script :code complet
Voici le code complet du script pour effectuer des opérations avec SFTP en utilisant Golang :
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"github.com/pkg/sftp"
)
const (
sftpUser = "citizix"
sftpPass = "Str0ngP4ss"
sftpHost = "10.2.11.10"
sftpPort = "22"
)
func main() {
// Create a url
rawurl := fmt.Sprintf("sftp://%v:%[email protected]%v", sftpUser, sftpPass, sftpHost)
// Parse the URL
parsedUrl, err := url.Parse(rawurl)
if err != nil {
log.Fatalf("Failed to parse SFTP To Go URL: %s", err)
}
// Get user name and pass
user := parsedUrl.User.Username()
pass, _ := parsedUrl.User.Password()
// Parse Host and Port
host := parsedUrl.Host
// Get hostkey
hostKey := getHostKey(host)
log.Printf("Connecting to %s ...\n", host)
var auths []ssh.AuthMethod
// Try to use $SSH_AUTH_SOCK which contains the path of the unix file socket that the sshd agent uses
// for communication with other processes.
if aconn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
auths = append(auths, ssh.PublicKeysCallback(agent.NewClient(aconn).Signers))
}
// Use password authentication if provided
if pass != "" {
auths = append(auths, ssh.Password(pass))
}
// Initialize client configuration
config := ssh.ClientConfig{
User: user,
Auth: auths,
// Auth: []ssh.AuthMethod{
// ssh.KeyboardInteractive(SshInteractive),
// },
// Uncomment to ignore host key check
// HostKeyCallback: ssh.InsecureIgnoreHostKey(),
HostKeyCallback: ssh.FixedHostKey(hostKey),
// HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
// return nil
// },
Timeout: 30 * time.Second,
}
addr := fmt.Sprintf("%s:%s", host, sftpPort)
// Connect to server
conn, err := ssh.Dial("tcp", addr, &config)
if err != nil {
log.Fatalf("Failed to connec to host [%s]: %v", addr, err)
}
defer conn.Close()
// Create new SFTP client
sc, err := sftp.NewClient(conn)
if err != nil {
log.Fatalf("Unable to start SFTP subsystem: %v", err)
}
defer sc.Close()
// List files in the root directory .
theFiles, err := listFiles(*sc, ".")
if err != nil {
log.Fatalf("failed to list files in .: %v", err)
}
log.Printf("Found Files in . Files")
// Output each file name and size in bytes
log.Printf("%19s %12s %s", "MOD TIME", "SIZE", "NAME")
for _, theFile := range theFiles {
log.Printf("%19s %12s %s", theFile.ModTime, theFile.Size, theFile.Name)
}
// Upload local file
err = uploadFile(*sc, "/Users/etowett/Desktop/data.csv", "./citizix/data.csv")
if err != nil {
log.Fatalf("could not upload file: %v", err)
}
// Download remote file to local file.
err = downloadFile(*sc, "citizix/data.csv", "data.csv")
if err != nil {
log.Fatalf("Could not download file data.csv; %v", err)
}
return
}
func SshInteractive(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
// Hack, check https://stackoverflow.com/questions/47102080/ssh-in-go-unable-to-authenticate-attempted-methods-none-no-supported-method
answers = make([]string, len(questions))
// The second parameter is unused
for n, _ := range questions {
answers[n] = sftpPass
}
return answers, nil
}
type remoteFiles struct {
Name string
Size string
ModTime string
}
func listFiles(sc sftp.Client, remoteDir string) (theFiles []remoteFiles, err error) {
files, err := sc.ReadDir(remoteDir)
if err != nil {
return theFiles, fmt.Errorf("Unable to list remote dir: %v", err)
}
for _, f := range files {
var name, modTime, size string
name = f.Name()
modTime = f.ModTime().Format("2006-01-02 15:04:05")
size = fmt.Sprintf("%12d", f.Size())
if f.IsDir() {
name = name + "/"
modTime = ""
size = "PRE"
}
theFiles = append(theFiles, remoteFiles{
Name: name,
Size: size,
ModTime: modTime,
})
}
return theFiles, nil
}
// Upload file to sftp server
func uploadFile(sc sftp.Client, localFile, remoteFile string) (err error) {
log.Printf("Uploading [%s] to [%s] ...", localFile, remoteFile)
srcFile, err := os.Open(localFile)
if err != nil {
return fmt.Errorf("Unable to open local file: %v", err)
}
defer srcFile.Close()
// Make remote directories recursion
parent := filepath.Dir(remoteFile)
path := string(filepath.Separator)
dirs := strings.Split(parent, path)
for _, dir := range dirs {
path = filepath.Join(path, dir)
sc.Mkdir(path)
}
// Note: SFTP Go doesn't support O_RDWR mode
dstFile, err := sc.OpenFile(remoteFile, (os.O_WRONLY | os.O_CREATE | os.O_TRUNC))
if err != nil {
return fmt.Errorf("Unable to open remote file: %v", err)
}
defer dstFile.Close()
bytes, err := io.Copy(dstFile, srcFile)
if err != nil {
return fmt.Errorf("Unable to upload local file: %v", err)
}
log.Printf("%d bytes copied", bytes)
return nil
}
// Download file from sftp server
func downloadFile(sc sftp.Client, remoteFile, localFile string) (err error) {
log.Printf("Downloading [%s] to [%s] ...\n", remoteFile, localFile)
// Note: SFTP To Go doesn't support O_RDWR mode
srcFile, err := sc.OpenFile(remoteFile, (os.O_RDONLY))
if err != nil {
return fmt.Errorf("unable to open remote file: %v", err)
}
defer srcFile.Close()
dstFile, err := os.Create(localFile)
if err != nil {
return fmt.Errorf("unable to open local file: %v", err)
}
defer dstFile.Close()
bytes, err := io.Copy(dstFile, srcFile)
if err != nil {
return fmt.Errorf("unable to download remote file: %v", err)
}
log.Printf("%d bytes copied to %v", bytes, dstFile)
return nil
}
// Get host key from local known hosts
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 {
fmt.Fprintf(os.Stderr, "Unable to read known_hosts file: %v\n", err)
os.Exit(1)
}
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 {
fmt.Fprintf(os.Stderr, "Error parsing %q: %v\n", fields[2], err)
os.Exit(1)
}
break
}
}
if hostKey == nil {
fmt.Fprintf(os.Stderr, "No hostkey found for %s", host)
os.Exit(1)
}
return hostKey
}
6. Construire et tester le code
Maintenant que nous avons notre code, construisons-le et testons-le.
Assurez-vous d'abord que toutes les dépendances sont téléchargées à l'aide de go mod tidy
commande.
Voici ma sortie :
❯ go mod tidy
go: finding module for package golang.org/x/crypto/ssh
go: finding module for package github.com/pkg/sftp
go: found github.com/pkg/sftp in github.com/pkg/sftp v1.13.4
go: found golang.org/x/crypto/ssh in golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
Construisons ensuite notre code dans un gosftp
binaire dans le répertoire courant :
➜ go build -o gosftp
Exécutez maintenant le script. Voici ma sortie :
➜ ./gosftp
2021/10/08 13:10:36 Connecting to 10.2.11.10 ...
2021/10/08 13:10:43 Found Files in . Files
2021/10/08 13:10:43 MOD TIME SIZE NAME
2021/10/08 13:10:43 PRE etowett/
2021/10/08 13:10:43 PRE citizix/
2021/10/08 13:10:43 PRE PAYMENTDATA/
2021/10/08 13:10:43 Uploading [/Users/etowett/Desktop/data.csv] to [./citizix/data.csv] ...
2021/10/08 13:10:44 24 bytes copied
2021/10/08 13:10:45 Downloading [citizix/data.csv] to [data.csv] ...
2021/10/08 13:10:46 24 bytes copied to &{0xc000090a20}
Conclusion
Dans cet article, nous avons réussi à créer un script pour répertorier les fichiers sur un serveur sftp distant, télécharger des fichiers et télécharger des fichiers.