Skip to content

Commit 2ecc705

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Add a collection for managing encryption of secret data"
2 parents cfe86ab + 6a600eb commit 2ecc705

File tree

20 files changed

+780
-0
lines changed

20 files changed

+780
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ logs/*
4545
# OS generated files #
4646
######################
4747
._*
48+
.ansible
4849
.tox
4950
*.egg-info
5051
.eggs

doc/source/encrypt_secrets.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
.. include:: ../../encrypt_secrets/README.rst

doc/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ OpenStack-Ansible Operator Tooling
88
swift_storage_mount_drives
99
elk_metrics
1010
mcapi
11+
encrypt_secrets
1112

1213
OpenStack-Ansible Diff Generator
1314
--------------------------------

encrypt_secrets/README.rst

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
==================
2+
Encrypting secrets
3+
==================
4+
5+
This document describes the supported operations for encrypting secrets and explains how to perform them using the appropriate tooling.
6+
7+
Ansible-Vault
8+
=============
9+
10+
OpenStack-Ansible provides tooling to encrypt and rotate secret files and keypairs using Ansible Vault.
11+
12+
Role Defaults
13+
-------------
14+
15+
.. literalinclude:: ../../encrypt_secrets/roles/ansible_vault/defaults/main.yml
16+
:language: yaml
17+
:start-after: under the License.
18+
19+
Installing the Collection
20+
-------------------------
21+
22+
To install the collection, define it in your region deployment configuration file, located at `/etc/openstack_deploy/user-collection-requirements.yml`, as shown below:
23+
24+
.. code-block:: yaml
25+
26+
- name: osa_ops.encrypt_secrets
27+
type: git
28+
version: master
29+
source: https://opendev.org/openstack/openstack-ansible-ops#/encrypt_secrets
30+
31+
Then, run `./scripts/bootstrap-ansible.sh` to install the collection.
32+
33+
Initial Encryption of Secret Files
34+
----------------------------------
35+
36+
When initializing a region for the first time, you should encrypt secrets and generated private keys before storing them in Git. You can perform this process locally or on the deployment host.
37+
38+
.. NOTE::
39+
40+
You must re-run the encryption process whenever new services or keypairs are generated, which may occur at later deployment stages.
41+
42+
Encrypting Secrets Locally
43+
~~~~~~~~~~~~~~~~~~~~~~~~~~
44+
45+
The process for encrypting secrets locally is similar to running it on the deploy host, but some context-specific variables required by OpenStack-Ansible may be unavailable and must be supplied manually.
46+
47+
Ensure you have a Python virtual environment with Ansible installed before proceeding.
48+
49+
1. Generate a password for the Ansible Vault and store it securely:
50+
51+
.. code-block:: bash
52+
53+
pwgen 36 1 > /tmp/vault.secret
54+
55+
2. Run the encryption playbook:
56+
57+
.. code-block:: bash
58+
59+
ansible-playbook osa_ops.encrypt_secrets.ansible_vault -e ansible_vault_region=${REGION_NAME} -e ansible_vault_pw=/tmp/vault.secret
60+
61+
3. Copy the contents of `/tmp/vault.secret` to the deployment host, for example to `/etc/openstack/vault.secret`.
62+
4. Define the vault secret path in `/etc/openstack_deploy/user.rc`:
63+
64+
.. code-block:: bash
65+
66+
export ANSIBLE_VAULT_PASSWORD_FILE=/etc/openstack/vault.secret
67+
68+
5. Store the password securely in your preferred password manager.
69+
6. Push the changes to your Git repository.
70+
7. Ensure that the deploy host decrypts any required secrets.
71+
72+
Encrypting Secrets on the Deployment Host
73+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74+
75+
Follow these steps to encrypt secrets directly on the deployment host:
76+
77+
1. Generate a password and store it securely:
78+
79+
.. code-block:: bash
80+
81+
pwgen 36 1 > /etc/openstack/vault.secret
82+
83+
2. Define the vault secret path in `/etc/openstack_deploy/user.rc`:
84+
85+
.. code-block:: bash
86+
87+
export ANSIBLE_VAULT_PASSWORD_FILE=/etc/openstack/vault.secret
88+
89+
3. Run the encryption playbook:
90+
91+
.. code-block:: bash
92+
93+
openstack-ansible osa_ops.encrypt_secrets.ansible_vault
94+
95+
4. Commit and push changes to `/etc/openstack_deploy` in your Git repository.
96+
5. Save the vault password (`/etc/openstack/vault.secret`) in a secure password manager.
97+
6. Decrypt any necessary secrets before running OpenStack playbooks.
98+
99+
100+
Decrypting Keypairs on the Deploy Host
101+
--------------------------------------
102+
103+
The OpenStack-Ansible PKI role does not support storing private keys in encrypted format on the deployment host. Instead, configure a pipeline that decrypts the keys after placing them on the deploy host.
104+
105+
Encrypted keypairs should be committed to the Git repository, but stored unencrypted on the deployment host.
106+
107+
To decrypt them, run the following playbook:
108+
109+
.. code-block:: bash
110+
111+
openstack-ansible osa_ops.encrypt_secrets.ansible_vault -e ansible_vault_action=decrypt
112+
113+
114+
Rotating the Ansible Vault Secret
115+
---------------------------------
116+
117+
Rotating the Ansible Vault password requires re-encrypting all secrets in the repository. Assuming the original password is stored in `/tmp/vault.secret`, follow these steps:
118+
119+
1. Generate a new vault password/encryption key:
120+
121+
.. code-block:: bash
122+
123+
pwgen 45 1 > /tmp/vault.secret.new
124+
125+
2. Re-encrypt all secrets using the new password:
126+
127+
.. code-block:: bash
128+
129+
ANSIBLE_VAULT_PASSWORD_FILE=/tmp/vault.secret ansible-playbook osa_ops.encrypt_secrets.ansible_vault -e ansible_vault_action=rotate
130+
131+
3. Transfer the new password to the deployment host and store it securely in a password manager.

encrypt_secrets/galaxy.yml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
### REQUIRED
2+
# The namespace of the collection. This can be a company/brand/organization or product namespace under which all
3+
# content lives. May only contain alphanumeric lowercase characters and underscores. Namespaces cannot start with
4+
# underscores or numbers and cannot contain consecutive underscores
5+
namespace: osa_ops
6+
7+
# The name of the collection. Has the same character restrictions as 'namespace'
8+
name: encrypt_secrets
9+
10+
# The version of the collection. Must be compatible with semantic versioning
11+
version: 0.1.0
12+
13+
# The path to the Markdown (.md) readme file. This path is relative to the root of the collection
14+
readme: README.md
15+
16+
# A list of the collection's content authors. Can be just the name or in the format 'Full Name <email> (url)
17+
# @nicks:irc/im.site#channel'
18+
authors:
19+
- Dmitriy Rabotyagov <[email protected]>
20+
21+
22+
### OPTIONAL but strongly recommended
23+
# A short summary description of the collection
24+
description: Encrypt and manage encrypted files for OpenStack-Ansible
25+
26+
# Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only
27+
# accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file'
28+
license:
29+
- Apache-2.0
30+
31+
# The path to the license file for the collection. This path is relative to the root of the collection. This key is
32+
# mutually exclusive with 'license'
33+
license_file: ''
34+
35+
# A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character
36+
# requirements as 'namespace' and 'name'
37+
tags: []
38+
39+
# Collections that this collection requires to be installed for it to be usable. The key of the dict is the
40+
# collection label 'namespace.name'. The value is a version range
41+
# L(specifiers,https://python-semanticversion.readthedocs.io/en/latest/#requirement-specification). Multiple version
42+
# range specifiers can be set and are separated by ','
43+
dependencies: {}
44+
45+
# The URL of the originating SCM repository
46+
repository: https://opendev.org/openstack/openstack-ansible-ops
47+
48+
# The URL to any online docs
49+
documentation: https://docs.openstack.org/openstack-ansible-ops
50+
51+
# The URL to the homepage of the collection/project
52+
homepage: https://docs.openstack.org/openstack-ansible
53+
54+
# The URL to the collection issue tracker
55+
issues: https://bugs.launchpad.net/openstack-ansible
56+
57+
# A list of file glob-like patterns used to filter any files or directories that should not be included in the build
58+
# artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This
59+
# uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry',
60+
# and '.git' are always filtered
61+
build_ignore: []
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
3+
- name: Encrypt secrets
4+
hosts: encrypt-default
5+
tasks:
6+
7+
- name: Importing ansible_vault role
8+
ansible.builtin.import_role:
9+
name: ansible_vault
10+
vars:
11+
ansible_vault_action: encrypt
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
dependency:
3+
name: galaxy
4+
options:
5+
requirements-file: requirements.yml
6+
7+
driver:
8+
name: docker
9+
10+
platforms:
11+
- name: "encrypt-${MOLECULE_SCENARIO_NAME}"
12+
image: "${DOCKER_REGISTRY:-quay.io/gotmax23}/${DOCKER_IMAGE_TAG:-debian-systemd:bookworm}"
13+
command: ${DOCKER_COMMAND:-""}
14+
pre_build_image: true
15+
privileged: true
16+
systemd: true
17+
18+
provisioner:
19+
name: ansible
20+
lint:
21+
name: ansible-lint
22+
env:
23+
ANSIBLE_ROLES_PATH: ../../roles
24+
inventory:
25+
group_vars:
26+
all:
27+
ansible_vault_repo_path: /etc/openstack_deploy
28+
ansible_vault_pw: /etc/openstack_deploy/vault_pw
29+
ansible_vault_region: molecule
30+
_molecule_password_mapping:
31+
keystone_container_mysql_password: oequ0iejahgh8amaiy3Qua1Moo3weicaazo4
32+
keystone_auth_admin_password: chaumei2Hoh5eisiesaip5goodees9eesahs
33+
keystone_oslomsg_rpc_password: ei6Ooraenuavahleijuv3oos7asheih6Aidi
34+
config_options:
35+
defaults:
36+
inject_facts_as_vars: false
37+
38+
scenario:
39+
name: default
40+
test_sequence:
41+
- dependency
42+
- cleanup
43+
- destroy
44+
- syntax
45+
- create
46+
- prepare
47+
- converge
48+
- idempotence
49+
# NOTE: We don't use side-effect due to bug preventing to define multiple of them:
50+
# https://github.com/ansible/molecule/issues/3617
51+
- verify verify_converge.yml
52+
- verify verify_rotate.yml
53+
- verify verify_decrypt.yml
54+
- cleanup
55+
- destroy
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
- name: Generate data for role verification
3+
hosts: encrypt-default
4+
tasks:
5+
- name: Install required packages
6+
ansible.builtin.package:
7+
name:
8+
- python3-cryptography
9+
- ansible-core
10+
update_cache: "{{ (ansible_facts['os_family'] | lower == 'debian') | ternary(true, omit) }}"
11+
12+
- name: Create required directories
13+
ansible.builtin.file:
14+
path: "{{ item }}"
15+
state: directory
16+
recurse: true
17+
mode: "0755"
18+
loop:
19+
- /etc/openstack_deploy/pki/certs/private
20+
- /etc/openstack_deploy/pki/certs/certs
21+
- /etc/openstack_deploy/pki/roots/TestRoot/private
22+
- /etc/openstack_deploy/ssh_keypairs
23+
24+
- name: Generate ansible-vault secrets to use for data encryption
25+
ansible.builtin.copy:
26+
content: "{{ item.content }}"
27+
dest: "{{ item.dest }}"
28+
mode: "0600"
29+
loop:
30+
- dest: /etc/openstack_deploy/vault_pw
31+
content: "{{ lookup('ansible.builtin.password', '/dev/null', chars=['ascii_lowercase', 'digits'], length=32) }}"
32+
- dest: /etc/openstack_deploy/vault_pw.new
33+
content: "{{ lookup('ansible.builtin.password', '/dev/null', chars=['ascii_lowercase', 'digits'], length=32) }}"
34+
- dest: /etc/openstack_deploy/user_secrets.yml
35+
content: |
36+
---
37+
{{ _molecule_password_mapping | to_yaml }}
38+
39+
- name: Generate private keys
40+
community.crypto.openssl_privatekey:
41+
path: "{{ item }}"
42+
loop:
43+
- /etc/openstack_deploy/pki/certs/private/noop.key.pem
44+
- /etc/openstack_deploy/pki/roots/TestRoot/private/TestRoot.key.pem
45+
46+
- name: Generate test certificate
47+
community.crypto.x509_certificate:
48+
path: /etc/openstack_deploy/pki/certs/certs/noop.crt
49+
privatekey_path: /etc/openstack_deploy/pki/certs/private/noop.key.pem
50+
provider: selfsigned
51+
52+
- name: Generate ssh keypair
53+
community.crypto.openssh_keypair:
54+
path: /etc/openstack_deploy/ssh_keypairs/noop_keypair

0 commit comments

Comments
 (0)