Skip to content

Instantly share code, notes, and snippets.

@jedy
Last active May 31, 2022 07:20
Show Gist options
  • Save jedy/3357393 to your computer and use it in GitHub Desktop.
Save jedy/3357393 to your computer and use it in GitHub Desktop.
an example of scp in golang
// https://blogs.oracle.com/janp/entry/how_the_scp_protocol_works
package main
import (
"fmt"
"golang.org/x/crypto/ssh"
)
const privateKey = `content of id_rsa`
func main() {
signer, _ := ssh.ParsePrivateKey([]byte(privateKey))
clientConfig := &ssh.ClientConfig{
User: "jedy",
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
}
client, err := ssh.Dial("tcp", "127.0.0.1:22", clientConfig)
if err != nil {
panic("Failed to dial: " + err.Error())
}
session, err := client.NewSession()
if err != nil {
panic("Failed to create session: " + err.Error())
}
defer session.Close()
go func() {
w, _ := session.StdinPipe()
defer w.Close()
content := "123456789\n"
fmt.Fprintln(w, "D0755", 0, "testdir") // mkdir
fmt.Fprintln(w, "C0644", len(content), "testfile1")
fmt.Fprint(w, content)
fmt.Fprint(w, "\x00") // transfer end with \x00
fmt.Fprintln(w, "C0644", len(content), "testfile2")
fmt.Fprint(w, content)
fmt.Fprint(w, "\x00")
}()
if err := session.Run("/usr/bin/scp -tr ./"); err != nil {
panic("Failed to run: " + err.Error())
}
}
@madhurranjan
Copy link

Hi , Thanks for the gist. Really helped. There are a couple of small changes however. 1. -qrt is not needed for a file . 2. clientConfig := &ssh.ClientConfig{
User: "< > ",
Auth: []ssh.AuthMethod{
ssh.PublicKeys(clientKey),
},
}

@nullne
Copy link

nullne commented Dec 7, 2015

nice job

@zhangfuwen
Copy link

how about the other way around, how to copy files from remote host to local directories?

@nullne
Copy link

nullne commented Mar 2, 2016

@DeanSinaean just run the command scp file user@host:/path/to

@uynap
Copy link

uynap commented Jul 19, 2016

@DeanSinaean Apparently, it doesn't support copying files from the remote. It only works in "sink mode". What you need is the "source mode". Have a look at here https://blogs.oracle.com/janp/entry/how_the_scp_protocol_works. Then you will get the trick.

@svigne1
Copy link

svigne1 commented Mar 18, 2017

@uynap
I ran the example commands in the blog. The scp -t command outputs exaclt what the blog states it would.
But the scp -f command doesnot output anything. It ends without any output for me.

I know using the following code, i can get the output of any command i run remotely.
`

var stdoutBuf bytes.Buffer
session.Stdout = &stdoutBuf
session.Run(command)

`
Since scp -f command was not outputting anything. I wasn't able to scp in source mode and get the file contents.

This stackoverlow question & the 2nd answer helped me output the file contents to stdout. Using which i was able to scp the file from the remote box.

Hope this helps others. (The box i was working on gave me permissions only to scp. I didnt have permissions to run cat command. Hence this workaround)

@tectiv3
Copy link

tectiv3 commented May 10, 2017

@svigne1, @uynap thank you. That blog post and scp remotehost:/path/to/remote/file /dev/stdout from that stackoverflow answer really helped. It was fun to get it working.

@ishankhare07
Copy link

very interesting, however I have some doubts of my own

when I do a simple ssh -v user@ip_address
i see the following

OpenSSH_7.4p1, LibreSSL 2.5.0
debug1: Reading configuration data /Users/ishan/.ssh/config
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: Connecting to x.x.x.x [x.x.x.x] port 22.
debug1: Connection established.                                                                                                                                             
debug1: identity file /Users/ishan/.ssh/id_rsa type 1
debug1: key_load_public: No such file or directory
debug1: identity file /Users/ishan/.ssh/id_rsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /Users/ishan/.ssh/id_dsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /Users/ishan/.ssh/id_dsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /Users/ishan/.ssh/id_ecdsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /Users/ishan/.ssh/id_ecdsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /Users/ishan/.ssh/id_ed25519 type -1
debug1: key_load_public: No such file or directory
debug1: identity file /Users/ishan/.ssh/id_ed25519-cert type -1
debug1: Enabling compatibility mode for protocol 2.0
debug1: Local version string SSH-2.0-OpenSSH_7.4
debug1: Remote protocol version 2.0, remote software version OpenSSH_7.2p2 Ubuntu-4ubuntu2.2
debug1: match: OpenSSH_7.2p2 Ubuntu-4ubuntu2.2 pat OpenSSH* compat 0x04000000
debug1: Authenticating to x.x.x.x:22 as 'ishan'
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: algorithm: curve25519-sha256@libssh.org
debug1: kex: host key algorithm: ecdsa-sha2-nistp256
debug1: kex: server->client cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug1: kex: client->server cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug1: Server host key: ecdsa-sha2-nistp256 SHA256:vomMDUDrTVCWd9/L7N4kSJ4eJ3VHP9+4uFCnEyJLREA
debug1: Host 'x.x.x.x' is known and matches the ECDSA host key.
debug1: Found key in /Users/ishan/.ssh/known_hosts:28
debug1: rekey after 134217728 blocks
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug1: SSH2_MSG_NEWKEYS received
debug1: rekey after 134217728 blocks
debug1: SSH2_MSG_EXT_INFO received
debug1: kex_input_ext_info: server-sig-algs=<rsa-sha2-256,rsa-sha2-512>
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Offering RSA public key: /Users/ishan/.ssh/id_rsa
debug1: Server accepts key: pkalg rsa-sha2-512 blen 535
debug1: Authentication succeeded (publickey).
Authenticated to x.x.x.x ([x.x.x.x]:22).
debug1: channel 0: new [client-session]
debug1: Requesting no-more-sessions@openssh.com
debug1: Entering interactive session.
debug1: pledge: network
debug1: client_input_global_request: rtype hostkeys-00@openssh.com want_reply 0
Welcome to Ubuntu 16.04.3 LTS (GNU/Linux 4.10.0-38-generic x86_64)

From what I understand is, the ssh-client first connects to the server, then picks up my id_rsa
uses my current hostname to authenticate.

This script here first takes a client config and then opens the connection.
Is there a way one can do the same with go- open connection, then try with different authentication methods available and proceed with the one that succeeds?

@baptistedonaux
Copy link

baptistedonaux commented Apr 12, 2018

Thank you for great job !

How can I determine "command" to send in Stdin ?

  • D0775 : D for directory (?) and next chars for permissions
  • C0644 : C for… create (?) and next chars for permissions

Do you have any links about theses instructions ? I searched in scp manual with no success.

[UPDATE]

I can found this post (thanks Web Archive) which gives commands availables. More, after few research it seems that SCP commands are not defined in any RFC.

@yesview
Copy link

yesview commented Mar 21, 2019

@baptistedonaux God bless you! Helped me a lot!

@juztin
Copy link

juztin commented Mar 10, 2022

Awesome! ty

This is working for me, and the file is copied over correctly, but the call to Run always returns an error: Process exited with status 1

session.Run("/usr/bin/scp -tr /tmp/")

anyone have any clues as to why this would be?

Got it!
I was writing a binary file, so I need this in-place of fmt.Fprint(w, content):

io.Copy(w, bytes.NewReader(galaArchive))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment