ansible最佳实践之playbook不同上下文提权demo(代码片段)

山河已无恙 山河已无恙     2022-11-29     546

关键词:

写在前面


  • 生活加油,今天和小伙伴们分享一些 Ansible 提权的笔记
  • 博文内容涉及
    • 如何选择Ansible的提权方式
    • 提权策略有哪些
    • 提权策略具体的Demo
  • 食用方式:
    • 需要有 Ansible 基础,了解 Ansible 变量的使用
  • 理解不足小伙伴帮忙指正
  • 近几天的内蒙有风也有云,就是热了些,你那里呢 ^_^

傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的生命被剥夺了。当时我是个年轻人,但我害怕这样生活下去,衰老下去。在我看来,这是比死亡更可怕的事。--------王小波


Ansible 控制提权

ansible 版本

┌──[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 都将默认使用提权。

[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
vms82.liruilongs.github.io | CHANGED | rc=0 >>
uid=0(root) gid=0(root)=0(root)

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

┌──[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在连接受管机的时候会对连接相关的变量赋值。默认情况下有默认值,我们也可以主动修改

配置文件参数连接变量参数
becomeansible_become
become_methodansible_become_method
become_useransible_become_user
become_passwordansible_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=查看详情  

ansible最佳实践之playbook控制任务的执行(代码片段)

写在前面今天和小伙伴们分享一些Ansible中如何控制剧本任务执行的笔记博文内容分为两部分,控制任务执行,和控制主机执行顺序,涉及内容:剧本默认执行顺序分析Demo,先角色后任务import或include导入角色... 查看详情

ansible最佳实践之playbook高级循环任务如何操作(代码片段)

写在前面今天和小伙伴分享一些ansible剧本中数据迭代的笔记博文内容比较简单主要介绍的常见的迭代对比使用过滤器和查找插件在复杂数据结构上实施迭代循环食用方式:了解Ansible基础语法理解不足小伙伴帮忙指正傍晚时... 查看详情

playbook之ansible

ansibleplaybook最佳实践             http://blog.csdn.net/kellyseeme/article/details/50574340本文出自“运维自动化”博客,请务必保留此出处http://shower.blog.51cto. 查看详情

Ansible 最佳实践不重复常见角色

...述】:在Ansible最佳实践页面上:http://docs.ansible.com/ansible/playbooks_best_practices.html#top-level-playbooks-are-separated-by-role它显示 查看详情

ansible最佳实践之playbook高级循环任务如何操作(代码片段)

写在前面今天和小伙伴分享一些ansible剧本中数据迭代的笔记博文内容比较简单主要介绍的常见的迭代对比使用过滤器和查找插件在复杂数据结构上实施迭代循环食用方式:了解Ansible基础语法理解不足小伙伴帮忙指正傍晚时... 查看详情

ansible学习笔记04(最佳实践)(代码片段)

1、使用过滤器处理变量1.1ansiblefilteransible应用变量到playbook并且使用jinja2表达式来使用变量。例如下面的J2表达式中的变量使用两个大括号括起来。J2表达式也支持filter。Filter在playbook或者模板中被用来修改或者处理要替代的变量... 查看详情

ansible学习笔记9-playbooks之include

taskincludefiles在不同tasks之间plays和playbooks可以重复调用。includefiles可以达成目的系统通过使用includetask来完美实现role定义,记住,playbook中的play最终目的是映射系统群到多个roles中cattasks/foo.yml---#possiblysavedastasks/foo.yml-name:placeholder... 查看详情

ansible学习笔记5-playbooks之handlers

...module具有幂等性,所以当远端系统被人改动时,可以重放playbooks达到恢复的目的。playbooks本身可以识别这种改动,并且有一个基本的eventsystem,可以响应这种改动notifyactions会在playbook的每一个task结束时被触发,而且即使有多个不... 查看详情

ansible最佳实践之awx使用ansible与api通信(代码片段)

写在前面分享一些AWX使用Ansible与API通信的笔记博文内容涉及:curl方式调用AWXAPI浏览器接口文档方式调用AWXAPI使用API调用方式启动AWX中作业模板Ansible模块uri的简单介绍Ansible剧本方式调用API启动作业模板理解不足小伙伴帮忙指... 查看详情

ansible之playbook(代码片段)

文章目录一、Playbook概述(1)Playbook简介(2)Playbook核心元素(3)Playbook语法(4)剧本格式示例(5)Playbook的运行方式二、剧本中的元素属性-主机与用户-tasks任务列表-Handlers动作与Notify触发... 查看详情

ansible之playbook

官方文档https://docs.ansible.com/ansible/latest/reference_appendices/playbooks_keywords.html#playbook-keywordsPlaybook核心组件一个playbook中由多个组件组成,其中所用到的常见组件类型如下:Hosts执行的远程主机列表Tasks任务集,由多个task的元素组成的列表... 查看详情

ansible最佳实践之委派任务和事实(代码片段)

写在前面分享一些Ansible委派任务和事实委派的笔记博文内容涉及:Ploybook任务委派DemoPloybook事实委派Demo理解不足小伙伴帮忙指正傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的... 查看详情

运维自动化之ansible--(playbook模式)

运维自动化之ansible--(playbook模式) 一、Ansible Playbook简介  playbook是ansible用于配置,部署,和管理被控节点的剧本。   通过playbook的详细描述,执行其中的一系列tasks,可以让远端主机达到预期的状态。play... 查看详情

ansible之playbooks

playbook是一个或多个“paly”组成的列表。play的主要功能在于将事先并归为一组的主机扮成事先通过ansible中task定义好的角色,从根本上将,所谓的task就是调用ansible的一个module。将多个play组织的一个playbook中就可以让它们连同起... 查看详情

ansible学习笔记7-playbooks之执行一个playbook

执行一个playbook上面几节已经初步了解了playbook的语法,那么要如何运行一个playbook呢,这很简单,这里的示例是并行的运行playbook,并行的级别是10ansible-playbookplaybook.yml-f10Ansible-Pull(拉取配置而非推送配置)我们可不可以将ansibl... 查看详情

ansible之playbook

    ansbileplaybook是一系列ansible命令的集合,利用yaml语言编写,playbook命令根据自上而下的顺序依次执行。同时,playbook开创了很多特性,它可以允许你传输某个命令的状态到后面的指令,如你可以从一台机器的文件中... 查看详情

ansible个人学习通过playbook实践批量免密(代码片段)

方法一:直接使用for循环操作拷贝#指令foriin`seq6163`dossh-copy-id-i/root/.ssh/id_rsa.pubuser_name@192.168.31.$idone方法二:使用ansible-playbook实现1、安装ansible[root@~]#yuminstallepel-release-y[root@~]#yuminstallansible-y2、修改an 查看详情