GOで使えるssh-config Parserを作った
目次
はじめに
今回は、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の使い勝手を確認してから変えるかどうか考えてみようかと思います。