これは、この デマの質問 に基づいています。説明されている問題は、次のような影響を含むbashスクリプトがあることです。
rm -rf {pattern1}/{pattern2}
...どちらのパターンにも1つ以上の空の要素が含まれている場合、元のコマンドが正しく書き起こされ、OPが 括弧を実行していると想定して、rm -rf /
の少なくとも1つのインスタンスに展開されます。パラメータ展開ではなく展開 。
OPの デマの説明 で、彼は次のように述べています。
コマンド[...]は無害ですが、誰も気づいていないようです。
Ansibleツールはこれらのエラーを防ぎます[...]が、[...]だれもそれを知っているようには見えませんでした。
したがって、ブレース展開またはパラメーター展開のいずれかによってrm -rf /
コマンドを発行するシェルスクリプトがあると仮定すると、 Ansible を使用すると、そのコマンドが実行されないようにします。実行されている場合、どのように実行しますか?
Ansibleを使用している限り、root権限でrm -rf /
を実行しても本当に「無害」ですか?
私は仮想マシンを持っています、それらの束を爆破しましょう!科学について。
[root@diaf ~]# ansible --version
ansible 2.0.1.0
config file = /etc/ansible/ansible.cfg
configured module search path = Default w/o overrides
最初の試み:
[root@diaf ~]# cat killme.yml
---
- hosts: localhost
gather_facts: False
tasks:
- name: Die in a fire
command: "rm -rf {x}/{y}"
[root@diaf ~]# ansible-playbook -l localhost -vvv killme.yml
Using /etc/ansible/ansible.cfg as config file
1 plays in killme.yml
PLAY ***************************************************************************
TASK [Die in a fire] ***********************************************************
task path: /root/killme.yml:5
ESTABLISH LOCAL CONNECTION FOR USER: root
localhost EXEC /bin/sh -c '( umask 22 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1461128819.56-86533871334374 `" && echo "` echo $HOME/.ansible/tmp/ansible-tmp-1461128819.56-86533871334374 `" )'
localhost PUT /tmp/tmprogfhZ TO /root/.ansible/tmp/ansible-tmp-1461128819.56-86533871334374/command
localhost EXEC /bin/sh -c 'LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /root/.ansible/tmp/ansible-tmp-1461128819.56-86533871334374/command; rm -rf "/root/.ansible/tmp/ansible-tmp-1461128819.56-86533871334374/" > /dev/null 2>&1'
changed: [localhost] => {"changed": true, "cmd": ["rm", "-rf", "{x}/{y}"], "delta": "0:00:00.001844", "end": "2016-04-20 05:06:59.601868", "invocation": {"module_args": {"_raw_params": "rm -rf {x}/{y}", "_uses_Shell": false, "chdir": null, "creates": null, "executable": null, "removes": null, "warn": true}, "module_name": "command"}, "rc": 0, "start": "2016-04-20 05:06:59.600024", "stderr": "", "stdout": "", "stdout_lines": [], "warnings": ["Consider using file module with state=absent rather than running rm"]}
[WARNING]: Consider using file module with state=absent rather than running rm
PLAY RECAP *********************************************************************
localhost : ok=1 changed=1 unreachable=0 failed=0
OK、つまりcommand
はリテラルを渡すだけで、何も起こりません。
私たちのお気に入りの安全バイパス raw
はどうですか?
[root@diaf ~]# cat killme.yml
---
- hosts: localhost
gather_facts: False
tasks:
- name: Die in a fire
raw: "rm -rf {x}/{y}"
[root@diaf ~]# ansible-playbook -l localhost -vvv killme.yml
Using /etc/ansible/ansible.cfg as config file
1 plays in killme.yml
PLAY ***************************************************************************
TASK [Die in a fire] ***********************************************************
task path: /root/killme.yml:5
ESTABLISH LOCAL CONNECTION FOR USER: root
localhost EXEC rm -rf {x}/{y}
ok: [localhost] => {"changed": false, "invocation": {"module_args": {"_raw_params": "rm -rf {x}/{y}"}, "module_name": "raw"}, "rc": 0, "stderr": "", "stdout": "", "stdout_lines": []}
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0
もう行きません!すべてのファイルを削除するのはどれほど難しいでしょうか?
ああ、でもそれらが未定義の変数か何かだったらどうでしょうか?
[root@diaf ~]# cat killme.yml
---
- hosts: localhost
gather_facts: False
tasks:
- name: Die in a fire
command: "rm -rf {{x}}/{{y}}"
[root@diaf ~]# ansible-playbook -l localhost -vvv killme.yml
Using /etc/ansible/ansible.cfg as config file
1 plays in killme.yml
PLAY ***************************************************************************
TASK [Die in a fire] ***********************************************************
task path: /root/killme.yml:5
fatal: [localhost]: FAILED! => {"failed": true, "msg": "'x' is undefined"}
NO MORE HOSTS LEFT *************************************************************
to retry, use: --limit @killme.retry
PLAY RECAP *********************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1
まあ、それはうまくいきませんでした。
しかし、変数が定義されているが空の場合はどうでしょうか?
[root@diaf ~]# cat killme.yml
---
- hosts: localhost
gather_facts: False
tasks:
- name: Die in a fire
command: "rm -rf {{x}}/{{y}}"
vars:
x: ""
y: ""
[root@diaf ~]# ansible-playbook -l localhost -vvv killme.yml
Using /etc/ansible/ansible.cfg as config file
1 plays in killme.yml
PLAY ***************************************************************************
TASK [Die in a fire] ***********************************************************
task path: /root/killme.yml:5
ESTABLISH LOCAL CONNECTION FOR USER: root
localhost EXEC /bin/sh -c '( umask 22 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1461129132.63-211170666238105 `" && echo "` echo $HOME/.ansible/tmp/ansible-tmp-1461129132.63-211170666238105 `" )'
localhost PUT /tmp/tmp78m3WM TO /root/.ansible/tmp/ansible-tmp-1461129132.63-211170666238105/command
localhost EXEC /bin/sh -c 'LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /root/.ansible/tmp/ansible-tmp-1461129132.63-211170666238105/command; rm -rf "/root/.ansible/tmp/ansible-tmp-1461129132.63-211170666238105/" > /dev/null 2>&1'
fatal: [localhost]: FAILED! => {"changed": true, "cmd": ["rm", "-rf", "/"], "delta": "0:00:00.001740", "end": "2016-04-20 05:12:12.668616", "failed": true, "invocation": {"module_args": {"_raw_params": "rm -rf /", "_uses_Shell": false, "chdir": null, "creates": null, "executable": null, "removes": null, "warn": true}, "module_name": "command"}, "rc": 1, "start": "2016-04-20 05:12:12.666876", "stderr": "rm: it is dangerous to operate recursively on ‘/’\nrm: use --no-preserve-root to override this failsafe", "stdout": "", "stdout_lines": [], "warnings": ["Consider using file module with state=absent rather than running rm"]}
NO MORE HOSTS LEFT *************************************************************
to retry, use: --limit @killme.retry
PLAY RECAP *********************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1
最後に、いくつかの進歩!しかし、それでも--no-preserve-root
を使用しなかったという不満があります。
もちろん、 the file
module とstate=absent
を試してみる必要があることも警告しています。うまくいくか見てみましょう。
[root@diaf ~]# cat killme.yml
---
- hosts: localhost
gather_facts: False
tasks:
- name: Die in a fire
file: path="{{x}}/{{y}}" state=absent
vars:
x: ""
y: ""
[root@diaf ~]# ansible-playbook -l localhost -vvv killme.yml
Using /etc/ansible/ansible.cfg as config file
1 plays in killme.yml
PLAY ***************************************************************************
TASK [Die in a fire] ***********************************************************
task path: /root/killme.yml:5
ESTABLISH LOCAL CONNECTION FOR USER: root
localhost EXEC /bin/sh -c '( umask 22 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1461129394.62-191828952911388 `" && echo "` echo $HOME/.ansible/tmp/ansible-tmp-1461129394.62-191828952911388 `" )'
localhost PUT /tmp/tmpUqLzyd TO /root/.ansible/tmp/ansible-tmp-1461129394.62-191828952911388/file
localhost EXEC /bin/sh -c 'LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /root/.ansible/tmp/ansible-tmp-1461129394.62-191828952911388/file; rm -rf "/root/.ansible/tmp/ansible-tmp-1461129394.62-191828952911388/" > /dev/null 2>&1'
fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "invocation": {"module_args": {"backup": null, "content": null, "delimiter": null, "diff_peek": null, "directory_mode": null, "follow": false, "force": false, "group": null, "mode": null, "original_basename": null, "owner": null, "path": "/", "recurse": false, "regexp": null, "remote_src": null, "selevel": null, "serole": null, "setype": null, "seuser": null, "src": null, "state": "absent", "validate": null}, "module_name": "file"}, "msg": "rmtree failed: [Errno 16] Device or resource busy: '/boot'"}
NO MORE HOSTS LEFT *************************************************************
to retry, use: --limit @killme.retry
PLAY RECAP *********************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1
皆さん、朗報です! すべてのファイルを削除する試行が始まりました!しかし、残念ながらエラーが発生しました。私はそれを修正して、読者への演習としてfile
モジュールを使用して、プレイブックにすべてを破壊させることにします。
この時点以降に表示されるプレイブックを実行しないでください!理由はすぐにわかります。
最後に、coup degrâce...
[root@diaf ~]# cat killme.yml
---
- hosts: localhost
gather_facts: False
tasks:
- name: Die in a fire
raw: "rm -rf {{x}}/{{y}}"
vars:
x: ""
y: "*"
[root@diaf ~]# ansible-playbook -l localhost -vvv killme.yml
Using /etc/ansible/ansible.cfg as config file
1 plays in killme.yml
PLAY ***************************************************************************
TASK [Die in a fire] ***********************************************************
task path: /root/killme.yml:5
ESTABLISH LOCAL CONNECTION FOR USER: root
localhost EXEC rm -rf /*
Traceback (most recent call last):
File "/usr/lib/python2.7/site-packages/ansible/executor/process/result.py", line 102, in run
File "/usr/lib/python2.7/site-packages/ansible/executor/process/result.py", line 76, in _read_worker_result
File "/usr/lib64/python2.7/multiprocessing/queues.py", line 117, in get
ImportError: No module named task_result
これはVM is an ex-parrot !
興味深いことに、上記ではcommand
ではなくraw
を使用して何もできませんでした。 file
をstate=absent
とともに使用することに関する同じ警告が出力されました。
raw
を使用していない場合、rm
からの何らかの保護が機能しなくなっているように思われます。 しかし、これに依存するべきではありません。私はAnsibleのコードをざっと見ましたが、警告を見つけましたが、何も見つかりませんでした実際にはrm
コマンドの実行を抑制します。
rm -rf /
の実行を妨げますか?私はcoreutils rm source を検査しました。
if (x.recursive && preserve_root)
{
static struct dev_ino dev_ino_buf;
x.root_dev_ino = get_root_dev_ino (&dev_ino_buf);
if (x.root_dev_ino == NULL)
error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
quoteaf ("/"));
}
ルートからをワイプする唯一の方法は、このコードブロックを通過することです。 this source から:
struct dev_ino *
get_root_dev_ino (struct dev_ino *root_d_i)
{
struct stat statbuf;
if (lstat ("/", &statbuf))
return NULL;
root_d_i->st_ino = statbuf.st_ino;
root_d_i->st_dev = statbuf.st_dev;
return root_d_i;
}
これは、関数get_root_dev_ino
が/
でnullを返すため、rmが失敗することを意味すると解釈します。
最初のコードブロックを(再帰で)バイパスする唯一の方法は、--no-preserve-root
を使用することであり、環境変数を使用してオーバーライドしないため、明示的にrmに渡す必要があります。
これは、Ansibleが--no-preserve-root
をrm
に明示的に渡さない限りこれを行わないことを証明していると思います。
rm
自体がそれを防止するため、Ansibleがrm -rf /
を明示的に防止するとは思いません。