AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / server / 问题 / 771598
Accepted
aroth
aroth
Asked: 2016-04-20 20:48:48 +0800 CST2016-04-20 20:48:48 +0800 CST 2016-04-20 20:48:48 +0800 CST

Ansible 会阻止在 shell 脚本中执行“rm -rf /”吗

  • 772

这是基于这里的这个恶作剧问题。所描述的问题是有一个 bash 脚本,其中包含以下内容:

rm -rf {pattern1}/{pattern2}

rm -rf /...如果两种模式都包含一个或多个空元素,则假设原始命令已正确转录并且 OP 正在执行大括号扩展而不是参数扩展,则该模式将扩展为 的至少一个实例。

在 OP对骗局的解释中,他指出:

命令 [...] 是无害的,但似乎几乎没有人注意到。

Ansible 工具可以防止这些错误,[...] 但是 [...] 似乎没有人知道这一点,否则他们会知道我所描述的不可能发生。

rm -rf /因此,假设您有一个通过大括号扩展或参数扩展发出命令的 shell 脚本,使用Ansible是否会阻止该命令被执行,如果是,它是如何做到的?

rm -rf /只要您使用 Ansible 来执行,以 root 权限执行真的“无害”吗?

linux bash ansible rm
  • 2 2 个回答
  • 5523 Views

2 个回答

  • Voted
  1. Best Answer
    Michael Hampton
    2016-04-20T21:18:46+08:002016-04-20T21:18:46+08:00

    我有虚拟机,让我们炸毁一堆!为了科学。

    [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
    

    好的,所以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.

    当然,它也警告我应该尝试使用file模块和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模块作为练习来破坏所有内容给读者。


    不要运行任何你看到的超出这一点的剧本!你马上就会明白为什么。

    最后,为了致命一击......

    [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
    

    这个虚拟机是一只前鹦鹉!

    有趣的是,上面没有做任何事情,command而不是raw. file它只是打印了关于使用with的相同警告state=absent。

    我要说的是,如果你不使用它,似乎raw有一些保护可以防止rm失控。但是,您不应该依赖于此。我快速浏览了 Ansible 的代码,虽然我发现了警告,但我没有发现任何实际上会抑制运行该rm命令的内容。

    • 54
  2. Aaron Hall
    2016-04-22T04:56:42+08:002016-04-22T04:56:42+08:00

    Ansible 会阻止rm -rf /在 shell 脚本中执行吗?

    我确实检查了 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 ("/"));
        }
    

    从根目录擦除的唯一方法是越过此代码块。从这个来源:

    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 on /,因此 rm 失败。

    绕过第一个代码块(使用递归)的唯一方法是拥有--no-preserve-root并且它不使用环境变量来覆盖,因此它必须显式传递给 rm。

    我相信这证明除非 Ansible 明确传递--no-preserve-root给rm,否则它不会这样做。

    结论

    我不相信 Ansible 明确阻止rm -rf /,因为rm它本身阻止了它。

    • 3

相关问题

  • 多操作系统环境的首选电子邮件客户端

  • 你最喜欢的 Linux 发行版是什么?[关闭]

  • 更改 PHP 的默认配置设置?

  • 保护新的 Ubuntu 服务器 [关闭]

  • (软)Ubuntu 7.10 上的 RAID 6,我应该迁移到 8.10 吗?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    新安装后 postgres 的默认超级用户用户名/密码是什么?

    • 5 个回答
  • Marko Smith

    SFTP 使用什么端口?

    • 6 个回答
  • Marko Smith

    命令行列出 Windows Active Directory 组中的用户?

    • 9 个回答
  • Marko Smith

    什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同?

    • 3 个回答
  • Marko Smith

    如何确定bash变量是否为空?

    • 15 个回答
  • Martin Hope
    Tom Feiner 如何按大小对 du -h 输出进行排序 2009-02-26 05:42:42 +0800 CST
  • Martin Hope
    Noah Goodrich 什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同? 2009-05-19 18:24:42 +0800 CST
  • Martin Hope
    Brent 如何确定bash变量是否为空? 2009-05-13 09:54:48 +0800 CST
  • Martin Hope
    cletus 您如何找到在 Windows 中打开文件的进程? 2009-05-01 16:47:16 +0800 CST

热门标签

linux nginx windows networking ubuntu domain-name-system amazon-web-services active-directory apache-2.4 ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve