じゃあ、おうちで学べる

本能を呼び覚ますこのコードに、君は抗えるか

Golangでcrypto/ssh をやっていく 単パスワード認証と単鍵認証編

Nヵ月ぶりに技術ブログを書くで候

概要

正直なことを話すとたぶん、人はcrypto: golang.org/x/crypto/sshさえ読めばGolangSSHを書けるようになる。しかし、50回ぐらい読んでいるので個人的によく使うモジュールについてのメモである。 本当にcrypto: golang.org/x/crypto/sshのドキュメントが優秀なのでここを見れば解決するのとりあえず、読んでくれそれだけではこのブログの価値はクソ以下なのでもはや、サンプルコードを書くしかない

パスワード認証

パスワード認証のみでsshを行いたければ&ssh.ClientConfigの中でHostKeyCallback: ssh.InsecureIgnoreHostKey() を設定してあげなければならない。
あとは、ssh.Dial でセッションを繋いで(切るの忘れずに) conn.NewSession()でアクセスを行って session.Stdout = &bで標準出力を確認しながらsession.Run()でコマンドを実行させる。

https://godoc.org/golang.org/x/crypto/ssh#Dial より

Dialは、指定されたSSHサーバーへのクライアント接続を開始します。これは、指定されたネットワークアドレスに接続し、SSHハンドシェイクを開始してからクライアントをセットアップする便利な機能です。着信チャネルおよび要求へのアクセスには、代わりにNewClientConnでnet.Dialを使用します。 上記のような

package main

import (
        "bytes"
        "log"

        "golang.org/x/crypto/ssh"
)

func main() {

        ip := "****.**.***.***"
        port := "22"
        user := "root"
        pass := "********************************"
        config := &ssh.ClientConfig{
                User:            user,
                HostKeyCallback: ssh.InsecureIgnoreHostKey(),   // https://github.com/golang/go/issues/19767
                Auth: []ssh.AuthMethod{
                        ssh.Password(pass),
                },
        }

        conn, err := ssh.Dial("tcp", ip+":"+port, config)
        if err != nil {
                log.Println(err)
        }
        defer conn.Close()

        session, err := conn.NewSession()
        if err != nil {
                log.Println(err)
        }
        defer session.Close()

        //Check whoami
        var b bytes.Buffer
        session.Stdout = &b
        remote_command := "/usr/bin/whoami"
        if err := session.Run(remote_command); err != nil {
                log.Fatal("Failed to run: " + err.Error())
        }
        log.Println(remote_command + ":" + b.String())
}

鍵認証

パスワード認証と鍵認証の差は&ssh.ClientConfigAuth: ssh.AuthMethod{} を変更すればよいだけである

package main

import (
        "bytes"
        "log"

        "golang.org/x/crypto/ssh"
)

func main() {

        ip := "***.***.***.***"
        port := "22"
        user := "root"
        pass := "*****************"
        config := &ssh.ClientConfig{
                User:            user,
                HostKeyCallback: ssh.InsecureIgnoreHostKey(),
                Auth: []ssh.AuthMethod{
                        ssh.Password(pass),
                },
        }

        conn, err := ssh.Dial("tcp", ip+":"+port, config)
        if err != nil {
                log.Println(err)
        }
        defer conn.Close()

        session, err := conn.NewSession()
        if err != nil {
                log.Println(err)
        }
        defer session.Close()

        //Check whoami
        var b bytes.Buffer
        session.Stdout = &b
        remote_command := "/usr/bin/whoami"
        if err := session.Run(remote_command); err != nil {
                log.Fatal("Failed to run: " + err.Error())
        }
        log.Println(remote_command + ":" + b.String())
}

利用したコードはここ workspace_2019/blog/crypto_ssh at master · nwiizo/workspace_2019 · GitHub に載せてあるので自由に使ってください。

まとめ

基本的には crypto: golang.org/x/crypto/ssh を読めばいいのです。が、もし Golangsshをいい感じにやっていくにはまだまだ工夫していかねばなりません。設定ファイルとの呼び出しをどうするのか?N個同時にセッションを持ちたい時にはどういった工夫が必要なのか?考慮すべき事項はたくさんあります。ですが 皆さん、オペレーションはどんどん自動化していきましょう。