GNU/Linux >> Znalost Linux >  >> Linux

Zabrání Ansible spuštění 'rm -rf /' ve skriptu shellu

Řešení 1:

Mám virtuální stroje, pojďme jich vyhodit do vzduchu! Pro vědu.

[[email protected] ~]# ansible --version
ansible 2.0.1.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides

První pokus:

[[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

Dobře, takže command stačí předat doslovy a nic se nestane.

Co takhle náš oblíbený bezpečnostní bypass, 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

Už znovu ne! Jak těžké může být smazat všechny vaše soubory?

Oh, ale co když to byly nedefinované proměnné nebo tak něco?

[[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

No, to nefungovalo.

Ale co když jsou proměnné definované, ale prázdné?

[[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

Konečně nějaký pokrok! Ale stále si stěžuje, že jsem nepoužil --no-preserve-root .

Samozřejmě mě to také varuje, že bych měl zkusit použít file modul a state=absent . Uvidíme, jestli to bude fungovat.

[[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

Dobré zprávy, všichni! Začalo to pokoušet se smazat všechny moje soubory! Ale bohužel došlo k chybě. Nechám to opravit a nechat playbook zničit vše pomocí file modul jako cvičení pro čtenáře.

NEPOUŽÍVEJTE žádné příručky, které vidíte za tímto bodem! Za chvíli uvidíte proč.

A konečně pro 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

Tento VM je bývalý papoušek!

Je zajímavé, že výše uvedenému se nepodařilo nic udělat s command místo raw . Právě to vypsalo stejné varování o použití file s state=absent .

Říkám, že to vypadá, že pokud nepoužíváte raw že existuje určitá ochrana před rm zbláznil se. Na to byste však neměli spoléhat. Rychle jsem se podíval do kódu Ansible, a když jsem našel varování, nenašel jsem nic, co by ve skutečnosti potlačilo spuštění rm příkaz.

Řešení 2:

Bude Ansible bránit spuštění rm -rf / ve skriptu shellu?

Zkontroloval jsem zdroj coreutils rm, který má následující:

  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 ("/"));
    }

Jediný způsob, jak vymazat z kořenového adresáře je dostat se přes tento blok kódu. Z tohoto zdroje:

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;
}

Interpretuji to tak, že funkce get_root_dev_ino vrátí hodnotu null na / , a proto rm selže.

Jediný způsob, jak obejít první blok kódu (s rekurzí), je mít --no-preserve-root a nepoužívá proměnnou prostředí k přepsání, takže by musela být předána explicitně rm.

Věřím, že to dokazuje, že pokud Ansible výslovně neprojde --no-preserve-root na rm , to neudělá.

Závěr

Nevěřím, že Ansible výslovně brání rm -rf / protože rm sám tomu brání.


Linux
  1. [Linux] :7 věcí, které se naučíte, když začnete psát jednoduchý skript Shell

  2. [Linux]:Jak zabránit náhodnému spuštění příkazu „rm -rf“.

  3. Proč „sudo Su“ ve skriptu Shell nespustí zbytek skriptu jako kořen?

  1. Jak zašifrovat nebo vykreslit skript Shell jako nečitelný?

  2. Shell Script Wrapper zabránit spuštění příkazu bez argumentů?

  3. Získejte čas provádění programu v shellu

  1. Použití příkazu passwd ze skriptu shellu

  2. Jaký je rozdíl mezi prováděním skriptu Bash a jeho získáváním?

  3. Může být proces init shell skript v Linuxu?