Ansible: шифрование и копирование PEM-ключа

Автор: | 02/05/2019
 

Задача — во время выполнения роли скопировать на удалённый хост PEM-ключ.

Для этого используем ansible-vault.

Идея состоит в том, что мы зашифруем содержимое ключа в переменную, а затем используем модуль copy — и создадим на удалённой системе новый файл.

Создание сертификата

Сначала создадим сам сертификат и приватный ключ с помощью openssl:

openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem

Проверяем содержимое:

openssl x509 -text -noout -in certificate.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
44:f4:a2:d1:c5:03:a8:f4:78:34:6a:b3:26:45:ef:23:07:dd:a2:34
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = AU, ST = Some-State, O = Internet Widgits Pty Ltd
Validity
Not Before: Feb  5 09:10:00 2019 GMT
Not After : Feb  5 09:10:00 2020 GMT
Subject: C = AU, ST = Some-State, O = Internet Widgits Pty Ltd
...

Шифрование данных

Сначала создадим файл, в котором  будет храниться пароль для расшифровки данных:

cat ~/.ssh/mobilebackend_aws_credentials.yml
oigh7fengeiG

Далее этот файл будет сохранён в Jenkins как Secret file, и будет использоваться для расшифровки данных Ansible:

Создаём второй файл — сюда добавим имя переменной и сам ключ.

Создаём каталог для файлов в роли deploy:

mkdir roles/deploy/files

Создаём в нём новый файл — apns-{{ env }}.pem.yml.

В переменную {{ env }} подставим значение, переданное из group_vars/hostname.yml, т.е. dev, stage или production:

cat group_vars/mobilebackend-dev.yml | grep -w env
env: "dev"

Вызываем ansible-vault с опцией create, указываем путь к файлу с паролем:

ansible-vault create roles/deploy/files/apns-dev.pem.yml --vault-password-file ~/.ssh/mobilebackend_aws_credentials.yml

Откроется окно редактирования, вносим имя переменной и значение:

Сохраняем и закрываем файл — и Ansible зашифрует его:

head -5 roles/deploy/files/apns-dev.pem.yml
ANSIBLE_VAULT;1.1;AES256
63313035646361303936636338313837353133623461363661363232316361623662316366343833
3335363539653530396336663936653262663534663339360a316364326335306562306666373737
31646538303038613063613837383231633039626561626666353338653662633661613230656463
3731653433383932330a353538323664633337653638393563343132626430663131373738653035

Если надо отредактировать файл после создания — используем edit:

ansible-vault edit roles/deploy/files/apns-dev.pem.yml --vault-password-file ~/.ssh/mobilebackend_aws_credentials.yml

Другой вариант — сначала создать plaintext-файл roles/deploy/files/apns-dev.pem.yml, а потом зашифровать его с помощью encrypt:

ansible-vault encrypt roles/deploy/files/apns-dev.pem.yml --vault-password-file ~/.ssh/mobilebackend_aws_credentials.yml

Ansible copy

Последним шагом остаётся добавить задачу копирования файла.

Разбиваем её на две части — в одной используем include_vars, в получаем значение переменной из roles/deploy/files/apns-dev.pem.yml, во второй — создадим на удалённой системе новый файл с текстом из переменной apns_certificate_key.

Проверяем возможность прочитать переменную вообще.

Тут Ansible вызывается скриптом из поста AWS: миграция RTFM 3.0 (final) – CloudFormation и Ansible роли, в котором есть функция ansible_exec():

...
ansible_exec () {

    local tags=$1
    local env=$2
    local vault=$3
    local rsa=$4
    local connection=$5
    local application=$6
    ansible-playbook --private-key $rsa --tags "$1" --limit=$env mobilebackend.yml --vault-password-file $vault --extra-vars "$connection $application"
}
...

Запускаем, проверяем:

./ansible_exec.sh -t deploy
Tags: deploy
Env: mobilebackend-dev
Vault: /home/setevoy/.ssh/mobilebackend_aws_credentials.yml
RSA: /home/setevoy/Work/example/aws-credentials/bm-backend-dev.pem
APP:
Are you sure to proceed? [y/n] y
...
TASK [deploy : Check certificate content] ****
ok: [dev.backend-app2-internal.example.world] => {
"msg": "Certificate body: -----BEGIN PRIVATE KEY-----\nMII***LJH\n-----END PRIVATE KEY-----\n"
...

Хорошо — переменная считалась, тело сертификата получили — добавляем задачу с copy:

...
- name: "Deploy APNS certificate"
  copy:
    dest: "/data/projects/{{ backend_project_name }}/common/config/apns-certificate.pem"
    content: "{{ apns_certificate_key }}"
    owner: "{{ backend_project_name }}"
    group: "{{ backend_project_name }}"
    mode: 0400
  when: "'yoga' in backend_project_name and 'console' in inventory_hostname"
...

Тут в dest указываем путь на удалённой системе, а в content — подставляем значение из переменной apns_certificate_key.

Задаём пользователя, группу и права — {{ backend_project_name }} и права 400.

В условии when проверяем какой проект деплоится, т.к. создавать ключи нам надо только для проекта «yoga» и на хосте в имени которого есть слово console (dev.backend-console-internal.example.world) в переменной inventory_hostname.

Имя проекта передаётся через переменную APP_PROJECT_NAME джобы в Jenkins, и считывается в плейбуке проекта:

...
    - role: deploy
      tags: deploy
      backend_prodject_git_branch: "{{ lookup('env','APP_REPO_BRANCH') }}"
      backend_project_git_repo: "{{ lookup('env','APP_REPO_RUL') }}"
      backend_project_name: "{{ lookup('env','APP_PROJECT_NAME') }}"
      when: "'backend-bastion' not in inventory_hostname"

Сейчас задаём переменную вручную:

export APP_PROJECT_NAME=yoga

Запускаем:

...
TASK [deploy : Read APNS certificate from the apns-dev.pem.yml] ****
ok: [dev.backend-app1-internal.example.world]
skipping: [dev.backend-bastion.examplee.world]
ok: [dev.backend-app2-internal.example.world]
ok: [dev.backend-console-internal.example.world]
TASK [deploy : Check certificate content] ****
ok: [dev.backend-app1-internal.example.world] => {
"msg": "Certificate body: -----BEGIN PRIVATE KEY-----\nMII***LJH\n-----END PRIVATE KEY-----\n"
}
...
TASK [deploy : Deploy APNS certificate] ****
...
skipping: [dev.backend-bastion.example.world]
changed: [dev.backend-console-internal.example.world]
...

Проверяем на удалённом хосте наличие файла, пользователя и права доступа:

root@bttrm-dev-console:/home/admin/Scripts# ll /data/projects/yoga/common/config/apns-certificate.pem
-r-------- 1 yoga yoga 1704 Feb  5 12:06 /data/projects/yoga/common/config/apns-certificate.pem

И содержимое:

root@bttrm-dev-console:/home/admin/Scripts# head -5 /data/projects/yoga/common/config/apns-certificate.pem
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCziHZIiFYYriVb
hRWDN2cR5j6wHpK3+CEc2rXl+Umc6m8el/jcJX3uVIckfmFfsapvpSO3MD0gkhKI
knJw48rhUlk3FUsTF7/pmstYIAxu7APc5nphiTt6YyNlp8L1xc0tKCuxSvcLDmR8
OiMbGE5Rm9EXXi4E6UAibFe9VQfZSsle9pzZCC+lDD4Rh1TLjOju/w4fnXprXkDY

Готово.