最近実践している OpenSSH の設定ファイルの分割

  • 3
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

OpenSSH の設定ファイル ~/.ssh/config のファイル分割について考察してみることにしました。

世間で模索される設定ファイルの分割

2016年4月現在の OpenSSH の設定ファイルは分割だったり外部ファイル読み込みをサポートしていません。Apache の Include ディレクティブのようなものがあるといいのですが、現状では分割管理したい人が独自に色々な手法を試みているようです。

だいたいの手法は以下のように分別できます。

  • 分割管理したファイルを手作業で ~/.ssh/config に結合する手法
  • 分割管理したファイルを自動的に ~/.ssh/config として結合する ssh コマンドなどの上書き or 別定義
  • 別の設定ファイル体系を持つ ssh をベースとした自作別コマンド

alias ssh='cat ~/.ssh/conf.d/*.conf > ~/.ssh/config ; ssh' など、結局は裏で ~/.ssh/config を作る方法に収斂されるわけですが、いくつか悩ましい部分があります。

  • ~/.ssh/config が作られたまま残ってしまうので、うっかりそちらを編集してしまい、分割したファイルのメンテナンスを忘れることがある
    • 意識の問題でもある
    • cat で結合生成して ssh した後に rm すればいいという意見も
  • 既に ~/.ssh/config があって結合の必要がない場合も結合することを容認しても、/.ssh/config のタイムスタンプ (mtime) に惑わされることもある

個人のコマンドなので結合コストの問題は無視したとしても、大元のデータがどこであるとかメタデータであるとかで油断したときに混乱させられるのは嫌だなぁと感じました。

~/.ssh/config を恒久的に置かないという作戦であれば

function ssh {
  cat ~/.ssh/conf.d/*.conf > ~/.ssh/config
  builtin ssh "$@"
  rm ~/.ssh/config
}

といった方法や、さらにコマンド内でファイルディスクリプタを作って捨てるような

function ssh {
  builtin ssh -F <(cat ~/.ssh/conf.d/*.conf) "$@"
}
# または
alias ssh='ssh -F <(cat ~/.ssh/conf.d/*.conf)'

といった方法もありそうです。

<() オペラントを知るなら echo <(:) といったコマンドを打ってみるとよいでしょう(: は何もしない bash 組み込みコマンド)。

ただ、私は分割した設定ファイルをバージョン管理していることもあり、結合は意識的にやったほうがあっているようでした。また、ssh コマンド類を上書き定義している or 別のラッパーコマンドがあるというのもしっくりこなかったです(別の環境で混乱しそうとか)。

設定ファイルを分割するメリット

普通には分割はできないわけで手間がデメリットだったりする設定ファイルの分割ですが、メリットもあるので行いたいという人もいます。私が考えるメリットを列挙してみます。

  • 接続ホストが大量になってくると、設定ファイルを分割することで見通しが良くなる
  • バージョン管理時には、大きなファイルを管理するよりも小さな複数のファイルを管理する方がコンフリクトが起こりづらかったりといったメリットがある
  • 他人に OpenSSH の設定ファイルを例示する際、開示してはいけないところを回避しつつ開示して良いところだけをすぐに出しやすい
  • ファイル分割という習慣だけで設定の片付けができる(一枚ファイルに書いていく方法だとどうしても散らかりやすい)

設定ファイルのバージョン管理をしている人は最近では珍しくなくなりましたが、特に他人に例示というところは意外なメリットだったりします。

私の設定ファイル管理

なるべくシンプルに、意識的な手間と利便性と環境依存性の少なさを取った方法を模索していますが、今のところは以下の様な方法をとっています。

バージョン管理

2004年くらいから CVS を使って雑にバージョン管理を行っていましたが、最近は Git でバージョン管理をしています。秘匿性の少ないドットファイル類は GitHub で公開していますが、OpenSSH のような秘匿性の高いものは BitBucket でプライベートリポジトリを作って管理しています。

今の OpenSSH 設定ファイルは ssh_config という BitBucket プライベートリポジトリで以下のような感じ。

Bitbucket の ssh_config リポジトリ.png

使いたい場合には初回に

$ cd ~/.ssh/ ; git@bitbucket.org:xtetsuji/ssh_config.git vc

などしています。ワーキングディレクトリは vc とか目立つ短いものにしておくといいかなという考えです。後々変わるかもしれません。

~/.ssh/ 自体をバージョン管理する方法も考えたのですが、さすがに秘密鍵はリモートリポジトリに入れたくないし、known_hosts のように無意識的に編集されるファイルのバージョン管理は私には面倒事が多かったのでバージョン管理はやめました(気が向いた時にコミットしたらプッシュ時にコンフリクトしたりとか)。

秘密鍵はローカルメディアにバックアップ(または Mac の TimeMachine バックアップ)、konwnhosts などは世代バックアップをしています。

独特の設定があるリモートホストについては、hosts/ホスト名/ といったディレクトリに config などの各種ファイルを放り込む運用にしています。

結合の手法

ファイル群が新たなファイルを作るというのはまさに Make の仕事。だいたいどこにでも入っている make コマンドを使うことにしました。

CP  = cp -p
CONFIG_FILES    = $(shell echo conf.d/*.conf)

usage:
    @echo "Usage:"
    @echo "  update    git update"
    @echo "  config    generate config from conf.d/*.conf"
    @echo "  ../config same as config"
    @echo "  keylist   show keylist from keylist.txt"
    @echo "  deploy    deploy config and keylist.txt to ~/.ssh/"
    @echo "  clean     remove config and deploy flag"

update:
    git fetch
    git pull origin master

config ../config: $(CONFIG_FILES)
    { \
    echo "# -*- ssh-config -*-" ; \
    cat $(CONFIG_FILES) | grep -E -v '^#( -\*-|//)' ; \
    } > $@
    perl -p -i -e 's/^# \$$sh\((.*)\)$$/ join "\n", qx{$$1} /e' $@

keylist: keylist.txt
    grep -v '^#' $<

deploy: config keylist.txt
    $(CP) config $(SSH_DIR)
    $(CP) keylist.txt $(SSH_DIR)
    echo "This file is flag file" > deploy

clean:
    rm -f config
    rm -f deploy

最初は make config deploy clean というコマンドで作成→デプロイ→掃除の流れをやっていたのですが、面倒だしワーキングディレクトリに config ができるのもややこしいので、make ../config だけにしました。これは ~/.ssh/vc がワーキングディレクトリであれば、../config が古ければ(conf.d/*.conf の mtime の最も新しいものより古ければ)作りなおすという自然な手順となります。mtime の計算をシェルスクリプトでやる必要などがないのが楽。

意識的にファイルを結合する方法

上記手順では、ファイル結合に関する設定ファイルの編集のサイクルは

  • ~/.ssh/vc/conf.d 以下のファイルを更新
  • make -C ~/.ssh/vc ../config
  • 設定の正しさを確認(ssh できるかくらい)
  • 変更を commit && push

といったものです。結局バージョン管理するなら意識的にならざるをえないし、今のところはこれで満足しています。

バージョン管理されているところは ~/.ssh/vc だということを意識さえしておけば、~/.ssh/config が存在し続けたとしてもバージョン管理上の無意識な失敗(`~/.ssh/config 自体をいじってしまうとか)もだいたい避けられます。