2016-11-13

SSH config を構造的に書くための Ruby の DSL Nymphia の紹介

このエントリーをはてなブックマークに追加

たくさんのサーバを日々扱うエンジニアにとって、SSH config をキレイに保つことは大切です。 そもそも SSH config は構造的に書きにくい仕様になっているし、「まあそんなものか..」と妥協して、SSH config のメンテが適当になってしまうこともあると思います。

今までわたしもそうだったのですが、さすがにそろそろ破滅が見えてきていて、なんとなく Ruby で DSL を作るやっていきが高まったので、Ruby の DSL で設定を書くと SSH config を生成する Nymphia というものを作りました。

https://github.com/mozamimy/nymphia

また、色があまりつかなくてさみしかったので、vim の syntax も作りました。

https://github.com/mozamimy/nymphia.vim

インストール

例によって Rubygems から。

$ gem install nymphia

こんな感じで SSH config が書けちゃうよ

基本形

まず、いちばん基本的なサンプルコードです。

identity_file :private, '~/.ssh/id_rsa.1'

my_server_port = 4321

host 'alice', 'my server on VPS' do
  hostname 'alice.example.com'
  user 'alice'
  port my_server_port
  use_identify_file :private
end

これをトランスレータにかけると、

#
# This config is generated by Nymphia 0.1.0
#

# my server on VPS
Host alice
  Hostname alice.example.com
  User alice
  Port 4321
  IdentityFile ~/.ssh/id_rsa.1

こんな感じの SSH config が生成されます。トランスレートは以下のようなコマンドを使います。

$ nymphia -f /path/to/nymphia_file.rb -o /path/to/output_file

ホストの定義は host メソッドで行います。第一引数にホスト名、第二引数に説明を書きます。第二引数は省略可能で、書いた場合は生成後の Host ディレクティブの上に添えられます。

基本的に Ruby なので、my_server_port のように変数を定義して使うといったことも可能です。

また、SSH config の不満点のひとつに、あらゆる場所に IdentityFile に設定する同じファイルパスを書かないといけないという点があります。

この問題は identity_fileuse_identify_file で解決していて、identity_file:private のような名前をつけて、host ブロックの中で use_identify_file のように書くといい感じに IdentityFile ディレクティブを埋め込んでくれます。 また、use_identify_file :usausa, :pyonpyon のように、複数指定することも可能です。

ファイル分割

load メソッドを使えば、ファイル分割をして設定を管理できます。 以下のように書くと、トランスレータ時に other_nymphia_file.rb を読み込んで実行します。

identity_file :private, '~/.ssh/id_rsa.1'

host 'alice', 'my server on VPS' do
  hostname 'alice.example.com'
  user 'alice'
  port 4321
  use_identify_file :private
end

load 'other_nymphia_file.rb'

Proxy を定義する

素の SSH config では、ホストの定義はすべて Host ディレクティブで定義するので、そのホストがプロクシとして使われるのか、通常の接続先として使われるのか、ゲートウェイとして踏み台にするのか、コードだけではわかりにくいのも不満でした。

そこで、Nymphia では、proxy というメソッドを使うことで、明示的にその接続先がプロクシとして使われることをコードで示すことができます。また、proxy コンテキスト中では、local_forward メソッドでよくあるフォワーディングの設定を読みやすく書くことができます。

以下は proxy を使った例です。

identity_file :company_gateway, '~/.ssh/id_rsa.company.gw'

proxy 'awsproxy.company.apne1' do
  hostname 'gw.apne1.example.com'
  user 'alice'
  port 19822
  use_identify_file :company_gateway

  # SOCKS proxy
  dynamic_forward 23921

  # ssh tunnels
  local_forward 'mysql-server', {
    'localhost' => 13306,
    'mysql.apne.aws.example.com' => 3306,
  }

  local_forward 'ldap', {
    'localhost' => 10389,
    'ldap.apne.aws.example.com' => 398,
  }
end

このサンプルコードを Nymphia にかけると、以下のような出力が得られます。

#
# This config is generated by Nymphia 0.1.0
#

Host awsproxy.company.apne1
  Hostname gw.apne1.example.com
  User alice
  Port 19822
  IdentityFile ~/.ssh/id_rsa.company.gw
  DynamicForward 23921
  LocalForward localhost:13306 mysql.apne.aws.example.com:3306
  LocalForward localhost:10389 ldap.apne.aws.example.com:398

応用編: ホストをグループにまとめる、group、use_gateway、default_params

構造化のための強力な機構として、Nymphia は groupgatewayuse_gatewaydefault_params といったメソッドを提供します。

以下にそれらのメソッドを使った例を示します。

identity_file :company, '~/.ssh/id_rsa.company'
identity_file :company_gateway, '~/.ssh/id_rsa.company.gw'

gateway 'company.gateway' do
  hostname 'gw.example.com'
  user 'alice'
  port 19822
end

group 'company.ap-northeast-1' do
  use_gateway 'company.gateway'

  default_params do
    check_host_ip 'no'
    strict_host_key_checking 'no'
    user 'alice'
    port 9822
    use_identify_file :company, :company_gateway
  end

  host '*.apne.aws.example.com'

  host 'alice.apne.aws.example.com' do
    hostname '10.16.16.16'
    user 'white_rabbit'
    port 7777
  end
end

この例をトランスレートすると、以下のような出力が得られます。

#
# This config is generated by Nymphia 0.1.0
#

Host company.gateway
  Hostname gw.example.com
  User alice
  Port 19822

Host *.apne.aws.example.com
  CheckHostIp no
  StrictHostKeyChecking no
  User alice
  Port 9822
  IdentityFile ~/.ssh/id_rsa.company
  IdentityFile ~/.ssh/id_rsa.company.gw
  ProxyCommand ssh company.gateway -q -W %h:%p

Host alice.apne.aws.example.com
  CheckHostIp no
  StrictHostKeyChecking no
  IdentityFile ~/.ssh/id_rsa.company
  IdentityFile ~/.ssh/id_rsa.company.gw
  ProxyCommand ssh company.gateway -q -W %h:%p
  Hostname 10.16.16.16
  User white_rabbit
  Port 7777

gateway メソッドも proxy メソッドと似ていて、host とほぼおなじはたらきをします。 ただし、gateway として登録されたホストは、group メソッドによるグループの中で use_gateway で指定することで、グループに含まれるホストの中で ProxyCommand ssh company.gateway -q -W %h:%p のような ProxyCommand ディレクティブをいい感じに生成してくれます。

また、default_params を使うと、グループ中のホストに対するデフォルトのパラメータを設定することができます。 host '*.apne.aws.example.com' で何も書いていないのに、種々のディレクティブが生成されているのがわかると思います。

また、host 'alice.apne.aws.example.com' の例のように、デフォルト値をオーバーライドすることも可能です。

GitHub 上の examples を見ると、以上の機構をすべて使った例があるので、Nymphia を使う際に参考になると思います。

Vim の syntax もあるよ

Ruby で気持ちよく書けるようになったのはいいのですが、種々の機能はメソッドとして実装されているので、vim のシンタックス定義 を作ってにぎやかに書けるようにしました。 nymphia.vim を入れると、以下のような感じでにぎやかに Nymphia ファイルを編集できます。

nymphia.vim

vim の syntax 定義を作るのが初めてで、ハチャメチャだったのを @Linda_pp さんが添削してくれました 🐶 🐰 vim のマニュアルは非常に親切なのでちゃんと読まねば..

Nymphia is 何

かわいい