概要
以下、自分用のメモです。忘れない内にメモメモ。
前回の続き。今回は秘密鍵にパスフレーズを付与している場合のコードです。
前回の記事は以下です。
サンプル
以下のサンプルですが、処理に必要な情報を環境変数から取得するようにしています。
動かす場合は以下を設定しておいてください。
- $SSH_USER
- SSH接続するユーザ名
- $SSH_PASS
- 秘密鍵のパスワード
- $SSH_HOST
- 接続先を
xxx.xxx.xxx.xxx:ポート番号
の形式で。
- 接続先を
- $HOST_KEY
- リモートサーバの公開鍵 (上の ssh-keyscan の結果)
SSH接続したら、cat /etc/os-release | head -n 2
を実行して終わります。
package main import ( "io/ioutil" "os" "path/filepath" "github.com/devlights/gomy/output" "golang.org/x/crypto/ssh" ) type ( // returnCode -- 処理結果 returnCode int ) const ( success returnCode = iota // 成功 homedirNotFound // $HOME が展開出来なかった readErrSSHPrivateKey // 秘密鍵読み取り中にエラー parseErrSSHPrivateKey // 秘密鍵の解析中にエラー parseErrPublicKey // 公開鍵の解析中にエラー connErrSSHClient // SSH接続中にエラー canNotCreateNewSSHSession // SSHにてセッションを生成中にエラー execErrInSSHSession // SSHにてコマンドを実行中にエラー ) const ( command = "cat /etc/os-release | head -n 2" ) // 環境変数より取得する情報 var ( sshUser string // SSH ユーザ名 sshPass string // 秘密鍵のパスワード sshHost string // SSH リモートホスト (xxx.xxx.xxx.xxx:port) hostKey string // リモートホストの公開鍵 (ssh-keyscan の 結果)(e.g: xxx.xxx.xxx.xxx ecdsa-sha2-nistp256 xxxxxxxxxxx) ) func init() { sshUser = os.ExpandEnv("$SSH_USER") sshPass = os.ExpandEnv("$SSH_PASS") sshHost = os.ExpandEnv("$SSH_HOST") hostKey = os.ExpandEnv("$HOST_KEY") } func main() { if sshUser == "" { output.Stderrl("[error]", "$SSH_USER が 設定されていません.") } if sshPass == "" { output.Stderrl("[error]", "$SSH_PASS が 設定されていません.") } if sshHost == "" { output.Stderrl("[error]", "$SSH_HOST が 設定されていません.") } if hostKey == "" { output.Stderrl("[error]", "$HOST_KEY が 設定されていません.") } os.Exit(int(run())) } func run() returnCode { // ------------------------------------------------------------- // GO で ssh を扱う場合 golang.org/x/crypto/ssh を使う // // 標準パッケージには入っていないので利用する場合は go get する. // $ go get -v -u golang.org/x/crypto/ssh // // SSH で接続する場合、大きく分けて // 1. パスワード認証 // 2. 鍵認証 // の2つがある。 // // また、リモートサーバの 公開鍵 を // 1. 検証する // 2. 検証しない // の2つがある。 // // なお、今回は ssh-keygen で パスワード付きの秘密鍵を作ったとする。 // ------------------------------------------------------------- // ------------------------------------------------------------- // SSH 接続してコマンド実行 // - 鍵認証 // - リモートサーバの公開鍵を検証する // ------------------------------------------------------------- // ------------------------------------------- // $HOME/.ssh/id_rsa からデータ読み取り // homeDir, err := os.UserHomeDir() if err != nil { output.Stderrl("[error]", err) return homedirNotFound } sshPrivKeyFile := filepath.Join(homeDir, ".ssh/id_rsa") privKey, err := ioutil.ReadFile(sshPrivKeyFile) if err != nil { output.Stderrl("[error]", err) return readErrSSHPrivateKey } // ------------------------------------------- // 秘密鍵を渡して Signer を取得 // signer, err := ssh.ParsePrivateKeyWithPassphrase(privKey, []byte(sshPass)) if err != nil { output.Stderrl("[error]", err) return parseErrSSHPrivateKey } // ------------------------------------------- // リモートサーバ の 公開鍵 を得る // _, _, pubKey, _, _, err := ssh.ParseKnownHosts([]byte(hostKey)) if err != nil { output.Stderrl("[error]", err) return parseErrPublicKey } // ------------------------------------------- // SSH の 接続設定 を構築 // config := &ssh.ClientConfig{ // SSH ユーザ名 User: sshUser, // 認証方式 Auth: []ssh.AuthMethod{ // 鍵認証 ssh.PublicKeys(signer), }, // リモートサーバの公開鍵を検証 HostKeyCallback: ssh.FixedHostKey(pubKey), } // ------------------------------------------- // SSH で 接続 // conn, err := ssh.Dial("tcp", sshHost, config) if err != nil { output.Stderrl("[error]", err) return connErrSSHClient } // ------------------------------------------- // セッションを開いて、コマンドを実行 // sess, err := conn.NewSession() if err != nil { output.Stderrl("[error]", err) return canNotCreateNewSSHSession } defer sess.Close() // リモートサーバでのコマンド実行結果をローカルの標準出力と標準エラーへ流す sess.Stdout = os.Stdout sess.Stderr = os.Stderr if err = sess.Run(command); err != nil { output.Stderrl("[error]", err) return execErrInSSHSession } return 0 }
秘密鍵のパスフレーズが存在する場合は、 ssh.ParsePrivateKeyWithPassphrase(privKey, passPhase)
を使います。
実行すると以下のようになります。
$ go run . NAME="Ubuntu" VERSION="20.04 LTS (Focal Fossa)"
参考情報

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
- 作者:Alan A.A. Donovan,Brian W. Kernighan
- 発売日: 2016/06/20
- メディア: 単行本(ソフトカバー)

Go Programming for Network Operations: A Golang Network Automation Handbook (English Edition)
- 作者:McAllen, Tom
- 発売日: 2018/10/21
- メディア: Kindle版
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場