目次

はじめに

今回は、GOで使用できるSSH-configのパーサーを作りました。

作った動機

自身の仕事で内部監査にかかわることがあり、その際に業務で使用するネットワークの説明をしなければいけないことがありました。 その時に、「ここのサーバーに入るためには、この踏み台サーバーを経由しないといけない」とか、 「認証が何々で~」ということを文章で伝えるよりも図で説明できれば楽だな~と思いました。 しかし、それだけのためにツールを使って手書きで図を作成したくはありませんでした。 なので、ssh-configから設定を読み込んでよしなに図を作ってくれればな~と思いそれを実現するための一歩としてこのgscpを作成しました。

既存のssh-configパーサーを使わなかった理由は?

すでに先人が作ってくれているパーサーもあるのですが、以下の理由で今回は使用しませんでした。

  • 自分が最終的に実現したい図の生成の際に使いやすい形でパース結果を出せるようにしたい
  • パーサーを作るのが好き
  • Functional Option Patternを使ってみたかった

使い方

基本的な使い方

下記のssh-configをgscpを使用してパースして見ます。

Host testhost
    # ホスト名
    HostName 192.0.2.1
    # ユーザー名
    User myuser
    # 接続用の鍵ファイルパス
    IdentityFile ~/.ssh/id_rsa
    # コネクションの切断防止(60秒周期でパケット送信)
    ServerAliveInterval  60
package main

import (
	"fmt"
	"github.com/harakeishi/gscp"
)

func main() {
	// ssh-configを読み込む
	s, _ := gscp.LoadConfig()
	// パース
	r, _ := gscp.Parse(s)
	fmt.Printf("%+v", r)
}

パース結果

[
    {
        Name:testhost 
        Options:[
            {
                Name:HostName 
                Value:192.0.2.1
            } 
            {
                Name:User 
                Value:myuser
            } 
            {
                Name:IdentityFile
                Value:~/.ssh/id_rsa
            } 
            {
                Name:ServerAliveInterval 
                Value:60
            }
        ]
    }
]

コンフィグを指定してパースする場合のやり方

上記例のように LoadConfig() に引数で何も渡さない場合、 ~/.ssh/config が読み込まれます。 もし、特定のconfigをパースしたい場合以下のように指定します。

package main

import (
	"fmt"
	"github.com/harakeishi/gscp"
)

func main() {
	// パスの指定
	path := gscp.Path("./testData/test1_config")
	// コンフィグの読み込み
	s, _ := gscp.LoadConfig(path)
	// パース
	hosts, _ := gscp.Parse(s)
	fmt.Printf("%+v\n", hosts)
}

特定ホストの情報のみを取得するやり方

パースした結果から特定のホストのみの情報を引き出したい場合は以下のように書きます。

package main

import (
	"fmt"
	"github.com/harakeishi/gscp"
)

func main() {
	// パスの指定
	path := gscp.Path("./testData/test1_config")
	// コンフィグの読み込み
	s, _ := gscp.LoadConfig(path)
	// パース
	hosts, _ := gscp.Parse(s)
	// testhostというホストの情報のみ取得する
	host := hosts.FindHost("testhost")
	fmt.Printf("%+v\n", host)
}

特定ホストの特定オプションの情報のみを取得するやり方

パースした結果から特定のホストの特定オプションみの情報を引き出したい場合は以下のように書きます。

package main

import (
	"fmt"
	"github.com/harakeishi/gscp"
)

func main() {
	// パスの指定
	path := gscp.Path("./testData/test1_config")
	// コンフィグの読み込み
	s, _ := gscp.LoadConfig(path)
	// パース
	hosts, _ := gscp.Parse(s)
	// testhostというホストの情報のみ取得する
	host := hosts.FindHost("testhost")
	// testhostのHostNameというオプションの値を取得する
	hostname := host.FindOption("HostName").Value
	fmt.Printf("%+v\n", hostname)
}

おわりに

今回ssh-configのパーサーを作成しました。 オプションをいったんname-valueのスライスで格納していますが、オプションの種類はそんなに多くないはずなのでしっかりと構造体として用意して使えるようにしたほうがいいかな?と思っています。 これも最終的な目標である、ネットワーク図の自動生成のツールを作成した際のgscpの使い勝手を確認してから変えるかどうか考えてみようかと思います。