Lösung 1:
Ich habe virtuelle Maschinen, lassen Sie uns ein paar davon in die Luft jagen! Für die Wissenschaft.
[[email protected] ~]# ansible --version
ansible 2.0.1.0
config file = /etc/ansible/ansible.cfg
configured module search path = Default w/o overrides
Erster Versuch:
[[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, also command
gibt die Literale einfach weiter und nichts passiert.
Wie wäre es mit unserer bevorzugten Sicherheitsüberbrückung, 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
Nie wieder! Wie schwer kann es sein, alle Ihre Dateien zu löschen?
Oh, aber was wäre, wenn es undefinierte Variablen oder so etwas wären?
[[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
Nun, das hat nicht funktioniert.
Was aber, wenn die Variablen zwar definiert, aber leer sind?
[[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
Endlich ein Fortschritt! Aber es beschwert sich immer noch, dass ich --no-preserve-root
nicht verwendet habe .
Natürlich warnt es mich auch, dass ich es mit file
versuchen sollte Modul und state=absent
. Mal sehen, ob das funktioniert.
[[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
Gute Nachrichten, Leute! Es fing an versuchen um alle meine Dateien zu löschen! Aber leider ist dabei ein Fehler aufgetreten. Ich überlasse es, das zu beheben und das Playbook dazu zu bringen, alles mit dem file
zu zerstören Modul als Übung für den Leser.
Führen Sie KEINE Playbooks aus, die Sie über diesen Punkt hinaus sehen! Sie werden gleich sehen, warum.
Endlich, für den coup de grâce ...
[[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
Diese VM ist ein Ex-Papagei!
Interessanterweise hat das obige nichts mit command
zu tun statt raw
. Es hat nur dieselbe Warnung zur Verwendung von file
ausgegeben mit state=absent
.
Ich werde sagen, dass es so aussieht, als ob Sie raw
nicht verwenden dass es einen gewissen Schutz vor rm
gibt Amok gegangen. Darauf sollten Sie sich jedoch nicht verlassen. Ich habe den Code von Ansible schnell durchgesehen, und obwohl ich die Warnung gefunden habe, habe ich nichts gefunden, was die Ausführung von rm
tatsächlich unterdrücken würde Befehl.
Lösung 2:
Wird Ansible die Ausführung von rm -rf /
verhindern in einem Shell-Skript?
Ich habe die Coreutils-RM-Quelle untersucht, die Folgendes enthält:
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 ("/"));
}
Die einzige Möglichkeit, von der Wurzel aus zu löschen ist es, diesen Codeblock zu überwinden. Aus dieser Quelle:
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;
}
Ich interpretiere das so, dass die Funktion get_root_dev_ino
gibt bei /
null zurück , und somit schlägt rm fehl.
Die einzige Möglichkeit, den ersten Codeblock (mit Rekursion) zu umgehen, ist --no-preserve-root
und es verwendet keine Umgebungsvariable zum Überschreiben, also müsste es explizit an rm übergeben werden.
Ich glaube, dies beweist, dass Ansible nicht ausdrücklich --no-preserve-root
übergibt bis rm
, wird dies nicht geschehen.
Schlussfolgerung
Ich glaube nicht, dass Ansible explizit rm -rf /
verhindert weil rm
selbst verhindert es.