Ansible sudoとか

投稿者: | 2018年1月26日

OpenLDAP環境が整ったところで、これまでrootユーザでansibleを実行していたが、これからは一般ユーザでansibleを扱う。

一応、イメージとしてはこんな感じ。

 

この環境下において、ldapクライアントに対してansibleでいろいろ作業する際の実行ユーザについて掘り下げていく。


前提条件

・ldapサーバでユーザを一括管理して、teraterm端末またはansibleサーバからすべてのldapクライアントに同じldapユーザでsshログインができる状態とする。
AnsibleでOpenLDAP環境構築 -⑦ldapクライアント インストールのplaybookでもあるように、/etc/sudoersをldapmanagerというグループに属しているユーザがsudoの実行権限を持つように設定されているとする。
・ldapmanagerに属しているユーザ名をuser01とする。
◎idコマンドでこのように見える状態↓

[root@ldapclient ~]# id user01
uid=1001(user01) gid=1001(ldapmanager) groups=1001(ldapmanager)

◎/etc/sudoersの設定

## Same thing without a password
# %wheel        ALL=(ALL)       NOPASSWD: ALL
%ldapmanager    ALL=(ALL)       ALL

・/etc/ansible/ansible.cfgのsudo_user、ask_sudo_pass、ask_passは事前にコメントアウトし、無効とする。

#sudo_user      = root
#ask_sudo_pass = True
#ask_pass      = True

ansible実行時のユーザ指定

rootユーザでansibleを実行する分にはあまり悩むことはないが、一般ユーザでansibleを実行する際には少し注意が必要。

ansible実行時のユーザパターンとして、以下が考えられる。

①ansibleサーバ⇒rootユーザ、playbook実行先ユーザ⇒rootユーザ
②ansibleサーバ⇒rootユーザ、playbook実行先ユーザ⇒ldapユーザ
③ansibleサーバ⇒rootユーザ、playbook実行先ユーザ⇒モジュール毎にrootユーザまたはldapユーザを指定
④ansibleサーバ⇒ldapユーザ、playbook実行先ユーザ⇒rootユーザ
⑤ansibleサーバ⇒ldapユーザ、playbook実行先ユーザ⇒ldapユーザ
⑥ansibleサーバ⇒ldapユーザ、playbook実行先ユーザ⇒モジュール毎にrootユーザまたはldapユーザを指定

だが、②③についてはあまり現実的ではない。ansibleをrootユーザで実行しているのにわざわざplaybook内でldapユーザにして権限を落としてしまっているからだ。
従って、②③の確認は(勝手に)不問とする。

一応表にまとめておく。

パターン ansible
サーバ
playbook実行時の
ldapクライアント側ユーザ
備考
root root
root ldapユーザ 確認無し
root root/ldapユーザ(モジュール毎に指定) 確認無し
ldapユーザ root
ldapユーザ ldapユーザ
ldapユーザ root/ldapユーザ(モジュール毎に指定)

 

上記のパターンごとにansibleの動作を追ってみることにする。


①ansibleサーバ⇒rootユーザ、playbook実行先ユーザ⇒rootユーザ

以下、sample.txtを生成するplaybookを例とする。

[root@ansible_sv ~]# cat /etc/ansible/yml/sample.yml
- hosts: ldapclient
  gather_facts: no
  remote_user: root

  tasks:
  - name: yum update
    yum:
      name=*
      state=present

  - name: create txt
    file:
      state=touch
      path=/home/user01/sample.txt
      owner=user01
      group=ldapmanager
      mode=0666
[root@ansible_sv ~]#

これをansible-playbookコマンドで実行すると。。。

[root@ansible_sv ~]# ansible-playbook /etc/ansible/yml/sample.yml

PLAY [ldapclient] ************************************************************************************************************************************

TASK [yum update] ************************************************************************************************************************************
fatal: [192.168.3.7]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Warning: Permanently added '192.168.3.7' (ECDSA) to the list of known hosts.\r\nPermission denied (publickey,gssapi-keyex,gssapi-with-mic,password).\r\n", "unreachable": true}

PLAY RECAP *******************************************************************************************************************************************
192.168.3.7                : ok=0    changed=0    unreachable=1    failed=0

[root@ansible_sv ~]#

エラーになってもうた。
確かに冷静に考えると、普通にsshコマンドで相手側サーバのユーザをrootにしたときにパスワードを聞かれるけど、前提条件で/etc/ansible/ansible.cfgの「ask_pass」をコメントアウトしちゃったもんだから、上記のansbile-playbookコマンドを実行したときはパスワード無しでアクセスしてるがために出てしまったエラー。

この場合ansible-playbookコマンドは「–ask-pass」オプションをつける。

[root@ansible_sv ~]# ansible-playbook /etc/ansible/yml/sample.yml --ask-pass
SSH password:

PLAY [ldapclient] ************************************************************************************************************************************

TASK [yum update] ************************************************************************************************************************************
ok: [192.168.3.7]

TASK [create txt] *********************************************************************************************************************************
changed: [192.168.3.7]

PLAY RECAP *******************************************************************************************************************************************
192.168.3.7                : ok=2    changed=1    unreachable=0    failed=0

[root@ansible_sv ~]#

いけました\(^o^)/

rootユーザしか使わないから–ask-passオプションをつけるのが面倒だという人は、/etc/ansible/ansible.cfgの「ask_pass」を再度生かせるように、コメントアウトを外せばOK。


②ansibleサーバ⇒rootユーザ、playbook実行先ユーザ⇒ldapユーザ

③ansibleサーバ⇒rootユーザ、playbook実行先ユーザ⇒モジュール毎にrootユーザまたはldapユーザを指定

は、省略。


④ansibleサーバ⇒ldapユーザ、playbook実行先ユーザ⇒rootユーザ

このパターンを行う場合、事前に以下の作業が必要。
・ansibleサーバにもldapクライアントを構築しておく。
・ansibleサーバにldapクライアントを構築した後、/home/<ldapユーザ名>/.ssh配下にssh秘密鍵を格納しておく。
AnsibleでOpenLDAP環境構築 -⑦ldapクライアント インストールか、LDAP構築 まとめを参照して頂けると幸い。

まずはansibleサーバにldapユーザでsshログインを行う。無事にログインでき、プロンプトが「[<ユーザ名>@<サーバ名> ~]$」となれば、ldapサーバからldapユーザの情報を受け取り、ldapユーザが使用できていることになる。

[user01@ansible_sv ~]$ 

ここまできたら、上記①のplaybookを再び使うことにする。

[user01@ansible_sv ~]$ sudo cat /etc/ansible/yml/sample.yml

あなたはシステム管理者から通常の講習を受けたはずです。
これは通常、以下の3点に要約されます:

    #1) 他人のプライバシーを尊重すること。
    #2) タイプする前に考えること。
    #3) 大いなる力には大いなる責任が伴うこと。

パスワード:
- hosts: ldapclient
  gather_facts: no
  remote_user: root

  tasks:
  - name: yum update
    yum:
      name=*
      state=present

  - name: create txt
    file:
      state=touch
      path=/home/user01/sample.txt
      owner=user01
      group=ldapmanager
      mode=0666
[user01@ansible_sv ~]$

初回にsudoを実行すると、上記の質問が出てくる。そのあとsudo実行用のパスワードを入力する。
ldapユーザを登録した際に、「userPassword:」モジュールで指定した文字列がsudo実行用のパスワードになる。本稿では「ldapuser01」となる。

①ansibleサーバ⇒rootユーザ、playbook実行先ユーザ⇒rootユーザとの差は、ansible-playbookコマンドをldapユーザで実行するということ。rootユーザで実行した場合と結果が異なるところに注目したい。

[user01@ansible_sv ~]$ ansible-playbook /etc/ansible/yml/sample.yml --ask-pass
SSH password:

PLAY [ldapclient] ************************************************************************************************************************************

TASK [yum update] *******************************************************************************************************************************

。。。

待てど暮らせどここから先に進まず。しょうがないのでCtrl+Cキーで脱出。すると、

[user01@ansible_sv ~]$ ansible-playbook /etc/ansible/yml/sample.yml --ask-pass
SSH password:

PLAY [ldapclient] ************************************************************************************************************************************

TASK [yum update] *******************************************************************************************************************************
^CProcess WorkerProcess-1:
Traceback (most recent call last):
  File "/usr/lib64/python2.7/multiprocessing/process.py", line 258, in _bootstrap
    self.run()
  File "/usr/lib/python2.7/site-packages/ansible/executor/process/worker.py", line 118, in run
    self._rslt_q
  File "/usr/lib/python2.7/site-packages/ansible/executor/task_executor.py", line 130, in run
    res = self._execute()
  File "/usr/lib/python2.7/site-packages/ansible/executor/task_executor.py", line 528, in _execute
    result = self._handler.run(task_vars=variables)
  File "/usr/lib/python2.7/site-packages/ansible/plugins/action/normal.py", line 45, in run
    results = merge_hash(results, self._execute_module(tmp=tmp, task_vars=task_vars, wrap_async=wrap_async))
  File "/usr/lib/python2.7/site-packages/ansible/plugins/action/__init__.py", line 642, in _execute_module
    tmp = self._make_tmp_path()
  File "/usr/lib/python2.7/site-packages/ansible/plugins/action/__init__.py", line 245, in _make_tmp_path
    tmpdir = self._remote_expand_user(self._play_context.remote_tmp_dir, sudoable=False)
  File "/usr/lib/python2.7/site-packages/ansible/plugins/action/__init__.py", line 549, in _remote_expand_user
    data = self._low_level_execute_command(cmd, sudoable=False)
  File "/usr/lib/python2.7/site-packages/ansible/plugins/action/__init__.py", line 889, in _low_level_execute_command
    rc, stdout, stderr = self._connection.exec_command(cmd, in_data=in_data, sudoable=sudoable)
  File "/usr/lib/python2.7/site-packages/ansible/plugins/connection/ssh.py", line 955, in exec_command
    (returncode, stdout, stderr) = self._run(cmd, in_data, sudoable=sudoable)
  File "/usr/lib/python2.7/site-packages/ansible/plugins/connection/ssh.py", line 240, in wrapped
    return_tuple = func(self, *args, **kwargs)
  File "/usr/lib/python2.7/site-packages/ansible/plugins/connection/ssh.py", line 853, in _run
    return self._bare_run(cmd, in_data, sudoable, checkrc)
  File "/usr/lib/python2.7/site-packages/ansible/plugins/connection/ssh.py", line 693, in _bare_run
    events = selector.select(timeout)
  File "/usr/lib/python2.7/site-packages/ansible/compat/selectors/_selectors2.py", line 482, in select
    maxevents=max_events)
  File "/usr/lib/python2.7/site-packages/ansible/compat/selectors/_selectors2.py", line 136, in _syscall_wrapper
    result = func(*args, **kwargs)
KeyboardInterrupt
 [ERROR]: User interrupted execution

[user01@ansible_sv ~]$

わっ、なんか出た 汗

見たところpython系エラーなんだけど、いちいち全部追うのは不毛すぎるので、ちらちら見てみると、

sudoable=False

という気になる表示があった。たしかにansible-playbookを実行するときにsudoはつけてない。
でもsudoをつけると、結局①ansibleサーバ⇒rootユーザ、playbook実行先ユーザ⇒rootユーザと一緒になるのでは。。。
と疑問を持ちながら、一応sudoつきで実行してみることにする。

[user01@ansible_sv ~]$ sudo ansible-playbook /etc/ansible/yml/sample.yml --ask-pass
パスワード:
SSH password:

PLAY [ldapclient] ************************************************************************************************************************************

TASK [yum update] ************************************************************************************************************************************
ok: [192.168.3.7]

TASK [create txt] *********************************************************************************************************************************
changed: [192.168.3.7]

PLAY RECAP *******************************************************************************************************************************************
192.168.3.7                : ok=2    changed=1    unreachable=0    failed=0

[user01@ansible_sv ~]$

思った通りでした ^^;
ただ面倒なことに、sudoをつけることによってsudoパスワードとrootパスワードの両方を聞かれることになってしまうという。
ldapユーザからの実行については、まだまだ改善の余地があるような気がする。


⑤ansibleサーバ⇒ldapユーザ、playbook実行先ユーザ⇒ldapユーザ

このパターンを行う場合も、④ansibleサーバ⇒ldapユーザ、playbook実行先ユーザ⇒rootユーザと同様に事前に以下の作業が必要。
・ansibleサーバにもldapクライアントを構築しておく。
・ansibleサーバにldapクライアントを構築した後、/home/<ldapユーザ名>/.ssh配下にssh秘密鍵を格納しておく。
AnsibleでOpenLDAP環境構築 -⑦ldapクライアント インストールか、LDAP構築 まとめを参照して頂けると幸い。

まずは、ansibleサーバにldapユーザでsshログインをする。無事にログインでき、プロンプトが「[<ユーザ名>@<サーバ名> ~]$」となれば、ldapサーバからldapユーザの情報を受け取り、ldapユーザが使用できていることになる。

[user01@ansible_sv ~]$ 

ldapユーザ(本稿ではuser01)でansible-playbookを実行するのだが、この場合①のplaybookを少し変える。
各モジュールをsudoで実行されるように、「become」「become_user」というモジュールを記述する。

[user01@ansible_sv ~]$ sudo cat /etc/ansible/yml/sample.yml
パスワード:
- hosts: ldapclient
  gather_facts: no
  become: yes
  become_user: root

  tasks:
  - name: yum update
    yum:
      name=*
      state=present

  - name: create txt
    file:
      state=touch
      path=/home/user01/sample.txt
      owner=user01
      group=ldapmanager
      mode=0666
[user01@ansible_sv ~]$

初回にsudoを実行すると、上記の質問が出てくる。そのあとsudo実行用のパスワードを入力する。
ldapユーザを登録した際に、「userPassword:」モジュールで指定した文字列がsudo実行用のパスワードになる。本稿では「ldapuser01」となる。

モジュールの説明。
「become: yes」は、モジュールに対しsudoで実行しますか?はい!という意味。
「become_user: root」は、sudoはどのユーザ権限を使用しますか?rootで!という意味。

becomeモジュールを記述することにより、それ以下の階層で実行するモジュールはすべてsudoつきで実行される。
通常linuxではsudo権限が与えられたユーザでsudoつきのlinuxコマンドを実行すると、sudoのパスワードを聞かれるようになる(/etc/sudoersでNOPASSWORDを指定すれば聞かれないが、本稿ではやらない)が、ansibleもbecomeモジュールを記述したplaybookを実行すると、それ以下の階層はもれなくsudoつきでモジュールが実行される。そのときにsudoパスワードが通っていないとansibleがエラーとなるので、パスワードを通す必要がある。
前提条件で/etc/ansible/ansible.cfgの「ask_sudo_pass」をコメントアウトしているので、普通にansible-playbookを実行してもsudoパスワードを聞かれることがなく、しかも実行結果で「”sudo: パスワー ドが必要です\r\n”」と表示されエラーとなる。

[user01@ansible_sv ~]$ ansible-playbook /etc/ansible/yml/sample.yml

PLAY [ldapclient] ************************************************************************************************************************************

TASK [yum update] ************************************************************************************************************************************
Enter passphrase for key '/home/user01/.ssh/id_ed25519':
fatal: [192.168.3.7]: FAILED! => {"changed": false, "module_stderr": "Shared connection to 192.168.3.7 closed.\r\n", "module_stdout": "sudo: パスワー ドが必要です\r\n", "msg": "MODULE FAILURE", "rc": 1}

PLAY RECAP *******************************************************************************************************************************************
192.168.3.7                : ok=0    changed=0    unreachable=0    failed=1

[user01@ansible_sv ~]$

このような場合は、「–ask-become-pass」オプションをつけると、sudoのパスワードを聞かれるようになる。

[user01@ansible_sv ~]$ ansible-playbook /etc/ansible/yml/sample.yml --ask-become-pass
SUDO password:

PLAY [ldapclient] ************************************************************************************************************************************

TASK [yum update] ************************************************************************************************************************************
Enter passphrase for key '/home/user01/.ssh/id_ed25519':
ok: [192.168.3.7]

TASK [create txt] *********************************************************************************************************************************
changed: [192.168.3.7]

PLAY RECAP *******************************************************************************************************************************************
192.168.3.7                : ok=2    changed=1    unreachable=0    failed=0

[user01@ansible_sv ~]$

ansible-playbookコマンドを実行直後、「SUDO password:」でsudoパスワードを聞かれる。ldapユーザに設定したuserPassword(ここではldapuser01)を入力すると、無事にansibleのタスクが全部通る。

ただしここでも、ansible-playbook実行中にssh公開鍵のパスフレーズを聞かれている。
今回使用しているplaybookはモジュールも少ないので大したことはないが、多数モジュールを組んだ場合そのモジュールを実行するごとにssh公開鍵のパスフレーズを聞かれたりするから、ものすごく面倒。

そこで、いったん自分の秘密鍵にパスフレーズを事前に登録することによって、連続でパスフレーズを入力しなければならない事態を回避することができる。
evalコマンドとssh-addコマンドを、以下のように実行する。

[user01@ansible_sv ~]$ eval "$(ssh-agent)"
Agent pid 3078
[user01@ansible_sv ~]$ ssh-add ~/.ssh/id_ed25519
Enter passphrase for /home/user01/.ssh/id_ed25519:
Identity added: /home/user01/.ssh/id_ed25519 (root@ldapserver)
[user01@ansible_sv ~]$

「Enter passphrase for ~」の箇所で、パスフレーズ(本稿ではsshmanager)を入力する。(root@ldapserver)は公開鍵から消すの忘れてた。。。害はないのでこのままにする。
これで、再度ansible-playbookコマンドを実行する。

[user01@ansible_sv ~]$ ansible-playbook /etc/ansible/yml/sample.yml --ask-become-pass
SUDO password:

PLAY [ldapclient] ************************************************************************************************************************************

TASK [yum update] ************************************************************************************************************************************
ok: [192.168.3.7]

TASK [create txt] *********************************************************************************************************************************
changed: [192.168.3.7]

PLAY RECAP *******************************************************************************************************************************************
192.168.3.7                : ok=2    changed=1    unreachable=0    failed=0

[user01@ansible_sv ~]$

「Enter passphrase for key ‘/home/user01/.ssh/id_ed25519’:」が出てこなくなりました\(^o^)/


⑥ansibleサーバ⇒ldapユーザ、playbook実行先ユーザ⇒モジュール毎にrootユーザまたはldapユーザを指定

このパターンを行う場合も、④ansibleサーバ⇒ldapユーザ、playbook実行先ユーザ⇒rootユーザと同様に事前に以下の作業が必要。
・ansibleサーバにもldapクライアントを構築しておく。
・ansibleサーバにldapクライアントを構築した後、/home/<ldapユーザ名>/.ssh配下にssh秘密鍵を格納しておく。
AnsibleでOpenLDAP環境構築 -⑦ldapクライアント インストールか、LDAP構築 まとめを参照して頂けると幸い。

user01でldapクライアントへsshログイン実行し、プロンプトがuser01ありになるところまでを確認。

[user01@ansible_sv ~]$ 

このパターンの場合、playbookをさらに以下の内容に変えてみた。

[user01@ansible_sv ~]$ sudo cat /etc/ansible/yml/sample.yml
パスワード:
- hosts: ldapclient
  gather_facts: no

  tasks:
  - name: yum update
    remote_user: root
    yum:
      name=*
      state=present

  - name: create txt 1
    become: yes
    become_user: root
    file:
      state=touch
      path=/root/sample1.txt
      owner=root
      group=root
      mode=0666

  - name: create txt 2
    file:
      state=touch
      path=/root/sample2.txt
      owner=root
      group=root
      mode=0666

  - name: create txt 3
    file:
      state=touch
      path=/home/user01/sample3.txt
      owner=root
      group=root
      mode=0666
[user01@ansible_sv ~]$

各nameモジュールでremote_userやbecomeをつけたりつけなかったりの構成。
「 – name: yum update」のyumモジュールはrootユーザにて実行。
「 – name: create txt 1」のfileモジュールはsudoつきで実行。一応rootユーザでないと操作できない/root配下にファイルを作るようにしている。
「 - name: create txt 2」のfileモジュールはremote_userもbecome/become_userもない状態でファイルを作る。/root配下にファイルを作るからどうなるか楽しみ。
「 – name: create txt 3」のfileモジュールはldapユーザのホームディレクトリにファイルを作る。これもremote_user,become/become_userモジュールがない状態。

パターン④⑤の教訓もあり、ここで使用するansible-playbookコマンドのオプションは「–ask-pass」と「–ask-become-pass」の両方と想定。
しかもsudoつきで実行してみることにする。

[user01@ansible_sv ~]$ sudo ansible-playbook /etc/ansible/yml/sample.yml --ask-pass --ask-become-pass
パスワード:
SSH password:
SUDO password[defaults to SSH password]:

PLAY [ldapclient] ************************************************************************************************************************************

TASK [yum update] ************************************************************************************************************************************
ok: [192.168.3.7]

TASK [create txt 1] **********************************************************************************************************************************
changed: [192.168.3.7]

TASK [create txt 2] **********************************************************************************************************************************
changed: [192.168.3.7]

TASK [create txt 3] **********************************************************************************************************************************
changed: [192.168.3.7]

PLAY RECAP *******************************************************************************************************************************************
192.168.3.7                : ok=4    changed=3    unreachable=0    failed=0

[user01@ansible_sv ~]$

一応うまくいったけど、ansible-playbookコマンド冒頭のsudoパスワードと、rootパスワードと、playbook上のbecomeモジュール用にさらにsudoパスワードと。。。
一回コマンド打つのにどんだけパスワード打たすねん!て感じ。

実は、ansible-playbookコマンドをオプション変えたりしていろいろ試してみたけど、–ask-become-passはなくても行けました。

[user01@ansible_sv ~]$ sudo ansible-playbook /etc/ansible/yml/sample.yml --ask-pass
パスワード:
SSH password:

PLAY [ldapclient] ************************************************************************************************************************************

TASK [yum update] ************************************************************************************************************************************
ok: [192.168.3.7]

TASK [create txt 1] **********************************************************************************************************************************
changed: [192.168.3.7]

TASK [create txt 2] **********************************************************************************************************************************
changed: [192.168.3.7]

TASK [create txt 3] **********************************************************************************************************************************
changed: [192.168.3.7]

PLAY RECAP *******************************************************************************************************************************************
192.168.3.7                : ok=4    changed=3    unreachable=0    failed=0

[user01@ansible_sv ~]$

まぁ、sudoつきでansible-playbookコマンドを実行しているので、結局rootユーザでansible-playbookコマンドを実行しているのと変わんないんだよね。。
rootユーザでbecomeつけても意味がない(root権限でモジュールを実行しているのと同じになる)ので、本来ldapユーザでは扱えない「 - name: create txt 2」の/rootディレクトリにもファイルを作ることができる。

 

言えること

①~⑥パターンをやってみて言えること。
・ansible-playbookコマンドを実行する場合は、playbookでremote_userやbecome/become_userがどのように割り当てられているか確認し、–ask-passや–ask-become-passオプションで権限クリアできるようにしておく。
・ansibleサーバ側はrootユーザか、もしくはsudoつきにしてansible-playbookコマンドを実行するとよい(かも)。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA