7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

GoでSSH接続してコマンドを叩く

Last updated at Posted at 2021-03-13

下記にてWin10でSSHサーバーを立ててみたので、
ならGoからいじってみようということでいろいろ試してみます。

試した環境

  • Windows 10 Pro (64bit) 20H2
    • SSHサーバー

別のPCに接続するわけではなく、同じPC内でやり取りする感じです。

やりたいこと

Go から SSHサーバーに 接続してコマンド叩く!

どうやるか

まずは golang.org/x/crypto/ssh の godoc を見てみる。

Dial() の Example 使ったらできそう。

  1. Dial する
  2. Session 作る
  3. 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を使ってファイルを送ってみる。

  1. epoch秒+文字な入力を送ってファイルを作成
  2. 送られたファイルの内容を表示
  3. ファイルに追記・表示

コードはいろいろまとめたほうが綺麗になるし再利用できるけど
とりあえず試すだけなので適当実装です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をなぞっただけですが、手順がわかったのでいろいろ使えそう!

7
1
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?