假设某个客户要求您帮助脚本迁移,以在RHEL和AIX服务器上部署集中式sudoers文件。

好吧,这是一个非常普遍的场景,您可以通过其示例来演示Ansible的高级功能的使用以及方法的变化方式-从执行特定任务的脚本到幂等(无需进行更改)描述并监视实例状态的符合性。
采取脚本:
有212行代码,sudoers文件中没有版本控制。 客户已经有一个进程,该进程每周运行一次,并检查文件的校验和以确保安全性。 尽管脚本包含对Solaris的引用,但对于该客户,我们不必转移此要求。
让我们从创建角色开始,然后将sudoers文件放入Git中进行版本控制。 除其他外,这将使我们摆脱挂载NFS卷的需要。
使用
复制和
模板模块的验证和备份选项,我们可以无需编写代码来备份和还原文件。 在这种情况下,将在将文件放置到目标点之前执行验证,如果验证失败,则模块将引发错误。
对于每个角色,我们需要指定任务,模板和变量。 这是相应文件的结构:

角色脚本文件(剧本)
sudoers.yml具有简单的结构:
---
##
# Role playbook
##
- hosts: all
roles:
- sudoers
...
角色变量位于
vars / main.yml文件中 。 该文件包含校验和文件以及include / exclude指令,这些指令将用于创建特殊逻辑以跳过Lawson主机,仅在hd主机中包括sudoers.d文件。
这是
vars / main.yml文件的内容:
---
MD5FILE: /root/.sudoer.md5
EXCLUDE: la
INCLUDE: hd
...
如果我们使用
copy和
lineinfile模块 ,那么该角色将不是幂等的。 复制模块将安装基本文件,并且lineinfile将在每次启动时重新插入include。 由于此角色将在
Ansible Tower上
启动 ,因此必须具备幂等性。 我们将文件转换为jinja2模板。
在第一行中,我们添加以下命令来
控制空格和缩进 :
#jinja2: lstrip_blocks: True, trim_blocks: True
请注意,较新版本的
模板模块包括
trim_blocks的参数(在Ansible 2.4中添加)。
以下是在文件末尾插入
include行的代码:
{% if ansible_hostname[0:2] == INCLUDE %}
#includedir /etc/sudoers.d
{% endif %}
我们为shell命令使用条件构造({%if%},{%endif%}),该命令为名称以字符“ hd”开头的主机插入一行。 我们使用Ansible事实和过滤器[0:2]来解析主机名。
现在让我们继续执行任务。 首先,您需要建立一个事实来解析主机名。 我们将在条件构造中使用事实“ parhost”。
---
##
# Parse hostnames to grab 1st 2 characters
##
- name: "Parse hostname's 1st 2 characters"
set_fact: parhost={{ ansible_hostname[0:2] }}
RHEL库存服务器上没有
csum参数。 如有必要,我们可以使用另一个事实来用校验和有条件地指示二进制文件的名称。 请注意,如果这些功能在AIX,Solaris和Linux上不同,则可能需要附加代码。
此外,我们还必须解决AIX和RHEL上根组差异的问题。
##
# Conditionally set name of checksum binary
##
- name: "set checksum binary"
set_fact:
csbin: "{{ 'cksum' if (ansible_distribution == 'RedHat') else 'csum' }}"
##
# Conditionally set name of root group
##
- name: "set system group"
set_fact:
sysgroup: "{{ 'root' if (ansible_distribution == 'RedHat') else 'sys' }}"
使用块可以让我们为整个任务设置条件。 我们将使用该块末尾的条件来排除“ la”主机。
##
# Enclose in block so we can use parhost to exclude hosts
##
- block:
模板模块将验证并安装文件。 我们修复结果,以便我们可以确定任务是否已更改。 在此模块中使用validate参数可以使您在将新的sudoer文件放入主机之前验证其有效性。
##
# Validate will prevent bad files, no need to revert
# Jinja2 template will add include line
##
- name: Ensure sudoers file
template:
src: sudoers.j2
dest: /etc/sudoers
owner: root
group: "{{ sysgroup }}"
mode: 0440
backup: yes
validate: /usr/sbin/visudo -cf %s
register: sudochg
如果已安装新模板,请运行shell脚本以生成校验和文件。 安装sudoers模板时,或者缺少校验和文件时,条件结构会更新校验和文件。 由于正在运行的进程还监视其他文件,因此我们使用源脚本中提供的shell代码:
- name: sudoers checksum
shell: "grep -v '/etc/sudoers' {{ MD5FILE }} > {{ MD5FILE }}.tmp ; {{ csbin }} /etc/sudoers >> {{ MD5FILE }} ; mv {{ MD5FILE }}.tmp {{ MD5FILE }}"
when: sudochg.changed or MD5STAT.exists == false
文件模块验证必要权限的安装:
- name: Ensure MD5FILE permissions
file:
path: "{{ MD5FILE }}"
owner: root
group: "{{ sysgroup }}"
mode: 0600
state: file
由于backup参数不提供任何用于处理先前备份的选项,因此我们将不得不自己创建适当的代码。 在下面的示例中,我们为此使用“ register”参数和“ stdout_lines”字段。
##
# List and clean up backup files. Retain 3 copies.
##
- name: List /etc/sudoers.*~ files
shell: "ls -t /etc/sudoers*~ |tail -n +4"
register: LIST_SUDOERS
changed_when: false
- name: Cleanup /etc/sudoers.*~ files
file:
path: "{{ item }}"
state: absent
loop: "{{ LIST_SUDOERS.stdout_lines }}"
when: LIST_SUDOERS.stdout_lines != ""
区块完成:
##
# This conditional restricts what hosts this block runs on
##
when: parhost != EXCLUDE
...
预期的用例是在Ansible Tower上扮演此角色。 可以配置Ansible塔式警报,以便在作业执行失败的情况下,警报将发送到电子邮件,Slack或其他方式。 该角色在Ansible,Ansible引擎或Ansible塔中运行。
结果,我们从脚本中删除了所有多余的内容,并创建了一个完全等幂的角色,可以提供sudoers文件的所需状态。 使用SCM可以进行版本控制,提供更有效的变更管理和透明度。 带有Jenkins或其他工具的CI / CD允许您设置Ansible代码的自动化测试以用于将来的更改。 Ansible Tower中的Auditor角色使您可以监视和执行组织要求。
可以从脚本中删除用于处理校验和的代码,但是为此,客户需要首先咨询其安全服务。 如有必要,可以使用Ansible Vault保护sudoers模式。 最后,使用组避免了使用包含和排除来编写逻辑。
→您可以在此
链接从GitHub下载角色