多数のホストの root 権限を安全に共有する


root 権限共有の悩み

チームメンバーに対する root 権限の付与は、今のところ、不本意ながらパスワード共有式となっています。

個人情報流出事件等のあおりを受け、定期的に共有パスワードを変更するポリシーで運用されてはいます。しかし、管理しているホストが多いのでメンテナンスコストもばかにならないし、作業効率も利便性も良くない。なにより、メンバーの入れ替わりはパスワード変更時期とは無関係に発生するので、安全面でも問題があります。

なんかいい方法はないものだろうかと探してみると、「これは!」と思うものを見つけました。

Sharing Admin Privileges for Many Hosts Securely

詳しくは原文に当たってもらうとして、ここではかいつまんで仕組みと注意点をまとめます。

実現できること

主に管理者サイドからの視点として、

  • チームメンバーの増減が発生しても、多数のホストのアカウント修正は不要
  • 各ホストの鍵の更新・削除は不要
  • 個々のメンバーの鍵のメンテナンスは不要(各自が責任を持つ)
  • 特別な権限管理ツールの導入は不要
  • パスワードやパスフレーズの共有は不要(原則として)

といったところが実現できます。特に重要なのは、最後のところです。では、どうやって実現しているのか?

基本的な仕組み

全メンバーのアカウントを集中管理するホストが一つ必要です。ここでは仮に darter とします。darter には univ アカウントを用意し、このアカウントの公開鍵を、管理対象となるホスト群の ~root/.ssh/authorized_keys にあらかじめ追加しておきます。

チームメンバーは darter にログインしたあと、sudo su - univ して、管理すべき他のホストに ssh します。univ に su できるかどうかは、/etc/sudoers で制御します。

どこが安全なの?

univ アカウントの秘密鍵はパスフレーズで暗号化しておきます。このパスフレーズは、長くて簡単には推測できない強固なものであり、チームメンバーの中でもコアな管理者である Joe が最初に設定しておく必要があります。

しかし、このままではチームメンバーが ssh するたびにパスフレーズを求められてしまいます。

そこで、ssh-agent が登場します。

まず、以下のように sudo su - univ した時に ssh-agent のインスタンスをチェックし、動いていれば再利用、動いていなければ自動起動するようしておきます。

~univ/.bash_profile
/usr/bin/pgrep -u univ 'ssh-agent' >/dev/null

RESULT=$?

if [[ $RESULT -eq 0 ]]  # ssh-agent is running
then
    if [[ -f /tmp/.env_ssh.univ ]]   # bring env in to session
    then
        source /tmp/.env_ssh.univ
    else    # error condition
        echo 'WARNING:  univ ssh agent running, no environment file found'
        echo '          ssh-agent being killed and restarted ... '
        /usr/bin/pkill -u univ 'ssh-agent' >/dev/null
        RESULT=1     # due to kill, execute startup code below
    fi

if [[ $RESULT -ne 0 ]] # ssh-agent not running, start it from scratch
then
    echo "WARNING:  ssh-agent being started now; ask Joe to cache key"
    /usr/bin/ssh-agent > /tmp/.env_ssh.univ
    /bin/chmod 600 /tmp/.env_ssh.univ
    source /tmp/.env_ssh.univ
fi

darter が起動すると Joe はおもむろにログインし、周りに誰もいないことを確認した上で以下のコマンドを打ち込みます。

$ sudo su - univ
$ ssh-add ~/.ssh/univ_key
Enter passphrase for /home/univ/.ssh/univ_key:

これでパスフレーズがキャッシュされました。

Joe は「準備完了」とつぶやき、これ以降ログインするチームメンバーは、管理対象のホストに軽々と ssh することができるようになるわけです。

良いところ

繰り返しになりますが、以下のようなメリットは評価できます。

  • 全メンバーでパスワードを共有しなくて良い
  • メンバー離脱できちんと権限を剥奪できる
  • おおげさなツールが不要(sudo と ssh だけ)
  • メンテナンス対象のホストは一つだけ(管理対象ホストが増えると、authorized_keys の更新は必要)
  • ユーザー追加で権限のグルーピングが容易(univ は全ホスト管理可能、rstr はホスト限定などいくらでも)
  • たとえ univ の秘密鍵が盗まれたとしても、パスフレーズを盗まれなければ安全

注意点

一方で、多くの人が気づいているであろう注意点も挙げておきます。

  • darter は SPOF になる
  • darter の root を乗っ取られると壊滅状態
  • darter の再起動がプレッシャーになる
  • Joe の不在(休暇・転職・入院・急死等)に備え、パスフレーズの共有は不可避(限定的にせよ)
  • パスフレーズを知るメンバーの離脱タイミングでパスフレーズの変更が必要

元記事のコメント欄では、チームメンバーが管理対象ホストの authorized_keys を書き換え可能だからこれではダメだ、と書いた人がいましたが、そもそもの発端は root 権限を安全に共有しようという話 darter を必ず経由する必要があるという前提なので、ピントがずれてる気がします。

というわけで、以下のような課題が浮かび上がります。

  • darter の可用性・耐障害性・冗長性確保
  • Joe をはじめとするコア管理メンバー間のパスフレーズ共有手段
  • 悪意を持ったメンバーの検出(いやな仕事だ。。。)

なかなか、一筋縄ではいかないですね。