Best practices should referring to at least basic encryption of data including SSH keypairs, PKI certificates, user_secrets, etc. This collection aims to help/assist with managing data in encrypted state, in case ansuble_vault is used as an encryption mechanism. The collection should allow adding more supproted mechanism, like SOPS for managing data encryption in the future. Change-Id: I8af3118946682af4ec31bb1d4f6bea93be34f68c
96 lines
3.2 KiB
YAML
96 lines
3.2 KiB
YAML
---
|
|
# Copyright 2025, Advanced Hosters B.V.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
- name: Try to read secrets file as yaml or fallback to decrypt secrets
|
|
block:
|
|
- name: Fetch the file
|
|
ansible.builtin.slurp:
|
|
src: "{{ file['stat']['path'] }}"
|
|
register: _secrets_file
|
|
|
|
- name: Read file as unencrypted
|
|
ansible.builtin.set_fact:
|
|
_secrets: "{{ _secrets_file['content'] | b64decode | from_yaml }}"
|
|
|
|
rescue:
|
|
- name: Skipping file as it is likely already encrypted
|
|
ansible.builtin.debug:
|
|
msg: "We failed to read file as YAML, which means that it's likely already encrypted. Skipping..."
|
|
when:
|
|
- ansible_vault_action == "encrypt"
|
|
|
|
- name: Loading encrypted variables to re-encrypt
|
|
ansible.builtin.include_vars:
|
|
file: "{{ file['stat']['path'] }}"
|
|
when:
|
|
- ansible_vault_action == "rotate"
|
|
|
|
- name: Read current secrets file for rotation
|
|
vars:
|
|
_secret_vars: "{{ _secrets_file['content'] | b64decode | regex_findall('(.*):\\s!vault\\s\\|\\n') }}"
|
|
ansible.builtin.set_fact:
|
|
_secrets: |-
|
|
{% set secrets_mapping = {} %}
|
|
{% for var in _secret_vars %}
|
|
{% set _ = secrets_mapping.update({var: hostvars['localhost'][var]}) %}
|
|
{% endfor %}
|
|
{{ secrets_mapping }}
|
|
when:
|
|
- ansible_vault_action == "rotate"
|
|
|
|
always:
|
|
- name: Encrypt individual secrets from unencrypted file
|
|
ansible.builtin.command:
|
|
argv:
|
|
- "{{ ansible_vault_binary }}"
|
|
- encrypt_string
|
|
- --vault-id
|
|
- "{{ ansible_vault_region | upper }}@{{ _ansible_vault_encrypt_file }}"
|
|
- --encrypt-vault-id
|
|
- "{{ ansible_vault_region | upper }}"
|
|
- '{{ item.value }}'
|
|
- --name
|
|
- "{{ item.key }}"
|
|
with_dict: "{{ _secrets | default({}) }}"
|
|
no_log: true
|
|
register: new_secrets
|
|
changed_when: false
|
|
|
|
- name: Place encrypted secrets in-place
|
|
ansible.builtin.copy:
|
|
content: "---\n{{ new_secrets.results | map(attribute='stdout') | join('\n') }}\n"
|
|
dest: "{{ file['stat']['path'] }}"
|
|
mode: "0600"
|
|
when:
|
|
- _secrets is defined
|
|
- _secrets | length > 0
|
|
- ansible_vault_in_place_copy
|
|
|
|
- name: Place encrypted secrets in independent blocks
|
|
ansible.builtin.blockinfile:
|
|
block: "{{ item['stdout'] }}"
|
|
dest: "{{ file['stat']['path'] }}"
|
|
marker: "# {mark} ANSIBLE MANAGED {{ item.item['key'] }}"
|
|
mode: "0600"
|
|
loop: "{{ new_secrets.results }}"
|
|
when:
|
|
- _secrets is defined
|
|
- _secrets | length > 0
|
|
- not ansible_vault_in_place_copy
|
|
|
|
- name: Undefine the secrets variable
|
|
ansible.builtin.set_fact:
|
|
_secrets: {}
|