Ansible 学习总结—— Ansible 控制提权相关知识总结

Posted 科技D人生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ansible 学习总结—— Ansible 控制提权相关知识总结相关的知识,希望对你有一定的参考价值。

前言

Ansible 版本

zhanghaiyang@zhydesktop:/$ ansible --version
[WARNING]: log file at /var/log/ansible.log is not writeable and we cannot create it, aborting

ansible 2.9.6
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/zhanghaiyang/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.8.5 (default, Jan 27 2021, 15:41:15) [GCC 9.3.0]
zhanghaiyang@zhydesktop:/$

对于初学 ansible 的小伙伴提权配置大都是通过配置 ansible.cfg 的方式来配置提权,通过配置的文件的方式配置的提权,对所有执行的剧本角色有提权,这样的好处是,简单方便,但是有一定的风险,任何命令都通用过 root 来执行,即任何进程都是具有系统的最高权限,对于黑客来讲,最想得到的即 root 权限,如果进程被植入了木马病毒之类,控制了进程即拥有 root 权限。所以任何命令通过 root 来执行是一件很危险的事。所以从安全角度考虑,要遵循最小权限原则,即要求系统只授予主体必要的权限,而不要过度授权,这样能有效地减少系统、网络、应用、数据库出错的机会。所以 Linux 系统中,一种良好的操作习惯是使用普通账户登录,在执行需要 root 权限的操作时,再通过 sudo 命令完成。这样能最大化地降低一些误操作导致的风险;同时普通账户被盗用后,与 root 帐户被盗用所导致的后果是完全不同的。在 Ansible 中提供了很多细粒度的提权方式,可以根据需要有选择的提权,通过不同的的提权策略来配置提权。

选择合适的提权方法

在任务执行时,尤其是使用 ansible 处理一些批量初始化集群节点的情况,大多数需要提权处理,在选择如何控制提权时,在什么位置提权,我们需要考虑以下需求:

  • 要使 Playbook 尽量保持简单,不因为提权处理,提高剧本的复杂度,对于可变的部分统一管理,提权处理统一约束。 如果太复杂考虑分层。需要提权剧本任务考虑分组分层单独管理,使用组变量来控制提权,或者单独划分 ansibler 角色处理如果考虑剧本的复杂、只读性,可以通过配置文件,命令行的方式来提权。如果相同剧本不同主机需要不同提权,可以通过 ansible 连接变量(ansible_*)来控制提权。
  • 以最低特权运行任务以避免意外破坏和由于剧本错误对托管主机的损害。

有时候我们直接使用 root 用户来连接受管机,以避免特权升级。但是在生产环境,这通常不是一个好的做法;如果任何运行剧本的人都使用 root 来连接管理主机。这也使得很难确定是哪个运维执行了哪个剧本。容易背锅。但是实验环境或者测试环境我们可以这样使用。一个好的实践是有选择地控制哪些游戏或任务需要特权升级。例如,如果 apache 用户可以启动 httpd 服务器,则不需要以 root 用户运行。理想情况下,以尽可能简单的方式配置提权,并且应该清楚是否将其用于任务。

提权策略

Ansible Playbook 可以在许多不同的级别上实现提权。常见的提权方法:

  • 配置文件和命令行提权
  • 剧本中提权
  • 块中提权
  • 任务中提权
  • 角色中提权
  • 连接变量配置提权

配置文件和命令行提权

配置文件提权

如果将 Ansible 配置文件中的 privilege_escalation 部分中的 become 布尔值设为 yes/True,则 Playbook 中的所有 Play 都将默认使用提权。

[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False

在受管主机上运行时,这些 Play 将会使用当前的 become_method 的方式来切换当前用户为提权为 become_user 用户。可以看到配置之后提权用户为 root

┌──[root@vms81.liruilongs.github.io:]-[~/ansible]
└─$ansible all -m command -a id
vms81.liruilongs.github.io | CHANGED | rc=0 >>
uid=0(root) gid=0(root) 组=0(root)

当把 become 设置为 false 时,我们观察,并没有被提权,而是使用普通用户 liruilongs 进行连接

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible all -m command -a id
vms82.liruilongs.github.io | CHANGED | rc=0 >>
uid=1003(liruilong) gid=1003(liruilong) 组=1003(liruilong)
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$

通过命令行提权

在使用命令行选项执行 Playbook时,也可以覆盖配置文件并指定提权设置。下表比较了配置指令和命令行。选项:

配置文件参数 命令行参数 

become --become / -b become_method --become-method=BECOME_METIHOD become_user --become-user=BECOME_USER become_password --ask-become-pass /-K

如果 Ansible 配置文件指定 become: false,但是命令行中含 -b 选项,则 Ansible 将忽略配置文件,并且默认使用提权。即命令行的方式要高于配置文件提取

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$cat ansible.cfg
[defaults]
# 主机清单文件,就是要控制的主机列表
inventory=inventory
# 连接受管机器的远程的用户名
remote_user=liruilong
# 角色目录
roles_path=roles
# 设置用户的su 提权
[privilege_escalation]
become=False
#become_method=sudo
#become_user=root
#become_ask_pass=False

不使用命令行

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible all -m command -a id
vms82.liruilongs.github.io | CHANGED | rc=0 >>
uid=1003(liruilong) gid=1003(liruilong) 组=1003(liruilong)

使用提权命令

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible all -m command -a id -b
vms82.liruilongs.github.io | CHANGED | rc=0 >>
uid=0(root) gid=0(root) 组=0(root)

即命令行的提权要高于配置文件的提权

Play 剧本中的提权

如果 Play 中不指定是否使用提权,默认是不提权的,会使用配置文件或命令行中的默认设置。ansible_user_id 用于显示当前操作的用户

---
- name: Become the user "manager"
  hosts: all
  tasks:
    - name: Show the user used by this play
      debug:
        var: ansible_user_id

可以发现没有提权

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook becomet.yaml

PLAY [Become the user "manager"] ***********************************************************************

TASK [Gathering Facts] *********************************************************************************
ok: [vms82.liruilongs.github.io]

TASK [Show the user used by this play] *****************************************************************
ok: [vms82.liruilongs.github.io] => 
    "ansible_user_id": "liruilong"


PLAY RECAP *********************************************************************************************
vms82.liruilongs.github.io : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

可以明确指定各个 Play 是否使用提权。通过剧本中 become: true 的方式

- name: Become the user "manager"
  hosts: webservers
  become: true
  tasks:
  - name: Show the user used by this play
    debug:
      var: ansible_user_id

默认不提权,配置之后可以实现提权,即剧本的提权要高于默认配置

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook become.yaml

PLAY [Become the user "manager"] ***********************************************************************

TASK [Gathering Facts] *********************************************************************************
ok: [vms82.liruilongs.github.io]

TASK [Show the user used by this play] *****************************************************************
ok: [vms82.liruilongs.github.io] => 
    "ansible_user_id": "root"


PLAY RECAP *********************************************************************************************
vms82.liruilongs.github.io : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

任务中的提权

也可以只为 Play 中的一个任务打开或关闭提权

---
- name: Become the user "manager"
  hosts: all
  become: false
  tasks:
    - name: tasks sudo 1
      become: true
      yum:
        name: httpd
        state: installed
    - name: tasks sudo 2
      yum:
        name: nginx
        state: installed

任务二没有提权,提示我们需要 root 来执行命令

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook become.yaml

PLAY [Become the user "manager"] ***************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [vms82.liruilongs.github.io]

TASK [tasks sudo 1] ****************************************************************************************************
ok: [vms82.liruilongs.github.io]

TASK [tasks sudo 2] ****************************************************************************************************
fatal: [vms82.liruilongs.github.io]: FAILED! => "changed": false, "changes": "installed": ["nginx"], "msg": "You need to be root to perform this command.\\n", "rc": 1, "results": ["Loaded plugins: fastestmirror\\n"]

PLAY RECAP *************************************************************************************************************
vms82.liruilongs.github.io : ok=2    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

block 块中的提权

如果 Play 中有一部分任务需要(或不需要)提权,可以在 block 上设置 become。这里需要注意一下,在 block 中提权的话,对于提权参数只能放到任务的末尾,不能放到任务的第一个位置。下面的的写法就不对,会报语法错误

---
- name: Become the user "manager"
  hosts: all
  become: false
  tasks:
   - block:
     become: true
     - name: tasks sudo 1
       become: false
       yum:
         name: tomcat
         state: installed
     - name: tasks sudo 2
       yum:
         name: nginx
         state: installed

即下面的这种写法是正确的

---
- name: Become the user "manager"
  hosts: all
  become: false
  tasks:
   - block:
     - name: tasks sudo 1
       become: false
       yum:
         name: tomcat
         state: installed
     - name: tasks sudo 2
       yum:
         name: nginx
         state: installed
     become: true

我们来具看一下,安装 tomcat 需要 root 权限,虽然我们在 block 中提权了,但是在任务中设置不提权,所以会被覆盖。

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook become-block.yaml

PLAY [Become the user "manager"] *********************************************************************
TASK [Gathering Facts] *******************************************************************************
ok: [vms82.liruilongs.github.io]

TASK [tasks sudo 1] **********************************************************************************
fatal: [vms82.liruilongs.github.io]: FAILED! => "changed": false, "changes": "installed": ["tomcat"], "msg": "You need to be root to perform this command.\\n", "rc": 1, "results": ["Loaded plugins: fastestmirror\\n"]

PLAY RECAP *******************************************************************************************
vms82.liruilongs.github.io : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$

修改 yaml 文件之后,我们在来看一下,默认情况下,当 block 中设置了提权,那么默认情况下,block 块内的任务都是提权状态

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$cat become-block.yaml
---
- name: Become the user "manager"
  hosts: all
  become: false
  tasks:
   - block:
     - name: tasks sudo 1
       become: trueb
       yum:
         name: tomcat
         state: installed
     - name: tasks sudo 2
       yum:
         name: nginx
         state: installed
       become: false
     become: true

可以看到 Tomcat 已经安装成功,但是 nginx 安装失败,提示需要 root 权限,因为我们对 yum 模块设置了不提权 become: false,即对于 block 中的提权,任务中具体模块提权要高于 block 的提权

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook become-block.yaml

PLAY [Become the user "manager"] *********************************************************************

TASK [Gathering Facts] *******************************************************************************
ok: [vms82.liruilongs.github.io]

TASK [tasks sudo 1] **********************************************************************************
changed: [vms82.liruilongs.github.io]

TASK [tasks sudo 2] **********************************************************************************
fatal: [vms82.liruilongs.github.io]: FAILED! => "changed": false, "changes": "installed": ["nginx"], "msg": "You need to be root to perform this command.\\n", "rc": 1, "results": ["Loaded plugins: fastestmirror\\n"]

PLAY RECAP *******************************************************************************************
vms82.liruilongs.github.io : ok=2    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$

角色中的提权

角色可以通过两种基本方式来执行提权:针对角色本身,在其内部或针对其任务设置提权变量。这里不多讲,方式太多啦,在角色中可以通过变量或者直接的task目录下你的main.yaml 文件中进行提权。角色任务剧本,创建一个用户:

---
# tasks file for become_demo
- name: become roles  Demo
  debug:
    var: ansible_user_id
- user:
    name: liruilong1
    state: present
~   

调用角色剧本

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$cat become_roles_demo.yaml
---
- hosts: all
  roles:
    - role: become_demo

创建用户需要 root 权限,所以执行报错

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook become_roles_demo.yaml

PLAY [all] *******************************************************************************************

TASK [Gathering Facts] *******************************************************************************
ok: [vms82.liruilongs.github.io]

TASK [become_demo : become roles  Demo] **************************************************************
ok: [vms82.liruilongs.github.io] => 
    "ansible_user_id": "liruilong"


TASK [become_demo : user] ****************************************************************************
fatal: [vms82.liruilongs.github.io]: FAILED! => "changed": false, "cmd": "/sbin/useradd -m liruilong1", "msg": "[Errno 13] Permission denied", "rc": 13

PLAY RECAP *******************************************************************************************
vms82.liruilongs.github.io : ok=2    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

修改角色任务执行的文件,添加提权

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$cat roles/become_demo/tasks/main.yml
---
# tasks file for become_demo
- name: become roles  Demo
  debug:
    var: ansible_user_id
- user:
    name: liruilong1
    state: present
  become: true

可以正常提权创建用户。

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook become_roles_demo.yaml

PLAY [all] *******************************************************************************************

TASK [Gathering Facts] *******************************************************************************
ok: [vms82.liruilongs.github.io]

TASK [become_demo : become roles  Demo] **************************************************************
ok: [vms82.liruilongs.github.io] => 
    "ansible_user_id": "liruilong"


TASK [become_demo : user] ****************************************************************************
changed: [vms82.liruilongs.github.io]

PLAY RECAP *******************************************************************************************
vms82.liruilongs.github.io : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

我们也可以在 Ansible 配置或 Playbook 中指定此信息。

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$cat roles/become_demo/tasks/main.yml
---
# tasks file for become_demo
- name: become roles  Demo
  debug:
    var: ansible_user_id
- user:
    name: liruilong2
    state: present

这里我么修改调用角色剧本文件,提权处理

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$cat become_roles_demo.yaml
---
- hosts: all
  roles:
    - role: become_demo
      become: true

用户创建成功

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook become_roles_demo.yaml

PLAY [all] *******************************************************************************************

TASK [Gathering Facts] *******************************************************************************
ok: [vms82.liruilongs.github.io]

TASK [become_demo : become roles  Demo] **************************************************************
ok: [vms82.liruilongs.github.io] => 
    "ansible_user_id": "liruilong"


TASK [become_demo : user] ****************************************************************************
changed: [vms82.liruilongs.github.io]

PLAY RECAP *******************************************************************************************
vms82.liruilongs.github.io : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

我们最后在受管机器上看一下

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible all -m shell -a "grep liruilong  /etc/passwd "
vms82.liruilongs.github.io | CHANGED | rc=0 >>
liruilong:x:1003:1003::/home/liruilong:/bin/bash
liruilong1:x:1006:1006::/home/liruilong1:/bin/bash
liruilong2:x:1007:1007::/home/liruilong2:/bin/bash
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$

使用变量进行提权

当然我们还可以使用变量来配置提权。这些变量可以作为清单变量应用到组或各个主机上。下表将 Playbook 和配置指令与连接变量名称进行比较:所谓连接变量,即 ansible 在连接受管机的时候会对连接相关的变量赋值。默认情况下有默认值,我们也可以主动修改。配置文件参数 连接变量参数:

become ansible_become become_method ansible_become_method become_user ansible_become_user become_password ansible_become_pass

变量的定义方式可以有很多,感兴趣小伙伴可以看看我之前的博文,我们来简单的看几个

在主机组级别中设置连接变量

webservers: 
  hosts: 
    servera.lab.example.com:
    serverb.lab.example.com: 
  vars: 
     ansible_become: true

来看一个 Demo,添加 all 组变量 ansible_become: true

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$echo "ansible_become: true" > inventory/group_vars/all
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$vim roles/become_demo/tasks/main.yml

角色行为为删除刚才创建的用户

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$cat  roles/become_demo/tasks/main.yml
---
# tasks file for become_demo
- name: become roles  Demo
  debug:
    var: ansible_user_id
- user:
    name: liruilong2
    state: absent

角色删除成功,即通过连接变量实现提权

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook become_roles_demo.yaml

PLAY [all] *******************************************************************************************

TASK [Gathering Facts] *******************************************************************************
ok: [vms82.liruilongs.github.io]

TASK [become_demo : become roles  Demo] **************************************************************
ok: [vms82.liruilongs.github.io] => 
    "ansible_user_id": "root"


TASK [become_demo : user] ****************************************************************************
changed: [vms82.liruilongs.github.io]

PLAY RECAP *******************************************************************************************
vms82.liruilongs.github.io : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

在主机级别中设置连接变量

webservers: 
  hosts: 
    servera.lab.example.com: 
      ansible_become: true 
    serverb.lab.example.com:

同样的方式,这里我们设置组变量为不提权,主机变量为提权。

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$echo "ansible_become: false" > inventory/group_vars/all
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$echo "ansible_become: true" >  inventory/host_vars/vms82.liruilongs.github.io.yaml

角色行为为删除用户

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$cat roles/become_demo/tasks/main.yml
---
# tasks file for become_demo
- name: become roles  Demo
  debug:
    var: ansible_user_id
- user:
    name: liruilong1
    state: absent

用户被删除成功,即主机变量优先级要大于组变量

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook become_roles_demo.yaml

PLAY [all] *******************************************************************************************

TASK [Gathering Facts] *******************************************************************************
ok: [vms82.liruilongs.github.io]

TASK [become_demo : become roles  Demo] **************************************************************
ok: [vms82.liruilongs.github.io] => 
    "ansible_user_id": "root"


TASK [become_demo : user] ****************************************************************************
changed: [vms82.liruilongs.github.io]

PLAY RECAP *******************************************************************************************
vms82.liruilongs.github.io : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

当然我们也可以在 Playbook 中设置连接变量

- name: Example play using connection variables
  hosts: webservers
  vars:
    ansibte_become: true
  tasks:
    - name: Play will use privilege escalation even if inventory says no
      yum:
        name: httpd
        state: installed

参考博文书籍

  • 《Red Hat Ansible Engine 2.8 DO447》
  • 《白帽子讲Web安全》
  • https://docs.ansible.com/ansible/latest/user_guide/become.html
  • https://docs.ansible.com/ansible/latest/user_guide/playbooks_blocks.html

以上是关于Ansible 学习总结—— Ansible 控制提权相关知识总结的主要内容,如果未能解决你的问题,请参考以下文章

Ansible 学习总结—— Ansible 循环条件判断触发器处理失败等任务控制使用总结

Ansible 学习总结—— Ansible 循环条件判断触发器处理失败等任务控制使用总结

Ansible 学习总结—— Ansible playbook 入门详解

Ansible 学习总结—— Ansible playbook 入门详解

Ansible 学习总结—— Ansible 入门详解

Ansible 学习总结—— Ansible 入门详解