Separate backend-specific logic to improve extensibility

Although the role is intended to support multiple backends, in practice
it was tightly coupled with the standalone backend. Several tasks in the
"generic" parts were specific to the standalone case, and the
distinction between global and backend-specific variables was unclear.

This patch improves the separation between generic and backend-specific
logic, making it easier to add and maintain additional backends in the
future.

Change-Id: I9124c85099e1338c2cb2dcef971475eaf3a5860d
Signed-off-by: Damian Dabrowski <damian.dabrowski@cleura.com>
This commit is contained in:
Damian Dabrowski
2025-05-02 13:36:17 +02:00
parent 3e6f12b660
commit 7d35268e1d
10 changed files with 131 additions and 159 deletions

View File

@@ -72,11 +72,6 @@ pki_search_install_ca_pattern: "pki_install_ca_"
# set this to the name of a CA to regenerate, or to 'true' to regenerate all
pki_regen_ca: ""
# locations of system trust stores to install CA certs to
pki_trust_store_location:
apt: /usr/local/share/ca-certificates/
dnf: /etc/pki/ca-trust/source/anchors/
# Server certificates to create
pki_certificates: []
@@ -112,15 +107,6 @@ pki_setup_host: localhost
# Python interpreter that will be used during cert generation
pki_setup_host_python_interpreter: "{{ (pki_setup_host == 'localhost') | ternary(ansible_playbook_python, ansible_facts['python']['executable']) }}"
# base directory for the CA and server certificates
pki_dir: "/etc/pki"
# subdirectories to be created for holding CA certs/keys/csr
pki_ca_dirs: "{{ _pki_ca_dirs }}"
# subdirectories to be created for holding server certs/keys/csr
pki_cert_dirs: "{{ _pki_cert_dirs }}"
# certificates to install
pki_install_certificates: []
@@ -148,15 +134,18 @@ pki_search_install_certificates_pattern: "pki_install_certificates_"
# group: "myservice"
# mode: "0644"
# default backend used to create the certificates
# NOTE(damiandabrowski): Remove backwards compatbility with pki_method after 2026.1
pki_backend: "{{ pki_method | default(openstack_pki_backend | default('standalone')) }}"
# Handlers naming
pki_handler_ca_changed: "ca cert changed"
pki_handler_cert_changed: "cert changed"
pki_handler_cert_installed: "cert installed"
# default backend used to create the certificates
# NOTE(damiandabrowski): Remove backwards compatbility with pki_method after 2026.1
pki_backend: "{{ pki_method | default(openstack_pki_backend | default('standalone')) }}"
# standalone backend variables
# base directory for the CA and server certificates
pki_dir: "/etc/pki"
# Default permissions used on pki_setup_host
# pki_owner: "root"
# pki_group: "root"

View File

@@ -13,29 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
- name: Include {{ pki_backend }} CA variables
ansible.builtin.include_vars:
file: "{{ pki_backend ~ '_ca.yml' }}"
- name: Create PKI directories
ansible.builtin.file:
state: directory
path: "{{ item.path }}"
owner: "{{ item.owner | default(pki_owner) | default(omit) }}"
group: "{{ item.group | default(pki_group) | default(omit) }}"
mode: "{{ item.mode | default('0755') }}"
with_items:
- "{{ pki_ca_dirs }}"
- "{{ pki_cert_dirs }}"
delegate_to: "{{ pki_setup_host }}"
check_mode: false
- name: Create certificate authorities
ansible.builtin.include_tasks: "{{ pki_backend }}/create_ca.yml"
ansible.builtin.include_tasks: "{{ ca.backend | default(pki_backend) }}/create_ca.yml"
loop: "{{ _pki_ca_defs }}"
loop_control:
loop_var: ca
vars:
ca_dir: "{{ pki_dir }}/roots/{{ ca.name }}"
ca_cert_prefix: "{{ ca_dir ~ '/certs/' ~ ca.name }}"
when: pki_create_ca | bool

View File

@@ -13,10 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
- name: Include {{ pki_backend }} CA variables
ansible.builtin.include_vars:
file: "{{ pki_backend ~ '_ca.yml' }}"
- name: Install certificate authorities
ansible.builtin.include_tasks: "{{ pki_backend }}/install_ca.yml"
when: _pki_install_ca_defs | length > 0
ansible.builtin.include_tasks: "{{ ca_backend | default(pki_backend) }}/install_ca.yml"
loop: "{{ _pki_install_ca_defs }}"
loop_control:
loop_var: ca
vars:
ca_backend: "{{ (_pki_ca_defs | selectattr('name', 'equalto', ca.name) | map(attribute='backend') | first) }}"

View File

@@ -13,75 +13,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
- name: Create Server certificates
ansible.builtin.include_tasks: "{{ pki_backend }}/create_cert.yml"
- name: Sign server certificates
ansible.builtin.include_tasks: "{{ cert.backend | default(pki_backend) }}/sign_cert.yml"
loop: "{{ _pki_certificates_defs }}"
loop_control:
loop_var: cert
vars:
cert_dir: "{{ pki_dir }}/certs"
label: "{{ cert.name }}"
when:
- pki_create_certificates | default(true)
- name: Slurp up server certificates from pki setup host ({{ pki_setup_host }})
vars:
# location for the certificates on the PKI host
cert_dir: "{{ pki_dir }}/certs"
# construct the path to the source when "name" is specified
_source_files:
"certificate": "{{ cert_dir ~ '/certs/' ~ item.name ~ '.crt' }}"
"certificate_chain": "{{ cert_dir ~ '/certs/' ~ item.name ~ '-chain.crt' }}"
"ca_bundle": "{{ cert_dir ~ '/certs/' ~ item.name ~ '-ca_bundle.crt' }}"
"private_key": "{{ cert_dir ~ '/private/' ~ item.name ~ '.key.pem' }}"
# pick the source based on the type
_source: "{{ _source_files[item.type | default('certificate')] }}"
# detect a valid value of "src"
_use_src: "{{ item.src is defined and item.src is truthy }}"
delegate_to: "{{ pki_setup_host }}"
ansible.builtin.slurp:
src: "{{ _use_src | ternary(item.src, _source) }}"
register: _cert_slurp
- name: Install server certificates
ansible.builtin.include_tasks: "{{ install_cert.backend | default(pki_backend) }}/install_cert.yml"
loop: "{{ _pki_install_certificates_defs }}"
ignore_errors: "{{ ansible_check_mode }}"
- name: Create certificate destination directories
ansible.builtin.file:
path: "{{ install }}"
state: directory
mode: "{{ pki_cert_dir_mode }}"
loop: "{{ _cert_slurp.results | map(attribute='item') | map(attribute='dest') | map('dirname') | unique }}"
loop_control:
loop_var: install
label: "{{ loop_label | to_json }}"
vars:
loop_label:
path: "{{ install }}"
state: directory
mode: "{{ pki_cert_dir_mode }}"
- name: Install Server certificates to targets
ansible.builtin.copy:
content: "{{ install.content | b64decode }}"
dest: "{{ install.item.dest }}"
owner: "{{ _owner }}"
group: "{{ _group }}"
mode: "{{ _mode }}"
loop: "{{ _cert_slurp.results }}"
loop_control:
loop_var: install
label: "{{ loop_label | to_json }}"
vars:
_mode : "{{ install.item.mode | d(pki_file_mode[install.item.type | d('certificate')]) }}"
_owner: "{{ install.item.owner | default(pki_install_owner) }}"
_group: "{{ install.item.group | default(pki_install_group) }}"
loop_label:
dest: "{{ install.item.dest }}"
owner: "{{ _owner }}"
group: "{{ _group }}"
mode: "{{ _mode }}"
ignore_errors: "{{ ansible_check_mode }}"
notify:
- "{{ pki_handler_cert_installed }}"
loop_var: install_cert
label: "{{ install_cert.dest }}"
when:
- pki_install_certificates | default(true)

View File

@@ -13,10 +13,26 @@
# See the License for the specific language governing permissions and
# limitations under the License.
- name: Create certificate {{ cert.name }}
- name: Create PKI directories
ansible.builtin.file:
state: directory
path: "{{ item.path }}"
owner: "{{ item.owner | default(pki_owner) | default(omit) }}"
group: "{{ item.group | default(pki_group) | default(omit) }}"
mode: "{{ pki_cert_dir_mode }}"
with_items:
- "{{ pki_ca_dirs }}"
- "{{ pki_cert_dirs }}"
delegate_to: "{{ pki_setup_host }}"
check_mode: false
run_once: true
- name: Create certificate {{ ca.name }}
vars:
next_serial_no: "{{ serial_no['content'] | b64decode | int + 1 }}"
ansible_python_interpreter: "{{ pki_setup_host_python_interpreter }}"
ca_dir: "{{ pki_dir }}/roots/{{ ca.name }}"
ca_cert_prefix: "{{ pki_dir }}/roots/{{ ca.name }}/certs/{{ ca.name }}"
delegate_to: "{{ pki_setup_host }}"
block:
- name: Create directories for certificate authority {{ ca.name }}
@@ -80,9 +96,9 @@
country_name: "{{ ca.country_name | default(omit) }}"
state_or_province_name: "{{ ca.state_or_province_name | default(omit) }}"
locality_name: "{{ ca.locality_name | default(omit) }}"
organization_name: "{{ ca.organizataion_name | default(omit) }}"
organization_name: "{{ ca.orgnization_name | default(omit) }}"
organizational_unit_name: "{{ ca.organization_unit_name | default(omit) }}"
subject: "{{ cert.subject | default(omit) }}"
subject: "{{ ca.subject | default(omit) }}"
backup: "{{ ca.backup | default(True) }}"
register: ca_csr
when:
@@ -111,7 +127,6 @@
- "{{ pki_handler_ca_changed }}"
- name: Sign the intermediate CA CSR for {{ ca.name }}
vars:
community.crypto.x509_certificate:
path: "{{ ca_cert_prefix ~ '-' ~ next_serial_no ~ '.crt' }}"
csr_path: "{{ ca_csr.filename }}"

View File

@@ -16,24 +16,16 @@
- name: Slurp up CA certificates from pki setup host ({{ pki_setup_host }})
delegate_to: "{{ pki_setup_host }}"
ansible.builtin.slurp:
src: "{{ item.src | default(pki_dir ~ '/roots/' ~ item.name ~ '/certs/' ~ item.name ~ '.crt') }}"
src: "{{ ca.src | default(pki_dir ~ '/roots/' ~ ca.name ~ '/certs/' ~ ca.name ~ '.crt') }}"
register: _ca_slurp
run_once: true
loop: "{{ _pki_install_ca_defs }}"
ignore_errors: "{{ ansible_check_mode }}"
- name: Copy CA certificates to target host
ansible.builtin.copy:
content: "{{ item.content | b64decode }}"
dest: "{{ pki_trust_store_location[ansible_facts['pkg_mgr']] }}/{{ item.item.filename | default(item.item.name ~ '.crt') }}"
content: "{{ _ca_slurp.content | b64decode }}"
dest: "{{ pki_trust_store_location[ansible_facts['pkg_mgr']] }}/{{ ca.filename | default(ca.name ~ '.crt') }}"
register: ca_copy
loop: "{{ _ca_slurp.results | default([]) }}"
loop_control:
label: "{{ loop_label | to_json }}"
vars:
loop_label:
dest: "{{ pki_trust_store_location[ansible_facts['pkg_mgr']] }}/{{ item.item.filename | default(item.item.name ~ '.crt') }}"
when: item.skipped is not defined
ignore_errors: "{{ ansible_check_mode }}"
- name: Update CA store

View File

@@ -0,0 +1,58 @@
---
# Copyright 2025, Cleura AB
#
# 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: Slurp up server certificate from pki setup host ({{ pki_setup_host }})
vars:
# location for the certificates on the PKI host
cert_dir: "{{ pki_dir }}/certs"
# construct the path to the source when "name" is specified
_source_files:
"certificate": "{{ cert_dir ~ '/certs/' ~ install_cert.name ~ '.crt' }}"
"certificate_chain": "{{ cert_dir ~ '/certs/' ~ install_cert.name ~ '-chain.crt' }}"
"ca_bundle": "{{ cert_dir ~ '/certs/' ~ install_cert.name ~ '-ca_bundle.crt' }}"
"private_key": "{{ cert_dir ~ '/private/' ~ install_cert.name ~ '.key.pem' }}"
# pick the source based on the type
_source: "{{ _source_files[install_cert.type | default('certificate')] }}"
# detect a valid value of "src"
_use_src: "{{ install_cert.src is defined and install_cert.src is truthy }}"
delegate_to: "{{ pki_setup_host }}"
ansible.builtin.slurp:
src: "{{ _use_src | ternary(install_cert.src, _source) }}"
register: _cert_slurp
ignore_errors: "{{ ansible_check_mode }}"
- name: Create certificate destination directory ({{ install_cert.dest }})
ansible.builtin.file:
path: "{{ install_cert.dest | dirname }}"
state: directory
mode: "{{ pki_cert_dir_mode }}"
- name: Install Server certificate to targets ({{ install_cert.dest }})
vars:
_mode : "{{ install_cert.mode | d(pki_file_mode[install_cert.type | d('certificate')]) }}"
_owner: "{{ install_cert.owner | default(pki_install_owner) }}"
_group: "{{ install_cert.group | default(pki_install_group) }}"
ansible.builtin.copy:
content: "{{ _cert_slurp.content | b64decode }}"
dest: "{{ install_cert.dest }}"
owner: "{{ _owner }}"
group: "{{ _group }}"
mode: "{{ _mode }}"
ignore_errors: "{{ ansible_check_mode }}"
notify:
- "{{ pki_handler_cert_installed }}"

View File

@@ -28,3 +28,31 @@ _pki_certificates_defs: "{{ pki_certificates | union(_pki_certificates_candidate
# Gather certificate installation definitions from hostvars
_pki_install_certificates_candidates: "{{ query('vars', *query('varnames', '^' ~ pki_search_install_certificates_pattern)) | flatten(levels=1) }}"
_pki_install_certificates_defs: "{{ pki_install_certificates | union(_pki_install_certificates_candidates) | rejectattr('condition', 'false') }}"
# Command used to update CA trust store
pki_ca_install_command:
apt: "update-ca-certificates"
dnf: "update-ca-trust extract"
# locations of system trust stores to install CA certs to
pki_trust_store_location:
apt: /usr/local/share/ca-certificates/
dnf: /etc/pki/ca-trust/source/anchors/
# standalone backend variables
cert_dir: "{{ pki_dir }}/certs"
# subdirectories to be created for holding server certs/keys/csr
pki_cert_dirs:
- path: "{{ pki_dir }}"
- path: "{{ pki_dir ~ '/certs' }}"
mode: "{{ pki_cert_dir_mode }}"
- path: "{{ pki_dir ~ '/certs/csr' }}"
mode: "{{ pki_key_dir_mode }}"
- path: "{{ pki_dir ~ '/certs/private' }}"
mode: "{{ pki_key_dir_mode }}"
- path: "{{ pki_dir ~ '/certs/certs' }}"
mode: "{{ pki_cert_dir_mode }}"
# directories for certificate authorities on the CA host
pki_ca_dirs:
- path: "{{ pki_dir }}"
- path: "{{ pki_dir ~ '/roots' }}"

View File

@@ -1,35 +0,0 @@
---
# Copyright 2021, BBC
#
# 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.
# directories for certificate authorities on the CA host
_pki_ca_dirs:
- path: "{{ pki_dir }}"
- path: "{{ pki_dir ~ '/roots' }}"
# directories for server certificates on the CA host
_pki_cert_dirs:
- path: "{{ pki_dir }}"
- path: "{{ pki_dir ~ '/certs' }}"
mode: "{{ pki_cert_dir_mode }}"
- path: "{{ pki_dir ~ '/certs/csr' }}"
mode: "{{ pki_key_dir_mode }}"
- path: "{{ pki_dir ~ '/certs/private' }}"
mode: "{{ pki_key_dir_mode }}"
- path: "{{ pki_dir ~ '/certs/certs' }}"
mode: "{{ pki_cert_dir_mode }}"
pki_ca_install_command:
apt: "update-ca-certificates"
dnf: "update-ca-trust extract"