From 32b19e7f31b3fb448cfd7ab316bfc82407811df7 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 12 Nov 2026 17:13:16 +0100 Subject: [PATCH 1/2] Add ca_san_extra for extra subject alternative names --- README.md | 1 + defaults/main.yml | 2 ++ molecule/default/molecule.yml | 6 ++++++ molecule/default/verify.yml | 31 +++++++++++++++++++++++++++++++ tasks/main.yml | 5 +++++ 5 files changed, 45 insertions(+) diff --git a/README.md b/README.md index a6e428b..9d38f89 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ You need to have the Python library `cryptography` in version `>1.2.3` available * `ca_logstash`: Create Logstash compatible certificate as well. Needs `ca_server_cert` to be set. (default: `false`) * `ca_etcd`: Create additional etcd compatible certificates. Requires `ca_etcd_group` to be defined. (default: `false`) * `ca_etcd_group`: Needs to be set to the group name of etcd nodes and will add the default IPv4 address of each node to the certificates. 127.0.0.1 will also be added by the role to the SAN for loopback purposes.(default: `undefined`) +* `ca_san_extra`: Extra subject alternative names for the client and server certificates of a host, for example `["IP:192.0.2.10", "DNS:lb.example.com"]`. Set it in the host_vars. The etcd certificates get them as well. (default: `[]`) * `ca_keypassphrase`: Password for the client key, default not defined * `ca_openssl_cipher`: Cipher to use for key creation, default not defined * `ca_client_ca_dir`: Directory to place CA and certificates on the clients (default: `/opt/ca`) diff --git a/defaults/main.yml b/defaults/main.yml index f00a5c0..9084b68 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -15,6 +15,8 @@ ca_server_cert: true ca_logstash: false ca_etcd: false +ca_san_extra: [] + ca_country: EX ca_state: EX ca_locality: EX diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml index 0410f09..303d4c4 100644 --- a/molecule/default/molecule.yml +++ b/molecule/default/molecule.yml @@ -27,5 +27,11 @@ provisioner: config_options: defaults: roles_path: "$MOLECULE_PROJECT_DIRECTORY/.." + inventory: + host_vars: + ca_default_client: + ca_san_extra: + - IP:192.0.2.10 + - DNS:lb.example.com verifier: name: ansible diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml index 789fab0..873b0f4 100644 --- a/molecule/default/verify.yml +++ b/molecule/default/verify.yml @@ -116,3 +116,34 @@ - hostvars['ca_default_client']['ansible_default_ipv4']['address'] not in etcd_san_server_stat.stdout - '"127.0.0.1" not in etcd_san_server_stat.stdout' + + - name: Register SAN of client and server certificate + command: > + openssl x509 -noout -ext subjectAltName + -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}{{ item }}.crt + loop: + - "" + - "-server" + register: san_stat + changed_when: false + + # openssl prints an IP entry as "IP Address:192.0.2.10" + - name: Fail if an entry of ca_san_extra is missing + fail: + msg: >- + {{ item.1 }} is missing in + {{ inventory_hostname }}{{ item.0.item }}.crt + loop: "{{ san_stat.results | product(san_extra) | list }}" + vars: + san_extra: "{{ ca_san_extra | default([]) }}" + when: item.1 | regex_replace('^IP:', 'IP Address:') not in item.0.stdout + + - name: Fail if a host without ca_san_extra got the extra names + fail: + msg: >- + {{ inventory_hostname }}{{ item.item }}.crt + contains names of ca_san_extra + loop: "{{ san_stat.results }}" + when: + - ca_san_extra is not defined + - "'192.0.2.10' in item.stdout or 'lb.example.com' in item.stdout" diff --git a/tasks/main.yml b/tasks/main.yml index f7e5435..8e25a2a 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -44,6 +44,11 @@ DNS:{{ ansible_fqdn }}, DNS:{{ inventory_hostname }} +- name: Add ca_san_extra to ca_san + ansible.builtin.set_fact: + ca_san: "{{ ([ca_san] + ca_san_extra) | join(', ') }}" + when: ca_san_extra | length > 0 + ### client certificate ### - name: Create key From 95310894b3a9e798adb8e3eb823ed9b22f2ec0f1 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 12 Nov 2026 17:17:12 +0100 Subject: [PATCH 2/2] Update docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d38f89..6abcd41 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ You need to have the Python library `cryptography` in version `>1.2.3` available * `ca_logstash`: Create Logstash compatible certificate as well. Needs `ca_server_cert` to be set. (default: `false`) * `ca_etcd`: Create additional etcd compatible certificates. Requires `ca_etcd_group` to be defined. (default: `false`) * `ca_etcd_group`: Needs to be set to the group name of etcd nodes and will add the default IPv4 address of each node to the certificates. 127.0.0.1 will also be added by the role to the SAN for loopback purposes.(default: `undefined`) -* `ca_san_extra`: Extra subject alternative names for the client and server certificates of a host, for example `["IP:192.0.2.10", "DNS:lb.example.com"]`. Set it in the host_vars. The etcd certificates get them as well. (default: `[]`) +* `ca_san_extra`: Extra subject alternative names for the client and server certificates of a host, for example `["IP:192.0.2.10", "DNS:lb.example.com"]`. (default: `[]`) * `ca_keypassphrase`: Password for the client key, default not defined * `ca_openssl_cipher`: Cipher to use for key creation, default not defined * `ca_client_ca_dir`: Directory to place CA and certificates on the clients (default: `/opt/ca`)