【Ansible】開発者ユーザー追加・削除のベスト・プラクティスを考える
開発メンバーが新たに加わるたびに、
サーバ全てで
- ユーザを追加したり
- グループに加えてやったり
- 公開鍵を登録したり
- パスワード設定したり
手動でやってるとすごく面倒ですよね...
そういうときには、やはりプロビジョニングツールの出番でしょう。
いろいろ調べて、いい感じかな、という所まできたので、メモ代わりに晒します。
利用するツールは,Ansibleです。
準備
AnsibleやPythonのインストール。
加えて、pipでインストールできる、libpassをインストールしておいてください。
$ pip install libpass
ファイル
配置
Ansibleドキュメントの、ベスト・プラクティスを参考にしています。
Best Practices — Ansible Documentation
ファイル配置は下記の通りです。
├── README.md
├── Vagrantfile # 検証用のVagrantfile
├── ansible.cfg # 設定ファイル
├── hosts # インベントリディレクトリ
│ ├── dev
│ │ └── inventory # インベントリファイル
│ └── prd # 今回は使わない
├── roles # Playbook用roleディレクトリ
│ └── user
│ ├── tasks
│ │ └── main.yml # タスク(実際の処理)
│ └── vars
│ └── main.yml # 変数(追加するユーザの情報など)
├── tools
│ └── password_hash.py
└── user.yml
Vagrantfile
検証にvagrantを利用するため、vagrantで立ち上げる検証環境の設定を記述しています。
Vagrantで仮想環境を構築するまでの内容は、以前に執筆した下記の記事を参考にしてください。
【チュートリアル】Ansible 基本のキ (インストールから複数サーバへの変更適用) - Qiita
# -*- mode:ruby -*-
# vi: set ft=ruby :
Vagrant.configure(2) do |config|
config.ssh.insert_key = false
config.vm.define "webserver1" do |webserver1|
webserver1.vm.hostname = "webserver1"
webserver1.vm.box = "bento/centos-6.7"
webserver1.vm.network "private_network", ip: "192.168.11.21"
end
config.vm.define "webserver2" do |webserver2|
webserver2.vm.hostname = "webserver2"
webserver2.vm.box = "bento/centos-6.7"
webserver2.vm.network "private_network", ip: "192.168.11.22"
end
end
ansible.cfg
設定ファイル。
こちらも内容については以前の記事を参考にしてください。
[defaults]
hostfile=hosts/dev
remote_user=vagrant
private_key_file=~/.vagrant.d/insecure_private_key
retry_files_enabled=False
hosts/dev/inventory
Vagrantで立ち上げる仮想環境のインベントリを記述しています。
[webservers]
192.168.11.21
192.168.11.22
roles/user/tasks/main.yml
ここが一つのメインですね。
実行していくタスクを記述しています。
with_items
で、ループ処理を実行していますが、
with_items
で指定されている内容は、roles/user/vars/main.yml
で記述しています。
各行の内容は、コメントで説明します。
---
# グループは先に作成しておく
- name: グループを追加
group: name={{ item.name }}
with_items: '{{ add_groups }}'
# 追加するユーザのグループはwebmasterとする
# 追加するユーザは、全員sudoerとする
# パスワードハッシュは予め作成しておく
- name: ユーザを追加
user: >
name={{ item.name }}
group=webmaster
groups=webmaster,wheel
password={{ item.password_hash }}
with_items: '{{ add_users }}'
# authorized_keyモジュールを実行するのに必須
- name: libselinux-pythonインストール
yum: name=libselinux-python state=present
# 今回は、追加するユーザ = SSHで接続可能とする。よって変数は、「ユーザを追加」と同じ物を利用
- name: 公開鍵の追加
authorized_key: >
user={{ item.name }}
key={{ item.public_key }}
with_items: '{{ add_users }}'
# 今回は、削除対象のホームディレクトリごと削除するものとする
- name: ユーザを削除
user: >
name={{ item.name }}
state=absent
remove=yes
with_items: '{{ del_users }}'
roles/user/vars/main.yml
もう一つのメインです。
roles/user/tasks/main.yml
にある通り、
この変数ファイルには、公開鍵や、パスワードハッシュを含んでいます。
それらを平文のまま、バージョン管理に含めるのは、セキュリティ上好ましくないでしょう。
というわけで、ansible-vault
で暗号化してあります。
復号して...
$ ansible-vault decrypt roles/user/tasks/main.yml
平文だと、こんな内容です。
---
# 追加するユーザの名前,パスワードハッシュ,SSH接続用の公開鍵
add_users:
- name: 'watashi'
password_hash: '$6$hogehogehogehogehogehogehoge...'
public_key: 'ssh-rsa AAAABbbbbbCCCCCCDDDdddEEEE...'
# 削除するユーザの名前
del_users:
- name: 'aitsu'
# 追加するグループの名前
add_groups:
- name: 'webmaster'
**パスワードハッシュどうするの?**という声が聞こえてきそうですが、
これは、tools/password_hash.py
で作成するものとします。
これについては、のちほど。
ユーザを追加するときは、下記の様に、add_usersに追記する形で編集します
# 追加するユーザの名前,パスワードハッシュ,SSH接続用の公開鍵
add_users:
- name: 'watashi'
password_hash: '$6$hogehogehogehogehogehogehoge...'
public_key: 'ssh-rsa AAAABbbbbbCCCCCCDDDdddEEEE...'
+ - name: 'aitsu'
+ password_hash: '$6$piyopiyopiyopiyopiyopiyopiyo'
+ public_key: 'ssh-rsa AAAAHHHHHHOOOOOOGGGGGEEEEE...'
編集完了後、再び暗号化してください。
$ ansible-vault encrypt roles/user/tasks/main.yml
なお、暗号化された内容は、下記のような形になります
$ANSIBLE_VAULT;1.1;AES256
209384528435009238475029384570293485720349852430592874
(略)
tools/password_hash.py
/etc/shadowに記録されるパスワードハッシュを、取得する必要があります。
一度、どこかでshadowファイルに追記されていれば、同じパスワードハッシュを再利用するのも手ですが、
どこにも登録されていない場合は、コマンドなり、ツールなりでハッシュを生成しなければなりません。
そんなわけで、password_hashを生成するスクリプトを作成しました。
作成にあたって、下記のサイトを参考にさせて頂きました。
Python の passlib でパスワードをハッシュ化する | CUBE SUGAR STORAGE
from passlib.apps import custom_app_context as pwd_context
import getpass
if __name__ == '__main__':
password = getpass.getpass()
hashed_password = pwd_context.encrypt(password)
print hashed_password
実行するには、このファイルを実行するだけです
$ ./tools/password_hash.py
Password: < ここで入力
$6$rounds=603084$XsVk8R0O3KVgHJEi$mp... < ハッシュが生成される
user.yml
実行するPlaybookです。
roleを実行するだけの内容です。
対象(hosts)は、一旦allとしていますが、
inventoryでグループ管理を行っているなら、適宜書き換えてください。
また、コメントで、大まかな説明をしています。
---
# Playbook user.ymlで実行するユーザー関連の変更
#
# 必要なライブラリ
# - libpass
# $ pip install libpass
# vars
# - add_users: 追加するユーザ
# - del_users: 削除するユーザ
# - add_groups: 追加するグループ
#
#
# 注意
# - ユーザ削除時には、delete_usersに追加するだけではなく、add_usersからの削除も行う。
# 特に影響はでないが、無駄に作成と削除するのは好ましくない。
#
#
# パスワードハッシュを取得方法
#
# 0. 同じパスワードが使われているサーバがあれば、/etc/shadowから取得可能
#
# 1. ツールを利用
# $ ./tools/password_hash.py
# Password: {{ プロンプトでパスワードを入力 }}
- hosts: all
become: True
roles:
- user
実行コマンド
念のため。
ansible-playbook user.yml --ask-vault-pass
インベントリやsshユーザなどは、
ansible.cfgにデフォルトの値を記載しているので、
ここでは指定しません。
オプションに設定している --ask-vault-pass
は、
ansible-vaultで暗号化したファイルを復号するのに利用します。
おわりに
パスワードハッシュなど、バージョン管理に入れたくないファイルがあったり、
繰り返しの処理だったり、
Ansibleの便利機能に助けられますね。
もちろん、セキュリティ的にクリティカルな内容(秘密鍵など)は、
暗号化したとしてもバージョン管理に含めるべきではないですが。
とはいえ、Ansibleはまだ絶賛勉強中なので、
鋭い突っ込みをお待ちしております。