Soluzione 1:
Ho delle macchine virtuali, facciamone saltare in aria un mucchio! Per la scienza.
[[email protected] ~]# ansible --version
ansible 2.0.1.0
config file = /etc/ansible/ansible.cfg
configured module search path = Default w/o overrides
Primo tentativo:
[[email protected] ~]# cat killme.yml
---
- hosts: localhost
gather_facts: False
tasks:
- name: Die in a fire
command: "rm -rf {x}/{y}"
[[email protected] ~]# 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, allora command
si limita a trasmettere i valori letterali e non accade nulla.
Che ne dici del nostro bypass di sicurezza preferito, raw
?
[[email protected] ~]# cat killme.yml
---
- hosts: localhost
gather_facts: False
tasks:
- name: Die in a fire
raw: "rm -rf {x}/{y}"
[[email protected] ~]# 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
Non andare di nuovo! Quanto può essere difficile eliminare tutti i tuoi file?
Oh, ma se fossero variabili non definite o qualcosa del genere?
[[email protected] ~]# cat killme.yml
---
- hosts: localhost
gather_facts: False
tasks:
- name: Die in a fire
command: "rm -rf {{x}}/{{y}}"
[[email protected] ~]# 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
Beh, non ha funzionato.
Ma cosa succede se le variabili sono definite, ma vuote?
[[email protected] ~]# cat killme.yml
---
- hosts: localhost
gather_facts: False
tasks:
- name: Die in a fire
command: "rm -rf {{x}}/{{y}}"
vars:
x: ""
y: ""
[[email protected] ~]# 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
Finalmente qualche progresso! Ma continua a lamentarsi del fatto che non ho usato --no-preserve-root
.
Ovviamente, mi avverte anche che dovrei provare a usare il file
modulo e state=absent
. Vediamo se funziona.
[[email protected] ~]# cat killme.yml
---
- hosts: localhost
gather_facts: False
tasks:
- name: Die in a fire
file: path="{{x}}/{{y}}" state=absent
vars:
x: ""
y: ""
[[email protected] ~]# 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
Buone notizie, tutti! Ha iniziato a provare per eliminare tutti i miei file! Ma sfortunatamente si è verificato un errore. Lascerò risolverlo e ottenere il playbook per distruggere tutto usando il file
modulo come esercizio per il lettore.
NON eseguire nessun playbook che vedi oltre questo punto! Vedrai perché tra un attimo.
Infine, per il colpo di grazia ...
[[email protected] ~]# cat killme.yml
---
- hosts: localhost
gather_facts: False
tasks:
- name: Die in a fire
raw: "rm -rf {{x}}/{{y}}"
vars:
x: ""
y: "*"
[[email protected] ~]# 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
Questa VM è un ex pappagallo!
È interessante notare che quanto sopra non è riuscito a fare nulla con command
invece di raw
. Ha appena stampato lo stesso avviso sull'uso di file
con state=absent
.
Dirò che sembra che se non stai usando raw
che esiste una certa protezione da rm
impazzito. Non dovresti fare affidamento su questo, però. Ho dato una rapida occhiata al codice di Ansible e, anche se ho trovato l'avviso, non ho trovato nulla che potesse effettivamente sopprimere l'esecuzione di rm
comando.
Soluzione 2:
Ansible impedirà l'esecuzione di rm -rf /
in uno script di shell?
Ho ispezionato il sorgente coreutils rm, che contiene quanto segue:
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 ("/"));
}
L'unico modo per cancellare dalla radice è superare questo blocco di codice. Da questa fonte:
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;
}
Interpreto questo nel senso che la funzione get_root_dev_ino
restituisce null su /
, e quindi rm fallisce.
L'unico modo per aggirare il primo blocco di codice (con ricorsione) è avere --no-preserve-root
e non usa una variabile d'ambiente per eseguire l'override, quindi dovrebbe essere passato esplicitamente a rm.
Credo che questo dimostri che, a meno che Ansible non passi esplicitamente --no-preserve-root
a rm
, non lo farà.
Conclusione
Non credo che Ansible impedisca esplicitamente rm -rf /
perché rm
stesso lo impedisce.