下記にてWin10でSSHサーバーを立ててみたので、
ならGoからいじってみようということでいろいろ試してみます。
試した環境
- Windows 10 Pro (64bit) 20H2
- SSHサーバー
別のPCに接続するわけではなく、同じPC内でやり取りする感じです。
やりたいこと
Go から SSHサーバーに 接続してコマンド叩く!
どうやるか
まずは golang.org/x/crypto/ssh の godoc を見てみる。
Dial() の Example 使ったらできそう。
- Dial する
- Session 作る
- Run する
ssh.go
package main
import (
"bytes"
"fmt"
"log"
"golang.org/x/crypto/ssh"
)
func main() {
config := &ssh.ClientConfig{
User: "hiro",
Auth: []ssh.AuthMethod{
ssh.Password("password"),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // password認証は設定
}
client, err := ssh.Dial("tcp", "localhost:22", config)
if err != nil {
log.Fatal("Failed to dial: ", err)
}
defer client.Close()
session, err := client.NewSession()
if err != nil {
log.Fatal("Failed to create session: ", err)
}
defer session.Close()
var b bytes.Buffer
session.Stdout = &b
if err := session.Run("/usr/bin/whoami"); err != nil {
log.Fatal("Failed to run: " + err.Error())
}
fmt.Println(b.String())
}
パスワード認証な設定にしつつ、IDとPASSを変えて実行してみる。
output
$ ssh.exe
hiro
できた!
糸冬。
・・・だけだとあっけないので、もう少しいじってみます。
ファイルを送ってみる
SCPを使ってファイルを送ってみる。
- epoch秒+文字な入力を送ってファイルを作成
- 送られたファイルの内容を表示
- ファイルに追記・表示
コードはいろいろまとめたほうが綺麗になるし再利用できるけど
とりあえず試すだけなので適当実装ですw
ssh.go
package main
import (
"bytes"
"fmt"
"log"
"time"
"golang.org/x/crypto/ssh"
)
func main() {
config := &ssh.ClientConfig{
User: "hiro",
Auth: []ssh.AuthMethod{
ssh.Password("hirohito"),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // password認証は設定
}
client, err := ssh.Dial("tcp", "localhost:22", config)
if err != nil {
log.Fatal("Failed to dial: ", err)
}
defer client.Close()
// scp: send content remotely
fmt.Println("scp -----")
{
session, err := client.NewSession()
if err != nil {
log.Fatal("Failed to create session: ", err)
}
defer session.Close()
go func() {
w, _ := session.StdinPipe()
defer w.Close()
t := time.Now()
content := fmt.Sprintf("%d\n%s", t.Unix(), "hoge\nfuga\npiyo\n")
fmt.Fprintln(w, "C0644", len(content), "hoge.txt")
fmt.Fprint(w, content)
fmt.Fprint(w, "\x00")
}()
if err := session.Run("/usr/bin/scp -qrt ./"); err != nil {
log.Fatal("Failed to run: " + err.Error())
}
}
// wait
time.Sleep(1 * time.Second)
// view sent file
fmt.Println("cat -----")
{
session, err := client.NewSession()
if err != nil {
log.Fatal("Failed to create session: ", err)
}
defer session.Close()
var b bytes.Buffer
session.Stdout = &b
if err := session.Run("cat hoge.txt"); err != nil {
log.Fatal("Failed to run: " + err.Error())
}
fmt.Println(b.String())
}
// add to file
fmt.Println("add -----")
{
session, err := client.NewSession()
if err != nil {
log.Fatal("Failed to create session: ", err)
}
defer session.Close()
var b bytes.Buffer
session.Stdout = &b
if err := session.Run(`perl -e 'printf qq/%s\n/, time' >> hoge.txt && cat hoge.txt`); err != nil {
log.Fatal("Failed to run: " + err.Error())
}
fmt.Println(b.String())
}
}
output
$ ssh.exe
scp -----
cat -----
1615619729
hoge
fuga
piyo
add -----
1615619729
hoge
fuga
piyo
1615619730
SCPなノウハウは下記の記事を参考に作成。
SFTP
いろいろ調べてたら、ファイル転送系は SFTP 使った方が楽かもしれない。
感想
公式のExampleをなぞっただけですが、手順がわかったのでいろいろ使えそう!