ぼくのかんがえたさいきょうの(?)SSH鍵運用管理方法

サーバーへ接続する際のSSH鍵ってどうしてますか? 

AWSでEC2インスタンスを立てるときに生成するSSH鍵(keypair)を開発者間で持ち回って運用・管理しているというところも結構あるのではないでしょうか。ただ、開発者が部署異動する場合や退職する場合を考えるとセキュリティ上のリスクがあります。

開発者個別に自身のSSH公開鍵を提出してもらってそれで認証するのもいいんですが、なにぶんサーバー数やユーザー数が多くなるとその管理が大変になってきます。

そんな悩みどころをまとめると次の2点になります。

* 要求①: サーバー数が多くなっても自動的に全サーバーに反映したい
* 要求②: ユーザー個別にどのサーバーにアクセス可能か管理したい


そこで以下の方式を考えてみました。

## cron pull方式

信頼と実績のcronとリポジトリpull機能を使った方式です。

### サーバー数が多くなっても自動的に全サーバーに反映させる

要点は以下です。

* .ssh/authorized_keysに相当するファイルをリポジトリ管理する
* 各サーバーではcronで定期的に.ssh配下にリポジトリ内容をpullする

リポジトリ内のファイル構成(各サーバーの.sshフォルダ配下の構成)は下記のようにします。肝は`authorized_keys`をシンボリックリンクにしておくこと。この構成をcronで定期的にpullしてくるようにします。

```
REPO_ROOT
  ├ authorized_keys # ← シンボリックリンク。リポジトリ管理外
  ├ config          # ← リポジトリ管理外
  ├ known_hosts     # ← リポジトリ管理外
  ├ rc
  └ sources
  　　├ authorized_keys-Service1-PRD-root # 本番環境へアクセスできるユーザーのSSH鍵集
  　　├ authorized_keys-Service1-PRD-ref  # 本番環境へ参照のみできるユーザーのSSH鍵集
  　　├ authorized_keys-Service1-STG-root # ステージング環境へアクセスできるユーザーのSSH鍵集
  　　├ authorized_keys-Service1-DEV-root # 開発環境へアクセスできるユーザーのSSH鍵集
  　　├ authorized_keys-Service2-PRD-root
  　　├ authorized_keys-Service2-STG-root
  　　├ authorized_keys-Service2-DEV-root
  　　　　……
```

上記では仮に「authorized_keys-＜サーバー名＞-＜ステージ＞-＜Unixユーザー名＞」としています。

### ユーザー個別にどのサーバーにアクセス可能か管理する

前述までの仕組みで要求①が実現できました。次は要求②です。

ユーザーとアクセス可能なサーバーをスプレッドシートで管理し、CSV出力したものをスクリプトに食わせて`authorized_keys_XXX`ファイルを出力させます。

|timestamp          |user_name|ssh_key|servers|options|memo|
|:------------------|:--------|:------|:------|:------|:---|
|2015/07/15 12:34:56|hanako   |ssh-rsa AAAB3z(中略)FlqFz hanako@example.com|Server1-PRD-root, Server2-PRD-root|なし|管理者|
|2015/07/15 14:56:12|tarou    |ssh-rsa AAAB3x(中略)DzsBa tarou@example.com|Server1-DEV-root|なし|開発者|
|2015/07/15 16:23:46|jirou    |ssh-rsa AAAB3y(中略)AhrJk jirou@example.com|Server1-PRD-ref|no-pty,no-X11-forwarding,no-agent-forwarding|DB参照者|

optionsは、authorized_keys内で指定するオプションです。DB参照者のようにログインはせずポートフォワーディングだけしたい場合は「no-pty,no-X11-forwarding,no-agent-forwarding」を指定するなどしておくとよいと思います。

スプレッドシートはGoogle Driveのフォームなどを使うと、GitHubやBitbucketのSSH鍵登録フォームのような感じになり、ユーザーも管理者も幸せになれそうな気がします。

![スクリーンショット 2015-07-15 21.37.11.png](https://qiita-image-store.s3.amazonaws.com/1951/65069/ea91f5d9-c10e-829b-8df0-7cb996fa6b3c.png "スクリーンショット 2015-07-15 21.37.11.png")



### おまけ

authorized_keysには小粋な機能があって、接続してきたSSH鍵それぞれに対して個別に環境変数を設定することなどができます。

参考: [authorized_keys ファイルについて調べてみたら楽しかった． - それマグで！](http://takuya-1st.hatenablog.jp/entry/20110813/1313252992)

なので、以下の様な`authorized_keys_XXX`を生成するようにしておき、`.ssh/rc`にシェルスクリプトを仕込んでおくとログインしてきたユーザー名&接続元IPをログに記録することができます。

```authorized_keys_XXX
environment="SSH_LOGIN_NAME=hanako" ssh-rsa AAAB3z(中略)FlqFz hanako@example.com
```

```shell:.ssh/rc
if [ "$SSH_LOGIN_NAME" != "" ]; then
    logger -t SSH "$SSH_LOGIN_NAME on $USER accessing now! (from $SSH_CLIENT)"
fi
```


## メリット・デメリット

* 持ち回り方式
    * メリット:
        * サーバー側の運用・管理が楽。何もしなくてイイ
    * デメリット:
        * 開発者の部署移動や退職時に個人別に権限を剥奪することができない
* cron pull方式
    * メリット:
        * 開発者の部署移動や退職時に個人別に権限を剥奪することができる
    * デメリット:
        * pullが失敗するとログインできないサーバーになってしまう
            * それでも最後にpull成功していた状態は保たれると思われる


## おわりに

chefとか使ったらもっとスマートにできるのかもしれませんが、使ったことなくてわかってないです。
もっと簡単にできるよという方法をご存知のかた、ぜひとも教えて下さい。