じゃあ、おうちで学べる

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

Golangでcrypto/ssh をやっていくのだけどインフラエンジニアはssh_configが使いたい

概要

正直なことを話すとたぶん、人はcrypto: golang.org/x/crypto/sshさえ読めばGolangSSHを書けるようになる。
しかし、サーバーを実際に運用していくことになれば踏み台サーバーを用意して100台のサーバーにアクセスすることもよくあることである。
100台のサーバーにアクセスしたいのに前回 のようなやり方を取っていると100台分のサーバーに設定をせねばならずたぶんなのですがviperを用いて設定ファイルを書くことになるのだと思います。2重管理による徒労感は半端ないです。実装も膨らむし...

ssh_configが使いたい

kevinburke/ssh_configというライブラリには ssh_config を読み込む機能がありますのでこれを使って ~/.ssh/config の設定情報を使ってSSH接続をしてみます(サンプルはlocalhost)。

package main

import (
    "bytes"
    "github.com/kevinburke/ssh_config"
    "golang.org/x/crypto/ssh"
    "io/ioutil"
    "log"
)

func main() {
    host := "localhost"
    user := ssh_config.Get(host, "User")
    addr := ssh_config.Get(host, "Hostname") + ":" + ssh_config.Get(host, "Port")
    auth := []ssh.AuthMethod{}
    buf, err := ioutil.ReadFile(ssh_config.Get(host, "IdentityFile"))
    if err != nil {
        panic(err)
    }
    key, err := ssh.ParsePrivateKey(buf)
    if err != nil {
        panic(err)
    }
    auth = append(auth, ssh.PublicKeys(key))
    config := &ssh.ClientConfig{
        User:            user,
        HostKeyCallback: ssh.InsecureIgnoreHostKey(),
        Auth:            auth,
    }
    conn, err := ssh.Dial("tcp", addr, 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())
}

まとめ