ansible を使ったことがある人ならおそらくみんなハマったであろう PATH 問題についてまとめる。
こんなやつ
- shell: echo $PATH
EXEC /bin/sh -c 'sudo -H -S -n -u root /bin/sh -c '"'"'echo BECOME-SUCCESS-lzymqihsegohzfxrfdinmivkxocjkfsb; /usr/bin/python'"'"' && sleep 0'
changed: [centos] => {
(中略)
"stdout": "/sbin:/bin:/usr/sbin:/usr/bin",
"stdout_lines": [
"/sbin:/bin:/usr/sbin:/usr/bin"
]
}
あれ?となって確認した。
$ sudo su -
# echo $PATH
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
何が起きているか?
これは ansible の問題ではなく、 sudo, su の仕様の話。
- デフォルトの実行コマンドがログインシェルを使っていないので
.bash_profile
,.bashrc
などの設定ファイルは読み込まない。 - sudo で参照する先が root の PATH ではない。
/etc/sudoers
というファイルに
Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin
という記述があり、 セキュリティ確保のために最低限の PATH のみを参照するようになっている。
visudo
コマンドを使って/etc/sudoers
を編集すれば解決する。
(参考) 実行コマンドと PATH の違い
$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/vagrant/.local/bin:/home/vagrant/bin
$ printenv
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/vagrant/.local/bin:/home/vagrant/bin
********************************************************************************
$ sudo su
# echo $PATH
/sbin:/bin:/usr/sbin:/usr/bin
$ sudo su
# printenv
/sbin:/bin:/usr/sbin:/usr/bin
********************************************************************************
$ sudo su -
# echo $PATH
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
$ sudo su -
# printenv
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
********************************************************************************
$ sudo echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/vagrant/.local/bin:/home/vagrant/bin
$ sudo printenv
/sbin:/bin:/usr/sbin:/usr/bin
********************************************************************************
$ sudo -E echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/vagrant/.local/bin:/home/vagrant/bin
$ sudo -E printenv
/sbin:/bin:/usr/sbin:/usr/bin
********************************************************************************
$ sudo -i echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/vagrant/.local/bin:/home/vagrant/bin
$ sudo -i printenv
/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
********************************************************************************
$ sudo -s echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/vagrant/.local/bin:/home/vagrant/bin
$ sudo -s printenv
/sbin:/bin:/usr/sbin:/usr/bin
echo $PATH
と printenv
の挙動が違うのがなんだろう、気になる
解決方法
.bash_profile
, .bashrc
などを読み込ませるにはログインシェルで実行する必要がある
[解決策①] ansible 全体でログインシェルを使うよう記述する
ansible.cfg に以下のように記述する
[defaults]
executable = /bin/bash -l
- shell: echo $PATH
<centos> EXEC /bin/bash -l -c 'sudo -H -S -n -u root /bin/bash -l -c '"'"'echo BECOME-SUCCESS-oqlyupjtmruqutxhwwikvwvdzyhkqhzi; /usr/bin/python'"'"' && sleep 0'
"stdout": "/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin"
[解決策②] コマンド自体にログインシェルを使うよう記述する
- shell: /bin/bash -l -c 'echo $PATH'
EXEC /bin/sh -c 'sudo -H -S -n -u root /bin/sh -c '"'"'echo BECOME-SUCCESS-orthrhxlvhyzidtypbrafkgurhiziccv; /usr/bin/python'"'"' && sleep 0'
"stdout": "/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin"
[解決策③] 自分で PATH に追加する
- shell: echo $PATH
environment:
PATH: "/opt:{{ ansible_env.PATH }}"
EXEC /bin/sh -c 'sudo -H -S -n -u root /bin/sh -c '"'"'echo BECOME-SUCCESS-dqterilpwfhgbmbdsizvyanxvetoxonl; PATH=/opt:/sbin:/bin:/usr/sbin:/usr/bin /usr/bin/python'"'"' && sleep 0'
"stdout": "/opt:/sbin:/bin:/usr/sbin:/usr/bin"
色々と調べたけどコントリビューターの人が↓のように言っていた。
While I understand the underlying issues here Ansible is intended to be a centrally managed automation system.
The login shell of the user is not to be something that is required for managing the environment.
https://github.com/ansible/ansible/issues/4854#issuecomment-37657751
確かに複数サーバーを同時に扱ったりするからユーザーが好き勝手に変えれる部分を鵜呑みにするとアレかなーと思ったりした。
ってことで解決策③を使うのが ansible の思想に沿っているっぽい。 sudoers も編集しなくて良いし。
失敗例
[失敗例①]
- shell: echo hello
args:
executable: /bin/bash -l
EXEC /bin/sh -c 'sudo -H -S -n -u root /bin/sh -c '"'"'echo BECOME-SUCCESS-diybjbffvistlrsppnykceegqswzygxp; /usr/bin/python'"'"' && sleep 0'
同じ executable だからいけるのでは!?と思ったけどダメだった。
オプションまで含めて1つのコマンドとして扱われるらしく、 [Errno 2] No such file or directory
というエラーになる。
実行時のコマンドには変化がないので、実行ファイル内で呼び出される部分が変わるっぽい?
$ /bin/bash echo hello
/usr/bin/echo: /usr/bin/echo: cannot execute binary file
$ /bin/bash -c "echo hello"
hello
$ "/bin/bash" -c "echo hello"
hello
$ "/bin/bash -l" -c "echo hello" 👈 これ
-bash: /bin/bash -l: No such file or directory
[失敗例②]
- shell: source ~/.bashrc && echo $PATH
EXEC /bin/sh -c 'sudo -H -S -n -u root /bin/sh -c '"'"'echo BECOME-SUCCESS-ouzvwbrjctdmvcgmpgrkqfhresukbaqr; /usr/bin/python'"'"' && sleep 0'
"stdout": "/sbin:/bin:/usr/sbin:/usr/bin"
参考URL
- http://www.bunkei-programmer.net/entry/2015/05/16/162020
- https://qiita.com/FGtatsuro/items/2366c93131c47aef8dfe
- http://boscono.hatenablog.com/entry/2016/04/16/135753
- https://teratail.com/questions/29651