生活加油,今天和小伙伴们分享一些 Ansible 提权的笔记
博文内容涉及
如何选择Ansible的提权方式
提权策略有哪些
提权策略具体的Demo
食用方式:
需要有 Ansible 基础,了解 Ansible 变量的使用
理解不足小伙伴帮忙指正
近几天的内蒙有风也有云,就是热了些,你那里呢 ^_^
傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的生命被剥夺了。当时我是个年轻人,但我害怕这样生活下去,衰老下去。在我看来,这是比死亡更可怕的事。——–王小波
写在前面
生活加油,今天和小伙伴们分享一些 Ansible 提权的笔记
博文内容涉及
如何选择Ansible的提权方式
提权策略有哪些
提权策略具体的Demo
食用方式:
需要有 Ansible 基础,了解 Ansible 变量的使用
理解不足小伙伴帮忙指正
近几天的内蒙有风也有云,就是热了些,你那里呢 ^_^
傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的生命被剥夺了。当时我是个年轻人,但我害怕这样生活下去,衰老下去。在我看来,这是比死亡更可怕的事。——–王小波
Ansible 控制提权 ansible 版本
1 2 3 4 5 6 7 8 ┌──[root@vms81.liruilongs.github.io]-[~/ansible] └─$ansible --version ansible 2.9.25 config file = /root/ansible/ansible.cfg configured module search path = [u'/root/.ansible/plugins/modules' , u'/usr/share/ansible/plugins/modules' ] ansible python module location = /usr/lib/python2.7/site-packages/ansible executable location = /usr/bin/ansible python version = 2.7.5 (default, Aug 4 2017, 00:39:18) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]
对于初学ansible的小伙伴来将,提权配置大都是通过配置ansible.cfg
的方式来配置提权
,通过配置的文件的方式配置的提权,对所有执行的剧本角色有提权,这样的好处是,简单方便,但是有一定的风险,任何命令都通用过root
来执行,即任何进程都是具有系统的最高权限
,对于黑客来讲,最想得到的即root权限,如果进程被植入了木马病毒之类,控制了进程即拥有root权限。所以任何命令通过root来执行是一件很危险的事。
所以从安全角度考虑,要遵循最小权限原则,即要求系统只授予主体必要的权限,而不要过度授权,这样能有效地减少系统、网络、应用、数据库出错的机会。
所以Linux系统中,一种良好的操作习惯是使用普通账户登录,在执行需要root权限的操作时,再通过sudo命令完成。这样能最大化地降低一些误操作导致的风险;同时普通账户被盗用后,与root帐户被盗用所导致的后果是完全不同的。
在 Ansible 中提供了很多细粒度的提权方式
,可以根据需要有选择的提权
,通过不同的的提权策略
来配置提权。
选择合适的提权方法 在任务执行时,尤其是使用ansible处理一些批量初始化集群节点的情况,大多数需要提权处理,在选择如何控制提权时,在什么位置提权,我们需要考虑以下需求:
要使Playbook
尽量保持简单,不因为提权处理,提高剧本的复杂度,对于可变的部分统一管理,提权处理统一约束。
如果太复杂考虑分层。需要提权剧本任务考虑分组分层单独管理,使用组变量来控制提权,或者单独划分ansibler角色处理
如果考虑剧本的复杂、只读性,可以通过配置文件,命令行的方式来提权。
如果相同剧本不同主机需要不同提权,可以通过ansible 连接变量(ansible_*
)来控制提权。
以最低特权运行任务以避免意外破坏和由于剧本错误对托管主机的损害。
有时候我们直接使用root用户
来连接受管机,以避免特权升级。但是在生产环境,这通常不是一个好的做法;如果任何运行剧本的人都使用root来连接管理主机。这也使得很难确定是哪个运维执行了哪个剧本。容易背锅。但是实验环境或者测试环境我们可以这样使用。
一个好的实践是有选择地控制哪些游戏或任务需要特权升级。例如,如果apache用户可以启动httpd服务器,则不需要以root用户运行。理想情况下,以尽可能简单的方式配置提权,并且应该清楚是否将其用于任务。
提权策略 Ansible Playbook 可以在许多不同的级别上实现提权。常见的提权方法:
配置文件和命令行提权
剧本中提权
块中提权
任务中提权
角色中提权
连接变量配置提权
提权策略Demo 配置文件和命令行提权 配置文件提权 如果将Ansible
配置文件中的 privilege_escalation
部分中的become
布尔值设为 yes/True
,则 Playbook 中的所有Play 都将默认使用提权。
1 2 3 4 5 [privilege_escalation] become =True become_method =sudobecome_user =rootbecome_ask_pass =False
在受管主机上运行时,这些 Play 将会使用当前的become_method
的方式来切换当前用户为提权为 become_user
用户。
可以看到配置之后提权用户为root
1 2 3 4 ┌──[root@vms81.liruilongs.github.io]-[~/ansible] └─$ansible all -m command -a id vms82.liruilongs.github.io | CHANGED | rc=0 >> uid=0(root) gid=0(root) 组=0(root)
当把become设置为false
时,我们观察,并没有被提权,而是使用普通用户liruilong
进行连接
1 2 3 4 5 6 ┌──[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
将忽略配置文件,并且默认使用提权。即命令行的方式要高于配置文件提取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ┌──[root@vms81.liruilongs.github.io]-[~/ansible] └─$cat ansible.cfg [defaults] inventory=inventory remote_user=liruilong roles_path=roles [privilege_escalation] become=False
不使用命令行
1 2 3 4 ┌──[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)
使用提权命令
1 2 3 4 ┌──[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
用于显示当前操作的用户
1 2 3 4 5 6 7 --- - name: Become the user "manager" hosts: all tasks: - name: Show the user used by this play debug: var: ansible_user_id
可以发现没有提权
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ┌──[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
的方式
1 2 3 4 5 6 7 - name: Become the user "manager" hosts: webservers become: true tasks: - name: Show the user used by this play debug: var: ansible_user_id
默认不提权,配置之后可以实现提权,即剧本的提权要高于默认配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ┌──[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 中的一个任务打开或关闭提权
1 2 3 4 5 6 7 8 9 10 11 12 13 14 --- - 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来执行命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ┌──[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中提权的话,对于提权参数只能放到任务的末尾,不能放到任务的第一个位置。
下面的的写法就不对,会报语法错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 --- - 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
即下面的这种写法是正确的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 --- - 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中提权了,但是在任务中设置不提权,所以会被覆盖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ┌──[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 块内的任务都是提权状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ┌──[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的提权
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ┌──[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 文件中进行提权
角色任务剧本,创建一个用户
1 2 3 4 5 6 7 8 9 --- - name: become roles Demo debug: var: ansible_user_id - user: name: liruilong1 state: present ~
调用角色剧本
1 2 3 4 5 6 ┌──[root@vms81.liruilongs.github.io]-[~/ansible] └─$cat become_roles_demo.yaml --- - hosts: all roles: - role: become_demo
创建用户需要root权限,所以执行报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ┌──[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
修改角色任务执行的文件,添加提权
1 2 3 4 5 6 7 8 9 10 11 ┌──[root@vms81.liruilongs.github.io]-[~/ansible] └─$cat roles/become_demo/tasks/main.yml --- - name: become roles Demo debug: var: ansible_user_id - user: name: liruilong1 state: present become: true
可以正常提权创建用户。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ┌──[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
中指定此信息。
1 2 3 4 5 6 7 8 9 10 ┌──[root@vms81.liruilongs.github.io]-[~/ansible] └─$cat roles/become_demo/tasks/main.yml --- - name: become roles Demo debug: var: ansible_user_id - user: name: liruilong2 state: present
这里我么修改调用角色剧本文件,提权处理
1 2 3 4 5 6 7 ┌──[root@vms81.liruilongs.github.io]-[~/ansible] └─$cat become_roles_demo.yaml --- - hosts: all roles: - role: become_demo become: true
用户创建成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ┌──[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
我们最后在受管机器上看一下
1 2 3 4 5 6 7 8 ┌──[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
变量的定义方式可以有很多,感兴趣小伙伴可以看看我之前的博文,我们来简单的看几个
在主机组
级别中设置连接变量: 1 2 3 4 5 6 webservers: hosts: servera.lab.example.com: serverb.lab.example.com: vars: ansible_become: true
来看一个Demo,添加all组变量ansible_become: true
1 2 3 4 ┌──[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
角色行为为删除刚才创建的用户
1 2 3 4 5 6 7 8 9 10 ┌──[root@vms81.liruilongs.github.io]-[~/ansible] └─$cat roles/become_demo/tasks/main.yml --- - name: become roles Demo debug: var: ansible_user_id - user: name: liruilong2 state: absent
角色删除成功,即通过连接变量实现提权
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ┌──[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
在主机
级别中设置连接变量: 1 2 3 4 5 webservers: hosts: servera.lab.example.com: ansible_become: true serverb.lab.example.com:
同样的方式,这里我们设置组变量为不提权,主机变量为提权。
1 2 3 4 ┌──[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
角色行为为删除用户
1 2 3 4 5 6 7 8 9 10 ┌──[root@vms81.liruilongs.github.io]-[~/ansible] └─$cat roles/become_demo/tasks/main.yml --- - name: become roles Demo debug: var: ansible_user_id - user: name: liruilong1 state: absent
用户被删除成功,即主机变量优先级要大于组变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ┌──[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
中设置连接变量
1 2 3 4 5 6 7 8 9 - 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