例えばこんなhostsファイルがあったとして
[web]
192.0.2.1
192.0.2.2
192.0.2.3
[db]
192.0.2.4
こんな感じで共通のロールを適用する際にOSユーザーとパスワードも設定したい、ポリシー上パスワードの使い回しは厳禁なので全てユニークにしたい場合の方法
---
- hosts: all
roles:
- common
.
.
.
#lookup pluginの作成
Jinja2で用意されてるフィルタってやつでいい感じにできるのかなと思ったけど探しても見つからなかった。
結局ansibleに用意されているlookup pluginという機能を利用することにした。
元々ansibleにはパスワード用プラグインも用意されているけど、どうも一度生成したパスワードはファイルに書き込んで、二度目以降はそのファイルの中身を参照するようになってて今回の用途には合わないので、既存のプラグインをちょっと改造
元々のコードはpassword.py
改造した部分の抜粋
def run(self, terms, variables, **kwargs):
ret = []
for term in terms:
relpath, params = _parse_parameters(term)
# get password or create it if file doesn't exist
path = self._loader.path_dwim(relpath)
if not os.path.exists(path):
pathdir = os.path.dirname(path)
try:
makedirs_safe(pathdir, mode=0o700)
except OSError as e:
raise AnsibleError("cannot create the path for the password lookup: %s (error was %s)" % (pathdir, str(e)))
chars = "".join(getattr(string, c, c) for c in params['chars']).replace('"', '').replace("'", '')
password = ''.join(random.choice(chars) for _ in range(params['length']))
if params['encrypt'] is not None:
salt = self.random_salt()
content = '%s salt=%s' % (password, salt)
else:
content = password
with open(path, 'a') as f:
os.chmod(path, 0o600)
f.write(content + '\n')
if params['encrypt']:
password = do_encrypt(password, params['encrypt'], salt=salt)
ret.append(password)
return ret
password.pyとの差分
124,125c124,125
< chars = "".join(getattr(string, c, c) for c in params['chars']).replace('"', '').replace("'", '')
< password = ''.join(random.choice(chars) for _ in range(params['length']))
---
> chars = "".join(getattr(string, c, c) for c in params['chars']).replace('"', '').replace("'", '')
> password = ''.join(random.choice(chars) for _ in range(params['length']))
127,134c127,129
< if params['encrypt'] is not None:
< salt = self.random_salt()
< content = '%s salt=%s' % (password, salt)
< else:
< content = password
< with open(path, 'w') as f:
< os.chmod(path, 0o600)
< f.write(content + '\n')
---
> if params['encrypt'] is not None:
> salt = self.random_salt()
> content = '%s salt=%s' % (password, salt)
136,156c131,134
< content = open(path).read().rstrip()
<
< password = content
< salt = None
<
< try:
< sep = content.rindex(' salt=')
< except ValueError:
< # No salt
< pass
< else:
< salt = password[sep + len(' salt='):]
< password = content[:sep]
<
< if params['encrypt'] is not None and salt is None:
< # crypt requested, add salt if missing
< salt = self.random_salt()
< content = '%s salt=%s' % (password, salt)
< with open(path, 'w') as f:
< os.chmod(path, 0o600)
< f.write(content + '\n')
---
> content = password
> with open(path, 'a') as f:
> os.chmod(path, 0o600)
> f.write(content + '\n')
プラグインで指定したパスに元々ファイルが存在した場合もパスワードを生成してファイルに追記するようにしただけ
これをplaybookのディレクトリにlookup_pluginsってディレクトリに「{{ 任意の名前 }}.py」として放り込んでやると「{{ 任意の名前 }}」で呼び出せる
- name: 管理者ユーザー追加
user: name={{ item.name }} password={{ item.user_pass }}
with_items:
- { name: 'admin1', user_pass: "{{ lookup('password-uniq','~/playbook/.pass/user-pass') }}" }
これを実行するとそれぞれのユーザーのパスワードがユニークになる
TASK [common : 管理者ユーザー追加] *************************************************
changed: [192.0.2.1] => (item={u'name': u'admin1', u'user_pass': u'nyan'})
changed: [192.0.2.2] => (item={u'name': u'admin1', u'user_pass': u'wan'})
changed: [192.0.2.3] => (item={u'name': u'admin1', u'user_pass': u'caa'})
changed: [192.0.2.4] => (item={u'name': u'admin1', u'user_pass': u'chun'})
パスワードはプラグインで指定したファイルに追記されるけど、特にホスト名と紐づけされた形で書かれるわけではないので注意
元々用意されてたパスワードプラグインを改造しただけなので、引数の詳細などはここを