ansible管理机密、事实

一、管理机密

1、什么是Ansible Vault

Ansible提供的Ansible Vault可以加密和解密任何由Ansible使用的结构化数据文件
若要使用Ansible Vault,可通过一个名为ansible-vault的命令行工具创建、编辑、加密、解密和查看文件
Ansible Vault可以加密任何由Ansible使用的结构化数据文件。
包括清单变量、playbook中含有的变量文件、以及在执行playbook时作为参数传递的变量文件,或者Ansible角色中定义的变量

2、创建加密文件

1)用ansible-vault filename命令创建加密的文件,该命令在执行时会提示输入新的vault密码,然后利用默认编辑器vi打开文件

[root@ansible ansible]# ansible-vault create wxy.yml
New Vault password:      //输入密码
Confirm New Vault password:       //在此输入密码
[root@ansible ansible]# cat wxy.yml 
$ANSIBLE_VAULT;1.1;AES256
66366535343734366239343665613664613630613533306437353663653966373265626232343161
6230366139323732386561393462643439336335643934350a653563393234333363373236666565
35636232656264323937303833386562343736303834386638653562333963383564623333386237
3863383861616233620a623139343361386561643431363664373063323265326563353636363164
3664    //已经加密

2)使用vault密码文件来存储vault密码,而不是通过标准输入途径输入vault密码,这样做需要使用文件权限和其他方式来严密保护该文件

//首先创建密码文件
[root@ansible ansible]# cat password 
123456
[root@ansible ansible]# chmod 600 password 
[root@ansible ansible]# ll
-rw------- 1 root root     7 725 01:27 password
//创建加密的YAML文件
[root@ansible ansible]# ansible-vault create --vault-password-file=./password yy.yml 
[root@ansible ansible]# cat yy.yml 
$ANSIBLE_VAULT;1.1;AES256
64396164613463393661613035346161366162393939323732633663353964346236333065656536
3763393863363938316633346365646462623539343833620a386134373233363932383035303462
61646466626464393461386339373936623937366662366537366130306333653030346364643962
3732333438323835660a303432623666633038386438303561663662363035653034653765643536
3461

3、查看已经加密的文件

使用ansible-vault view filename命令查看Ansible Vault加密的文件,而不必打开它进行编辑

[root@ansible playbook]# cat host_vars/db 
$ANSIBLE_VAULT;1.1;AES256
64336531303962353132623036393334666432343235623338303562613135336230663838353934
6636333232343536363063363537643132333736333037640a656430353134613738646261343334
34373339396165363866343533656335346637316665663261613536623037376262353864383931
6133366332353062350a303361373432653439623534616464343931383431623862353134373738
35353133393337636664653133346662353234303838663563323263316432303536
[root@ansible playbook]# ansible-vault view host_vars/db 
Vault password: 
password: 123456

4、编辑已经加密的文件

Ansible Vault提供ansible-vault edit filename命令继续编辑已经加密文件;工作时将文件解密为一个临时文件,并允许编辑;保存时,它将复制其内容并删除临时文件

//查看已经加密文件内容
[root@ansible playbook]# ansible-vault view host_vars/db 
Vault password: 
password: 123456
//继续添加内容
[root@ansible playbook]# ansible-vault edit host_vars/db 
Vault password: 
[root@ansible playbook]# ansible-vault view host_vars/db 
Vault password: 
password: 123456
hello lh 

5、加密和解密现有的未加密的文件

使用ansible-vault encrypt filename加密现有的未加密的文件;此命令可以一次加密多个现有的未加密文件

其次还可以使用–output选项将需要加密的文件加密后另存为具有新名称的文件,一次只能有一个输入文件

ansible-vault encrypt playbook.yml --output=test.yml

使用anisble-vault decrypt filename解密现有的已经加密的文件,并且是永久解密
解密也可以是使用–output选项,在解密单个文件时加改选项将解密文件以另一个新名称来保存文件

ansible-vault decrypt playbook.yml --output=test.yml

实例:

[root@ansible playbook]# cat test.yml 
---
- hosts: apache
  vars:
    name: yy
    uid: 2000
  tasks: 
    - name: create user
      user:
        name: "{{ name }}"
        uid: "{{ uid }}"
        state: present 
 //加密
[root@ansible playbook]# ansible-vault encrypt test.yml 
New Vault password: 
Confirm New Vault password: 
Encryption successful
[root@ansible playbook]# cat test.yml 
$ANSIBLE_VAULT;1.1;AES256
36363533306262313933346138613331643034346438633330383432336338633266656466306539
3266343965316361613866343830643265663238633462610a346436363434346266376633373762
32373930353239313261623466613433613034656136333135643032383264383737336532396430
6434383939376136390a313631636330383331313064363364666434633731663733303561396135
62353439326433303834353963333565323565636432306336653261383534323466613636336136
33333761636638643030393862323333376239346438646630366238303239343562376135626462
33376431373339376234393466363636663630656537383539643038383636376564373836663566
62656632346238623330366231653032653635636634633033646166376336366534303464313238
39616538376138636536383264313566633238306264313662633837643734316539383637306232
34336436343636613765313533643036323438333631363632393232393765353036346561366166
32336133373639386535376362366535633866376461643630396431353837366635343031646537
35366233376532333964343430303063353433633565626531376632646632373931666438353432
65313533613337313664363966636130363564306238373166386134666438366161
//解密
[root@ansible playbook]# ansible-vault decrypt test.yml 
Vault password: 
Decryption successful
[root@ansible playbook]# cat test.yml 
---
- hosts: apache
  vars:
    name: yy
    uid: 2000
  tasks: 
    - name: create user
      user:
        name: "{{ name }}"
        uid: "{{ uid }}"
        state: present 
[root@ansible playbook]# 

6、更改已经加密文件的密码

使用ansible-vault rekey filename 命令更改已经加密文件的密码;此命令一次可以更改多个已经加密文件密码;并且需要提供原始密码

[root@ansible playbook]# ansible-vault view test.yml 
Vault password:     //此处我设置的密码是1
---
- hosts: apache
  vars:
    name: yy
    uid: 2000
  tasks: 
    - name: create user
      user:
        name: "{{ name }}"
        uid: "{{ uid }}"
        state: present 
   //更改密码为111
[root@ansible playbook]# ansible-vault rekey test.yml 
Vault password:      //输入原始密码
New Vault password:      //输入新密码
Confirm New Vault password: 
Rekey successful
[root@ansible playbook]# 

除此之外还可以使用vault文件作为面密码:使用–new-vault-password-file选项指定更改的密码

ansible-vault rekey --new-vault-password-file=./new_password test.yml

7、执行ansible-playbook与交互密码

通过Ansible Vault加密的文件的playbook,需要向ansible-playbook命令提供加密密码
为playbook提供vault密码,可使用–vault-id选项,以交互方式提供vault密码

要为playbook提供vault密码,可使用–vault-id选项。例如,要以交互方式提供vault密码,请使用下例中所示的–vault-id @prompt:
实例:

[root@ansible playbook]# ansible-playbook --vault-id @prompt test.yml 
Vault password (default): 

PLAY [apache] *************************************************************************

TASK [Gathering Facts] ****************************************************************
ok: [192.168.235.141]

TASK [create user] ********************************************************************
ok: [192.168.235.141]

PLAY RECAP ****************************************************************************
192.168.235.141            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

此外,也可以使用–vault-password-file选项指定密码文件;密码应当在该文件中存储为一行字符串

[root@ansible ansible]# ansible-playbook --vault-password-file=./password ww.yml 

PLAY [create user] ********************************************************************

TASK [Gathering Facts] ****************************************************************
ok: [192.168.235.141]

TASK [create user] ********************************************************************
changed: [192.168.235.141]

PLAY RECAP ****************************************************************************
192.168.235.141            : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

也可以使用ANSIBLE_VAULT_PASSWORD_FILE环境变量,指定密码文件的默认位置
从Ansible2.4开始,可以通过ansible-playbook使用多个Ansible Vault密码。要使用多个密码,需要将多个–vault-id或–vault-password-file选项传递给ansible-playbook命令
实例:

[root@ansible ansible]#  ansible-playbook --vault-id 1@prompt ww.yml 
Vault password (1): 

PLAY [create user] ********************************************************************

TASK [Gathering Facts] ****************************************************************
ok: [192.168.235.141]

TASK [create user] ********************************************************************
ok: [192.168.235.141]

PLAY RECAP ****************************************************************************
192.168.235.141            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

8、变量文件管理的推荐做法

1️⃣:简化管理,务必要设置Ansible项目,使敏感变量和其他变量保存在相互独立的文件中;包含敏感变量的文件可通过ansible-vault命令进行保护

2️⃣:管理组变量和主机变量的首选方式是在playbook级别上创建目录;

group_vars目录通常包含名称与它们所应用的主机组匹配的变量文件
host_vars目录通常包含名称与它们所应用的受管主机名称匹配的变量文件
3️⃣:Playbook变量(与清单变量相对)也可通过Ansible Vault加密保护;敏感的playbook变量可以放在单独的文件中,此文件通过Ansible Vault加密,并且vars_files指令包含在该playbook中

4️⃣:如果需要在playbook中使用多个vault密码,请确保每个加密文件分配一个vaultID,并在运行playbook时输入具有该vaultID的匹配密码

二、管理事实

1、Ansible事实描述

Ansible事实是Ansible在受管主机上自动检测到的变量

事实(fact)中包含有与主机相关的信息,可以像play中的常规变量、条件、循环或依赖于从受管主机收集的值的任何其他语句那样使用

一些事实可能包括:
主机名称、内核版本、网络接口、IP地址、操作系统版本、各种环境变量、CPU数量、提供的或可用的内存、可用磁盘空间等等

借助事实,可以方便地检索受管主机的状态,并根据该状态确定要执行的操作

例如:

  • 可以根据含有受管主机当前内核版本的事实运行条件任务,以此来重启服务器
  • 可以根据通过事实报告的可用内存来自定义MySQL配置文件
  • 可以根据事实的值设置配置文件中使用的IPv4地址

每个play在执行第一个任务之前会先自动运行setup模块来收集事实
实例:

[root@ansible playbook]# cat facts.yml 
---
- hosts: apache
  tasks:
    - name: 收集事实
      debug:
        var: ansible_facts
[root@ansible playbook]# ansible-playbook facts.yml 

PLAY [apache] *************************************************************************

TASK [Gathering Facts] ****************************************************************
ok: [192.168.235.141]

TASK [收集事实] ***************************************************************************
ok: [192.168.235.141] => {
    "ansible_facts": {
        "all_ipv4_addresses": [
            "192.168.235.141",
            "192.168.122.1"
        ],
        "all_ipv6_addresses": [
            "fe80::20c:29ff:fed7:4b2"
        ],
        "ansible_local": {},
        "apparmor": {
            "status": "disabled"
        },
        "architecture": "x86_64",
        "bios_date": "02/27/2020",
        "bios_version": "6.00",
        "cmdline": {
            "BOOT_IMAGE": "(hd0,msdos1)/vmlinuz-4.18.0-193.el8.x86_64",
            "biosdevname": "0",
            "crashkernel": "auto",
            "net.ifnames": "0",
            "quiet": true,
            "rd.lvm.lv": "rhel/swap",
            "resume": "/dev/mapper/rhel-swap",
            "rhgb": true,
            "ro": true,
            "root": "/dev/mapper/rhel-root"
        },
        "date_time": {
            "date": "2021-07-25",
            "day": "25",
            "epoch": "1627203996",
            "hour": "05",
            "iso8601": "2021-07-25T09:06:36Z",
            "iso8601_basic": "20210725T050636627687",
            "iso8601_basic_short": "20210725T050636",
            "iso8601_micro": "2021-07-25T09:06:36.627687Z",
            "minute": "06",
            "month": "07",
            "second": "36",
            "time": "05:06:36",
            "tz": "EDT",
            "tz_offset": "-0400",
            "weekday": "星期日",
            "weekday_number": "0",
            "weeknumber": "29",
            "year": "2021"
        },
············略

Playbook以JSON格式显示ansible_facts变量的内容
Ansible事实的示例:

事实变量
短主机名ansible_facts[‘hostname’]
完全限定域名ansible_facts[‘fqdn’]
IPv4地址ansible_facts[‘default_ipv4’][‘address’]
所有网络接口的名称列表ansible_facts[‘interfaces’]
/dev/vda1磁盘分区的大小ansible_facts[‘devices’][‘vda’][‘partitions’][‘vda1’][‘size’]
DNS服务器列表ansible_facts[‘dns’][‘nameservers’]
当前运行的内核版本ansible_facts[‘kernel’]

在playbook中使用事实时,Ansible将事实的变量名动态替换为对应的值
实例:

[root@ansible playbook]# cat facts.yml 
---
- hosts: apache
  tasks:
    - name: 收集事实
      debug:
        msg: >
          this host ip is {{ ansible_facts['default_ipv4']['address'] }}
          this hostname is {{ ansible_facts['hostname']}}
[root@ansible playbook]# ansible-playbook facts.yml 

PLAY [apache] *************************************************************************

TASK [Gathering Facts] ****************************************************************
ok: [192.168.235.141]

TASK [收集事实] ***************************************************************************
ok: [192.168.235.141] => {
    "msg": "this host ip is 192.168.235.141 this hostname is apache\n"
}

PLAY RECAP ****************************************************************************
192.168.235.141            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

2、Ansible事实作为变量注入

较早的变量注入:
在Ansible2.5之前,事实是作为前缀为字符串ansible_的单个变量注入,而不是作为ansible_facts变量的一部分注入
ansible_facts[‘distribution’]事实会被称为ansible_distribution
可以使用临时命令来运行setup模块,以此形式显示所有事实的值:
实例:

[root@ansible playbook]# ansible all -m setup
192.168.235.141 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.235.141",
            "192.168.122.1"
        ],
        "ansible_all_ipv6_addresses": [
            "fe80::20c:29ff:fed7:4b2"
        ],
        "ansible_apparmor": {
            "status": "disabled"
        },
·················

选定的Ansible事实名称比较

ansible_facts形式旧事实变量形式
ansible_facts[‘hostname’]ansible_hostname
ansible_facts[‘fqdn’]ansible_fqdn
ansible_facts[‘default_ipv4’][‘address’]ansible_default_ipv4[‘address’]
ansible_facts[‘interfaces’]ansible_interfaces
ansible_facts[‘devices’][‘vda’][‘partitions’][‘vda1’][‘size’]ansible_devices[‘vda’][‘partitions’][‘vda1’][‘size’]
ansible_facts[‘dns’][‘nameservers’]ansible_dns[‘nameservers’]
ansible_facts[‘kernel’]ansible_kernel

将Ansible配置文件的[default]部分中inject_facts_as_vars参数设置为False,可关闭旧命名系统。默认设置为True

inject_facts_as_vars的默认值在Ansible的未来版本中可能会更改为False

如果设置为False,则只能使用新的ansible_facts.*命名系统引用Ansible事实

3、关闭事实收集

有时我们不想为play收集事实。这样做的原因可能有:

  • 不准备使用任何事实
  • 希望加快play速度
  • 希望减小play在受管主机上造成的负载
  • 受管主机因为某种原因无法运行setup模块
  • 需要安装一些必备软件后再收集事实

要为play禁用事实收集功能,可将gather_facts关键字设置为no:

[root@ansible playbook]# cat playbook.yml 
--- 
- hosts: apache
  gather_facts: no
  tasks:
    - name: test file
      debug:
          var: anisble_facts

即使play设置了gather_facts: no,也可以随时通过运行使用setup模块的任务来手动收集事实

[root@ansible playbook]# cat playbook.yml 
---
- hosts: apache
  gather_facts: no
  tasks:
    - name: get ansible_facts
      setup:
 
    - name: test debug
      debug:
        var: ansible_facts

4、创建自定义事实

除了使用系统捕获的事实外,我们还可以自定义事实,并将其本地存储在每个受管主机上;

默认情况下,setup模块从各受管主机的( /etc/ansible/facts.d 目录下的文件和脚本中加载自定义事实

各个文件或脚本的名称必须以.fact结尾才能被使用;动态自定义事实脚本必须输出JSON格式的事实,而且必须是可执行文件

实例:
方式一:采用INI格式编写的静态自定义事实文件

//在受管主机上创建自定义文件
[root@apache ~]# mkdir -p /etc/ansible/facts.d
[root@apache ~]# cd /etc/ansible/facts.d
[root@apache facts.d]# cat example.fact 
[users]
user_one: zhangsan
user_two: lisi
 
[servers]
service_one: httpd
service_two: mariadb
//在控制节点上查看
"ansible_local": {
            "example": {
                "servers": {
                    "service_one": "httpd",
                    "service_two": "mariadb"
                },
                "users": {
                    "user_one": "zhangsan",
                    "user_two": "lisi"
                }
            }
        },

方式二: 以JSON格式提供事实,JSON事实等同于INI格式指定的事实,JSON数据可以存储在静态文本文件中

//查看用JSON格式写对fact文件
[root@apache facts.d]# pwd
/etc/ansible/facts.d
[root@apache facts.d]# cat test.fact 
{
   "users": {
      "user_one": "zhangsan",
      "user_two": "lisi"
   },
   "servers": {
      "service_one": "httpd",
      "service_two": "vsftpd"
   }
}
 //在控制节点上查看
 "ansible_local": {
            "test": {
                "servers": {
                    "service_one": "httpd",
                    "service_two": "vsftpd"
                },
                "users": {
                    "user_one": "zhangsan",
                    "user_two": "lisi"
                }
            }
        },

自定义事实由setup模块存储在ansible_facts[‘ansible_local’]变量中

事实按照定义它们的文件的名称来整理;例如:自定义事实由受管主机上保存为/etc/ansible/facts.d/example.fact的文件,在这种情况下,ansible_facts[‘ansible_local’][‘example’][‘users’][‘user_two’]的值为lisi
实例:

[root@ansible playbook]# cat playbook.yml 
---
- hosts: apache
  tasks:
    - name: 输出某个用户
      debug:
        msg: 该用户是:{{ ansible_facts['ansible_local']['example']['users']['user_one'] }}
 
[root@ansible playbook]# ansible-playbook playbook.yml

PLAY [apache] *************************************************************************

TASK [Gathering Facts] ****************************************************************
ok: [192.168.235.141]

TASK [输出某个用户] *************************************************************************
ok: [192.168.235.141] => {
    "msg": "该用户是:zhangsan"
}

PLAY RECAP ****************************************************************************
192.168.235.141            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@ansible playbook]#  ansible all -m setup |less
"ansible_local": {
            "example": {
                "servers": {
                    "service_one": "httpd",
                    "service_two": "vsftpd"
                },
                "users": {
                    "user_one": "zhangsan",
                    "user_two": "lisi"
                }
            }
        },

5、使用魔法变量

一些变量并非事实或通过setup模块配置,但也由Ansible自动设置。这些魔法变量也可用于获取与特定受管主机相关的信息。

最常用的有四个:

魔法变量说明
hostvars包含受管主机的变量,可以用于获取另一台受管主机的变量的值。如果还没有为受管主机收集事实,则它不会包含该主机的事实。
group_names列出当前受管主机所属的所有组
groups列出清单中的所有组和主机
inventory_hostname包含清单中配置的当前受管主机的主机名称。因为各种原因有可能与事实报告的主机名称不同