laitimes

Making Ansible more secure: Encrypt with Vault

author:Linux O&M base

When managing target nodes, some operations require the use of passwords to allow access, but Ansible is an automated configuration management tool, and the act of requiring interactive password entry during the automation phase should be frustrating.

In general, non-interactive scenarios are:

  • (1). Write sensitive data to a file (such as writing a variable file) and then read it, which is not safe;
  • (2). Define the environment variables corresponding to sensitive data, the disadvantage is that some client tools do not necessarily support this way, and this scheme is not convenient enough;
  • (3). Use the command line option, but the disadvantage is that it is not safe, and Ansible does not support this function;
  • (4).expect类工具,缺点是不方便。

Ansible itself provides a more convenient and secure solution: Vault encryption, which uses the AES256 encryption algorithm to protect Ansible.

Prior to Ansible 2.4, the Vault encryption feature was not user-friendly, and the features were restrictive, such as all tasks involved in encryption had to share the same password, for example, earlier versions could not encrypt some strings. The current Ansible version of Vault encryption is more user-friendly, and this article will introduce the friendly version of Vault encryption in detail.

1. An example of getting started: create an encrypted file

Ansible uses the ansible-valut command to complete vault encryption and decryption related operations, and it has many subcommands, such as the create subcommand to create encrypted files, the view subcommand to view encrypted files, and so on, and the options of these subcommands are mostly similar.

$ ansible-vault --help

positional arguments:
  {create,decrypt,edit,view,encrypt,encrypt_string,rekey}
    create              创建新的文件并加密
    decrypt             解密已加密的文件
    edit                编辑已加密文件的内容
    view                查看已加密文件的内容
    encrypt             加密已存在的未加密文件
    encrypt_string      加密一段字符串
    rekey               修改已加密文件的Vault ID和凭据密码
           

For example, use the create subcommand to create an encrypted file passwd_prompt.yml and write a password variable to this file.

$ ansible-vault create --vault-id @prompt passwd_prompt.yml

New vault password (default):             # 提示用户输入的
Confirm new vault password (default):     # 提示用户输入的

---                      # 自动打开编辑器,比如打开vim
mypasswd: 123456         # 输入一个变量
                         # 保存并退出
           

The --vault-id @prompt is used as an argument to ansible-vault, which interactively prompts the user for a password that is required to encrypt the encrypted file passwd_prompt.yml when ansible-vault create is executed. To put it simply, this file holds the encrypted password data, but the password is also required to access the file. For the sake of distinction, this password will be referred to later in this article as the "credential password".

In the example above, I set a variable mypasswd with a value of 123456. When you exit the editor, the file is automatically encrypted. For example:

# 为排版需求,我截断了一部分字符
$ cat passwd_prompt.yml
$ANSIBLE_VAULT;1.1;AES256
316336393534333831363334363633653232303268
373962616633356439393830353430373230613239
323530393466363035633164306133663030303839
6137616561353337660a3430313931376266643034
373234623839383838386537643635666232303563
           

If you want to view the ciphertext, you can use the view subcommand of ansible-vault, which also requires the credential password.

$ ansible-vault view --vault-id @prompt passwd_prompt.yml
Vault password (default):      # 输入凭据密码后将展示如下内容
---
mypasswd: 123456
           

Also, remember the first line of $ANSIBLE_VAULT in the encrypted file for the time being; 1.1; AES256, which explains the meaning of each field in this row later.

2. How to provide Vault ID and credential password

In the example above, the option --vault-id @prompt is used, and its behavior is to interactively prompt the user for a credential password.

Actually,-- full usage of the valut-id option looks like this:

--vault-id label@prompt
--vault-id label@path_to_normal_txt_file
--vault-id label@path_to_script_file
           

Before we explain these usages, a little bit about the history of Ansible Vault: in previous versions of Ansible, only one credential password was available for each ansbile or ansible-playbook command, which made it possible to use only one password for all the encryption involved in this task.

Making Ansible more secure: Encrypt with Vault
--vault-id dev_mysql@xxx
--vault-id test_mysql@xxx
--vault-id prod_mysql@xxx
           

You can also omit the vault ID and use the default vault ID uniformly.

--vault-id What is xxx in the label@xxx? There are three sources of credential passwords:

  • (1).prompt: prompt is a fixed value that prompts the user to provide the credential password in an interactive manner
  • (2). The file name of a common file: for example, "a.txt", which means that the password is read from the file
  • (3). Script file name: indicates that the password is obtained from the script execution result

For example, there are common file a.txt:

echo '123456' >a.txt
           

There are script files a.sh which are as follows (py scripts, perl scripts, or even programs, etc., as long as the password is output to standard output and has executable permissions):

#!/bin/bash
echo 123456
           

So let's create three encrypted files with three different credential cipher sources:

$ ansible-vault create --vault-id id1@prompt first_encrypted.yml
$ ansible-vault create --vault-id [email protected]  second_encrypted.yml
$ ansible-vault create --vault-id [email protected]   third_encrypted.yml
           

If the Vault ID is omitted, then:

$ ansible-vault create --vault-id @prompt first_encrypted1.yml

$ ansible-vault create --vault-id a.txt  second_encrypted1.yml
$ ansible-vault create --vault-id @a.txt  second_encrypted1.yml

$ ansible-vault create --vault-id a.sh   third_encrypted1.yml
$ ansible-vault create --vault-id @a.sh   third_encrypted1.yml
           

When you want to access encrypted data, such as the ansible command, ansible-playbook command, and ansible-vault command, you need to specify the same vault ID as the encrypted data, and you can specify multiple vaults.

For example:

# 以文件的方式获取凭据密码
ansible-vault view --vault-id [email protected]  second_encrypted.yml

# 以交互式方式提供凭据密码
ansible-vault view --vault-id id2@prompt  second_encrypted.yml

# 指定多个凭据密码,通常用在ansible-playbook命令中,
# 比如变量文件中多个敏感数据使用了不同的vault id加密
ansible-playbook --vault-id [email protected] --vault-id [email protected] main.yml
           

3. Encrypt existing files

If you want to encrypt an already existing file, use ansible-vault encrypt, which is almost the same as the create subcommand.

For example, plain.yml file is currently a plaintext variable file:

$ cat plain.yml
---
plain_passwd: 123456
port: 2312
           

of Encryption:

$ ansible-vault encrypt --vault-id [email protected] plain.yml
Encryption successful

$ cat plain.yml
$ANSIBLE_VAULT;1.2;AES256;id1
336130623037643739633938313061336431
363838333864616665623034336439316234
643337663235366365316332643063653863
3534303533633334340a6438326433616236
383566346533653135313633633139613133
353935653734363833616464326662336132
           

Multiple copies of data can be loaded at once, but they use the same Vault ID and password.

$ ansible-vault encrypt --vault-id [email protected] foo.yml bar.yml baz.yml
           

4. The meaning of the encryption protocol header

In each encrypted file, a line of header data is included. You can use the cat command to view this, for example:

$ cat plain.yml
$ANSIBLE_VAULT;1.2;AES256;id1        #<==加密协议头
336130623037643739633938313061336431
363838333864616665623034336439316234
643337663235366365316332643063653863
3534303533633334340a6438326433616236
383566346533653135313633633139613133
353935653734363833616464326662336132
           

There are two types of protocol headers:

$ANSIBLE_VAULT;1.1;AES256
$ANSIBLE_VAULT;1.2;AES256;id1
           

The first of these fields can only be $ANSIBLE_VAULT at the moment.

The second field 1.1, 1.2 represents the version number of the vault format, which is 1.2 if the vault ID is given, or 1.1 if the default vault ID is used. The old version still has version 1.0, but now it will not be set to this version number, and the encrypted data of version 1.0 can only be read.

The third field currently only supports AES256 encryption.

The fourth field, if the vault ID is specified during encryption, the fourth field is saved as the vault ID, otherwise the fourth field is not set.

If you forget the vault ID of an encrypted file, you can directly view the encrypted file with the cat command to obtain the ID, or you can extract it directly.

$ awk -F';' 'NR==1{print $4}' encrypted.yml
id2
           

If Ansible needs to access multiple vault-encrypted files, it will automatically look for the user-provided credentials and passwords based on the vault ID.

For example, for the following command, two vault IDs and two passwords are naturally required if two encrypted variable files are referenced in the main.yml. When the first file needs to be decrypted during the execution of a task, Ansible will automatically obtain its vault ID and look for the corresponding vault ID and the source of its credential password from the command line.

$ ansible-playbook --vault-id [email protected] --vault-id [email protected] main.yml
           

5. Decrypt encrypted files

For a vault-encrypted file, the file can be decrypted using ansible-vault decrypt.

For example, to decrypt the encrypted plain.yml in the example above:

$ ansible-vault decrypt --vault-id [email protected] plain.yml
Decryption successful

$ cat plain.yml
---
plain_passwd: 123456
port: 2312
           

Decrypt multiple files:

$ ansible-vault decrypt --vault-id [email protected] foo.yml bar.yml baz.yml
           

6. Change the vault ID and password

For a vault-encrypted file, you can use the ansible-vault rekey to modify its vault ID or its credential password.

For example, first_passwd.yml is an encrypted file:

$ cat first_encrypted.yml
$ANSIBLE_VAULT;1.2;AES256;id1
6361623434363834363065643838346361383830
3038366239353535663330653463376666346636
6439663030643839623965613563343632343234
3739313638363262390a62666466343864626466
3235633862306366383161663765663962306132
           

To modify its Vault ID and credential password:

$ ansible-vault rekey --vault-id [email protected] \
                      --new-vault-id [email protected] \
                      first_encrypted.yml
Rekey successful
           

Looking at the encrypted content, both the vault ID and the ciphertext have changed:

$ cat first_encrypted.yml
$ANSIBLE_VAULT;1.2;AES256;id2
366666343166313633396135346464653537373936
343936653437616235316630333538343139336361
656330646535656335376662623062343863306561
3033383533306266620a3866396139663235303931
313230666235303833633330653863323738623339
           

Of course, you can also modify only the Vault ID or just the credential password, just leave one of the label@xxx unchanged. For example:

# Vault ID不变,只修改凭据密码
$ ansible-vault rekey --vault-id [email protected] \
                      --new-vault-id [email protected] \
                      first_encrypted.yml
                      
# 凭据密码,只修改Vault ID
$ ansible-vault rekey --vault-id [email protected] \
                      --new-vault-id [email protected] \
                      first_encrypted.yml
           

7. Edit the contents of the encrypted file

If you want to edit the contents of a vault-encrypted file, use the ansible-vault edit command.

$ ansible-vault edit --vault-id [email protected] first_encrypted.yml
           

It will automatically open the default editor (like vim) for the user to edit. The internal process is to create a temporary file with the original plaintext data (when opening vim, the temporary file name is seen in the status bar at the bottom of vim), and the operations modified by the user are carried out in this temporary file, and when saved, the original file will be automatically encrypted and overwritten.

It can also be decrypted, modified, and re-encrypted, and it can be done in a non-interactive way.

ansible-vault decrypt xxx
sed -i 'xx' xxx
ansible-vault encrypt xxx
           

8. Use Vault to encrypt files in the ansible-playbook task

The original goal of encrypting data is to hide sensitive data in playbook, task, or variable files. What about files that use encryption?

For example, a playbook file named test.yml would look like this:

---
- hosts: localhost
  gather_facts: no
  vars_files: 
    - first_passwd.yml
    - second_passwd.yml
  tasks: 
    - name: debug var in first_passwd
      debug: 
        var: passwd1
    - name: debug var in second_passwd
      debug: 
        var: passwd2
           

In this playbook, two variable files first_passwd.yml and second_passwd.yml are introduced, and then two tasks are used to access the two variables defined in the two variable files, passwd1 and passwd2.

The contents of these two variable files are as follows:

# first_passwd.yml内容:
---
passwd1: "passwd in first_passwd"

# second_passwd.yml内容:
---
passwd2: "passwd in second_passwd"
           

Encrypt these two files with a different Vault ID and credential password:

$ echo 'abcdef' >a.txt
$ echo 'ABCDEF' >b.txt

$ ansible-vault encrypt --vault-id [email protected] first_passwd.yml
$ ansible-vault encrypt --vault-id [email protected] second_passwd.yml
           

Then execute the playbook, because there are multiple encrypted files to be accessed, and the vault ID is different, and the credential password is also different, so you need to specify multiple --vault-id options. As follows:

$ ansible-playbook --vault-id [email protected] --vault-id [email protected] test.yml
......
TASK [debug var in first_passwd] *********************
ok: [localhost] => {
    "passwd1": "passwd in first_passwd"
}

TASK [debug var in second_passwd] ********************
ok: [localhost] => {
    "passwd2": "passwd in second_passwd"
}
......
           

9. Encrypt the string and embed the YAML file

With ansible-vault encrypt_string you can encrypt a string.

For example:

$ ansible-vault encrypt_string --vault-id [email protected] 'hello' --name 'mysql_pass' 
mysql_pass: !vault |
          $ANSIBLE_VAULT;1.2;AES256;id1
          39623437656130313338613033383464376437
          66303938343635623430353664303334623138
          33353435366262653437663839316261623664
          6362633233653237360a343439376437633733
          6262
Encryption successful
           

where hello is the plaintext data that needs to be encrypted,-- name mysql_pass is the key in YAML, for example, it can be used as the name of a variable. The --name option can also be omitted.

$ ansible-vault encrypt_string --vault-id [email protected] 'hello'
!vault |
          $ANSIBLE_VAULT;1.2;AES256;id1
          61336334663365656361316532356133623
          62343863366463396338333933343635373
          39656461643962643831653561313266366
          6432316334653339340a336565333361346
          3635
Encryption successful
           

After encrypting a string, you can copy the encrypted string to the YAML file. For example, here is a variable file for MySQL Role:

---
mysql_port: 3306
mysql_user: root
mysql_pass: !vault |
          $ANSIBLE_VAULT;1.2;AES256;id1
          39623437656130313338613033383464376437
          66303938343635623430353664303334623138
          33353435366262653437663839316261623664
          6362633233653237360a343439376437633733
          6262
mysql_host: 192.168.200.27
           

In addition, the ansible-vault encrypt_string can also get the plaintext data to be encrypted from the standard input:

$ ansible-vault encrypt_string --vault-id [email protected] --stdin-name 'mysql_pass' <<<"hello"
mysql_pass: !vault |
          $ANSIBLE_VAULT;1.2;AES256;id1
          35386538393939633862626361323735306
          63383966356265333336303231313735376
          38346566373262663866363631373539313
          6531643131306537350a363232656661303
          6333
Encryption successful

# 或者
$ echo -n 'hello' | ansible-vault encrypt_string --vault-id [email protected] --stdin-name 'mysql_pass'
           

Note that when getting data to be encrypted from standard input, the option used is --stdin-name, not --stdin --name.

10. Speed up the process of encryption and decryption

If you want to encrypt and decrypt a large number of files, according to the official documentation, you can install the cryptography package to solve the problem.

pip install cryptography
           

11. Vault encryption best practices

Ansible Vault can encrypt all files in yaml and json formats, but it is not recommended to directly encrypt a file that contains a lot of data, because it is inconvenient to view and modify task files after encryption, and it is recommended to encrypt files that only contain sensitive data.

For example, consider the following variable file:

---
mysql_port: 3306
mysql_user: root
mysql_pass: xxxxxxxx
mysql_host: 192.168.200.27
           

In fact, the file only mysql_pass sensitive data that you want to hide, and the recommended approach is to use jinja2 in this file to refer to the variable that starts with the vault_ again, and the vault_ starts so that you can see at a glance that this is the variable encrypted by Vault. For example:

---
mysql_port: 3306
mysql_user: root
mysql_pass: "{{vault_mysql_pass}}"
mysql_host: 192.168.200.27
           

The referenced variable is then defined in a separate file vault_mysql_pass and the variable file is encrypted. For example, define vault_mysql_pass variables in a mysql_pass.yml file:

---
vault_mysql_pass: "abcdef"
           

of Encryption:

$ ansible-vault encrypt --vault-id [email protected] mysql_pass.yml           

Link: https://www.cnblogs.com/f-ck-need-u/p/17718508.html

(The copyright belongs to the original author, invaded and deleted)

Pay attention to the good of the industry: IT operation and maintenance base camp, and get the 60 G "Network Work + System Gift Package"

Read on