Ansible 認証情報管理
Ansible の運用で問題になるのは リモートホストへのアクセスに使用する認証情報をどう管理するかだと思います。 SSH や sudo のパスワードを毎回入力するのは避けたいが、 平文パスワードを Git リポジトリに保存するようなことはしたくないし、 SSH エージェントの起動と秘密鍵登録のセットアップを事前にやるのも面倒くさい。
Ansible には vault という組み込みのファイル暗号化機能と、 動作を拡張できるプラグインの仕組みがありますので、これらを活用して SSH 秘密鍵を含む認証情報すべてをひとつの vault ファイルに格納し、 その復号パスワードの入力だけで済むような手法を考えてみました。
コードは自分の GitHub で creds として公開していますので参照してください。
前提
creds が想定するリモートホストアクセスの認証設定は次のとおりです。
- Ansible のリモートログイン専用の エージェントユーザを各リモートホストに用意する。
- Ansible はリモートホストに公開鍵認証で SSH ログインする。 全リモートホスト共通で用いる秘密鍵を SSH エージェントプロセスに登録しておく。
- エージェントユーザは root 権限を sudo により獲得する。 ログインパスワードは推測不能かつリモートホストごとに異なるものにする。
ssh-agenty.py
Ansible には
コールバック・プラグイン
という仕組みがあり、特定のイベント発生時に好きな動作をさせることができます。
ssh-agent.py
は playbook_on_play_start
コールバックを利用し、
ansible-playbook
起動後プレイブック実行前に、
変数定義された SSH 秘密鍵を SSH エージェントに登録するプラグインです。
具体的には creds_ssh_private_key
変数に
RSA でパスフレーズなしの SSH 秘密鍵が定義されている場合、
このプラグインはそれを読み込んで SSH エージェントプロセスに登録し、
さらに authorized_keys
として使える文字列を
creds_ssh_public_key
変数に定義するという動作をします。
このプラグインを使うには、
プレイブックファイルと同じ位置にディレクトリ callback_plugins
を作成しその中にファイル ssh-agent.py を入れておきます。
あるいは ansible.cfg でプラグイン検索パスを設定してください。
creds_vault.yml
ssh-agent.py プラグインを活用した変数定義ファイルの例が
creds_vault.yml
です。このファイルは前述の creds_ssh_private_key
に加えて
次の変数を定義しています。
creds_hosts
… 各リモートホスト用ログインパスワード (inventory_hostname
をキーとするハッシュ)creds_user
… エージェントユーザ名creds_pass
… 実行中リモートホスト (inventory_hostname
) のログインパスワードcreds_crypt
… 実行中リモートホストの暗号化したログインパスワード
このファイルは GitHub リポジトリには平文で格納していますが、
SSH 秘密鍵やログインパスワードが含まれるため、
実際には ansible-vault
により暗号化した vault ファイルとして扱うべきです。
$ ansible-vault encrypt creds_vault.yml ← vault に変換する
$ ansible-vault edit creds_vault.yml ← vault の内容を編集する
エージェントユーザの作成
プレイブック init-agent.yml
は creds_vault.yml
の情報を用いて
各リモートホストにエージェントユーザをセットアップするプレイブックです。
具体的にはユーザ creds_user
を作成してパスワード creds_pass
を設定、
さらに ssh-agent.py プラグインにより自動生成された
creds_ssh_public_key
を authorized_keys
ファイルにインストールします。
このプレイブックを利用するには ansible-playbook
を実行するホストで
mkpasswd
ユーティリティが必要になりますので事前にインストールしてください
(Debian では apt-get install whois
を実行)。
また適当なリモートホストの認証情報を
ansible-playbook
のオプション -k
-K
-u
などで与える必要があります。
$ ansible-playbook -i hosts --ask-vault-pass -k -K -u ubuntu init-agent.yml
SSH password:
sudo password [defaults to SSH password]:
Vault password:
...
利用例
プレイブック
ping.yml
は実際の利用例です。 vars_files
で読み込んでいる
creds_ansible.yml
は Ansible の特殊変数 ansible_ssh_user
と ansible_sudo_pass
を
それぞれ creds_user
と creds_pass
で定義するだけのものです。
次のように常に ssh-agent
と共に ansible-playbook
を起動し
vault パスワードを与えるだけで、
適切な認証で各リモートホストにアクセスできます。
$ ssh-agent ansible-playbook -i hosts --ask-vault-pass ping.yml
Vault password:
PLAY [all] ********************************************************************
Loading SSH private key 93:e2:fa:0a:a6:5c:c7:43:06:79:8d:ba:2d:c4:d8:01
GATHERING FACTS ***************************************************************
ok: [host-a]
ok: [host-b]
ok: [host-c]
ok: [host-d]
...
内部
ssh-agent.py プラグインは SSH エージェントプロセスとの通信に Paramiko モジュールを使用しています。 Paramiko は Ansible が動く環境なら利用可能なはずです。
ただし Paramiko には SSH エージェントに秘密鍵を登録する機能がないため ssh-agent.py は自力でそれを行っています。 Ansible や Paramiko の内部 API を使っているためバージョンによっては正しく動作しないかもしれません。
SSH エージェントのプロトコルについては OpenSSH のファイル PROTOCOL.agent に解説があります。
おわりに
Ansible の vault 機能とプラグインの仕組みを使って、 ユーザに求める認証情報の数とセットアップの手間を減らす手法を紹介しました。
残念ながら、いくらがんばっても 最後に残る vault パスワード入力の手間だけは無くすことができません。
すでに Linux カーネルの鍵保持機構を利用するとか、 複数ユーザによるパスフレーズ共有を避けるために GnuPG を活用する とか、いろいろアイディアはあるようですが、 外付けでできることにはやはり限界がありますので、 今後の Ansible 本体の機能拡張に期待したいと思います。
2014/10/12 10:20:20 JST