おそらく golang を暫く使っておられる方であればご存じだと思いますが今日は crypto/ssh
を紹介します。
Windows で ssh と聞くとどうしても msys やら cygwin やら入れないといけなくて
- ランタイムを入れるのが嫌だ
- 特殊なパス形式とか嫌だ
- そもそも業務で使いづらい
といった個人的もしくは政治的な事柄が起きてなかなか実現しづらかったりします。でも golang なら msys や cygwin に頼らず ssh コマンドを、しかもライブラリとして扱う事が出来るので golang で作ったウェブサーバやバッチから UNIX ホストに対して ssh コマンドを送る事が出来るのです。
ssh - GoDoc
package ssh import "golang.org/x/crypto/ssh" Package ssh implements an SSH client and server. SSH is...
https://godoc.org/golang.org/x/crypto/ssh
しかも openssh に依存していないので、openssh の実装に脆弱性が発見されたとしても影響を受けません。インタフェースも net
と os/exec
がうまく組み合わさったイメージで扱う事が出来て非常に便利かつ拡張性のあるパッケージになっています。どれくらい簡単で拡張性が高いかを分かって頂ける様にオレオレ ssh コマンドを作ってみました。通常 ssh コマンドはユーザ名、ホストおよびオプションを指定して ssh コマンドを起動し、パスワードプロンプトにパスワード(パスフレーズ)を入力してログインしますが、この例ではパスワードをコマンド引数から得られる様にしてあります。
package main
import (
"flag"
"fmt"
"os"
"strings"
"time"
"golang.org/x/crypto/ssh"
)
var (
user = flag.String("u", "", "user")
password = flag.String("p", "", "password")
port = flag.Int("P", 22, "port")
)
func run() int {
flag.Parse()
if flag.NArg() == 0 {
flag.Usage()
return 2
}
config := &ssh.ClientConfig{
User: *user,
Auth: []ssh.AuthMethod{
ssh.Password(*password),
},
Timeout: 5 * time.Second,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
hostport := fmt.Sprintf("%s:%d", flag.Arg(0), *port)
conn, err := ssh.Dial("tcp", hostport, config)
if err != nil {
fmt.Fprintf(os.Stderr, "cannot connect %v: %v", hostport, err)
return 1
}
defer conn.Close()
session, err := conn.NewSession()
if err != nil {
fmt.Fprintf(os.Stderr, "cannot open new session: %v", err)
return 1
}
defer session.Close()
go func() {
time.Sleep(5 * time.Second)
conn.Close()
}()
session.Stdout = os.Stdout
session.Stderr = os.Stderr
session.Stdin = os.Stdin
err = session.Run(strings.Join(flag.Args()[1:], " "))
if err != nil {
fmt.Fprint(os.Stderr, err)
if ee, ok := err.(*ssh.ExitError); ok {
return ee.ExitStatus()
}
return 1
}
return 0
}
func main() {
os.Exit(run())
}
os/exec.Command
と同じ様に os.Stdout や os.Stderr をパイプ出来る様になっていて、終了コードも得られる様になっています。簡単ですね。もう少しコードを足せば公開鍵認証を行う事も出来ます。詳しくはドキュメントを参照して下さい。サーバがパスワード認証をサポートしている場合にはバッチコマンドとして実行出来るので、もしかすると意外と便利かもしれません。ただしパスワードがコマンド引数になるという事は、ps コマンドで他のユーザにパスワードが漏れてしまう危険性がある事を理解しておいて下さい。