feat!: apply subject alternative names to root and intermediate CA

The variables `certificate_authority_root_ca_subject_alternative_names` and
`certificate_authority_intermediate_ca_subject_alternative_names` were
documented but never referenced by any task, so both CA certificates were always
issued without SANs. Wire them into the corresponding CSR tasks and fall back to
`omit` when the list is empty.

SAN entries are now passed to `openssl_csr` unchanged instead of being prefixed
with `DNS:` by the role. This allows other types such as `IP:` or `email:`,
which the previous rewrite would have corrupted into values like `DNS:IP:...`.
The client tasks additionally dropped `join(',') | quote`, because `quote`
performs shell escaping and `openssl_csr` expects a list.

Since both client CSR tasks only differed in `subject_alt_name`, they collapse
into a single task per file.

BREAKING CHANGE: Entries of all `*_subject_alternative_names` variables must now
carry their type prefix, for example `DNS:example.local` instead of
`example.local`.

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-09-07 22:28:52 +02:00
co-authored by Copilot
parent 38ab2f55bb
commit 41d06e4e6d
9 changed files with 20 additions and 56 deletions
+6 -5
View File
@@ -13,8 +13,9 @@ the intermediate certificate authority.
certificate_authority_client_skip: false certificate_authority_client_skip: false
certificate_authority_client_common_name: "{{ inventory_hostname }}" certificate_authority_client_common_name: "{{ inventory_hostname }}"
certificate_authority_client_subject_alternative_names: certificate_authority_client_subject_alternative_names:
- "{{ inventory_hostname }}" - "DNS:{{ inventory_hostname }}"
- san.example.local - "DNS:san.example.local"
- "IP:10.11.12.13"
``` ```
## Parameters ## Parameters
@@ -33,7 +34,7 @@ certificate_authority_client_subject_alternative_names:
| `certificate_authority_root_ca_organization_name` | Organization name of the root certificate authority owner. | `""` | | `certificate_authority_root_ca_organization_name` | Organization name of the root certificate authority owner. | `""` |
| `certificate_authority_root_ca_organizational_unit_name` | Organizational unit name of the root certificate authority. | `""` | | `certificate_authority_root_ca_organizational_unit_name` | Organizational unit name of the root certificate authority. | `""` |
| `certificate_authority_root_ca_state_or_province_name` | State or province name where the owner of the root certificate authority is located. | `""` | | `certificate_authority_root_ca_state_or_province_name` | State or province name where the owner of the root certificate authority is located. | `""` |
| `certificate_authority_root_ca_subject_alternative_names` | Subject Alternative Names (SAN) of the root certificate authority. | `[]` | | `certificate_authority_root_ca_subject_alternative_names` | Subject Alternative Names (SAN) of the root certificate authority. Each entry must be prefixed by its type, for example `DNS:example.local` or `IP:10.11.12.13`. | `[]` |
| `certificate_authority_root_ca_not_after` | Time in the future from now when the TLS certificate should expire | `+3650d` | | `certificate_authority_root_ca_not_after` | Time in the future from now when the TLS certificate should expire | `+3650d` |
| `certificate_authority_root_ca_not_before` | Time in the past from now when the TLS certificate should be valid. | `+0s` | | `certificate_authority_root_ca_not_before` | Time in the past from now when the TLS certificate should be valid. | `+0s` |
| `certificate_authority_root_ca_tls_key_content` | Content of a custom used root certificate authority. Will only be imported, when `certificate_authority_root_ca_create: false`. | `""` | | `certificate_authority_root_ca_tls_key_content` | Content of a custom used root certificate authority. Will only be imported, when `certificate_authority_root_ca_create: false`. | `""` |
@@ -54,7 +55,7 @@ certificate_authority_client_subject_alternative_names:
| `certificate_authority_intermediate_ca_organization_name` | Organization name of the intermediate certificate authority owner. | `""` | | `certificate_authority_intermediate_ca_organization_name` | Organization name of the intermediate certificate authority owner. | `""` |
| `certificate_authority_intermediate_ca_organizational_unit_name` | Organizational unit name of the intermediate certificate authority. | `""` | | `certificate_authority_intermediate_ca_organizational_unit_name` | Organizational unit name of the intermediate certificate authority. | `""` |
| `certificate_authority_intermediate_ca_state_or_province_name` | State or province name where the owner of the intermediate certificate authority is located. | `""` | | `certificate_authority_intermediate_ca_state_or_province_name` | State or province name where the owner of the intermediate certificate authority is located. | `""` |
| `certificate_authority_intermediate_ca_subject_alternative_names` | Subject Alternative Names (SAN) of the intermediate certificate authority. | `[]` | | `certificate_authority_intermediate_ca_subject_alternative_names` | Subject Alternative Names (SAN) of the intermediate certificate authority. Each entry must be prefixed by its type, for example `DNS:example.local` or `IP:10.11.12.13`. | `[]` |
| `certificate_authority_intermediate_ca_not_after` | Time in the future from now when the TLS certificate should expire | `+1825d` | | `certificate_authority_intermediate_ca_not_after` | Time in the future from now when the TLS certificate should expire | `+1825d` |
| `certificate_authority_intermediate_ca_not_before` | Time in the past from now when the TLS certificate should be valid. | `+0s` | | `certificate_authority_intermediate_ca_not_before` | Time in the past from now when the TLS certificate should be valid. | `+0s` |
| `certificate_authority_intermediate_ca_tls_key_content` | Content of a custom used intermediate certificate authority. Will only be imported, when `certificate_authority_intermediate_ca_create: false`. | `""` | | `certificate_authority_intermediate_ca_tls_key_content` | Content of a custom used intermediate certificate authority. Will only be imported, when `certificate_authority_intermediate_ca_create: false`. | `""` |
@@ -75,7 +76,7 @@ certificate_authority_client_subject_alternative_names:
| `certificate_authority_client_organization_name` | Organization name of the client certificate owner. | `""` | | `certificate_authority_client_organization_name` | Organization name of the client certificate owner. | `""` |
| `certificate_authority_client_organizational_unit_name` | Common Name (CN) of the client certificate. | `""` | | `certificate_authority_client_organizational_unit_name` | Common Name (CN) of the client certificate. | `""` |
| `certificate_authority_client_state_or_province_name` | State or province name where the owner of the client certificate is located. | `""` | | `certificate_authority_client_state_or_province_name` | State or province name where the owner of the client certificate is located. | `""` |
| `certificate_authority_client_subject_alternative_names` | Subject Alternative Names (SAN) of the client certificate. | `[]` | | `certificate_authority_client_subject_alternative_names` | Subject Alternative Names (SAN) of the client certificate. Each entry must be prefixed by its type, for example `DNS:example.local` or `IP:10.11.12.13`. | `[]` |
| `certificate_authority_client_not_after` | Time in the future from now when the TLS certificate should expire | `+397d` | | `certificate_authority_client_not_after` | Time in the future from now when the TLS certificate should expire | `+397d` |
| `certificate_authority_client_not_before` | Time in the past from now when the TLS certificate should be valid. | `+0s` | | `certificate_authority_client_not_before` | Time in the past from now when the TLS certificate should be valid. | `+0s` |
| `certificate_authority_client_tls_key_passphrase` | Passphrase for the private key of the generated or imported client certificate. | `""` | | `certificate_authority_client_tls_key_passphrase` | Passphrase for the private key of the generated or imported client certificate. | `""` |
+3 -3
View File
@@ -15,7 +15,7 @@ certificate_authority_root_ca_import: true
## @param certificate_authority_root_ca_organization_name Organization name of the root certificate authority owner. ## @param certificate_authority_root_ca_organization_name Organization name of the root certificate authority owner.
## @param certificate_authority_root_ca_organizational_unit_name Organizational unit name of the root certificate authority. ## @param certificate_authority_root_ca_organizational_unit_name Organizational unit name of the root certificate authority.
## @param certificate_authority_root_ca_state_or_province_name State or province name where the owner of the root certificate authority is located. ## @param certificate_authority_root_ca_state_or_province_name State or province name where the owner of the root certificate authority is located.
## @param certificate_authority_root_ca_subject_alternative_names Subject Alternative Names (SAN) of the root certificate authority. ## @param certificate_authority_root_ca_subject_alternative_names Subject Alternative Names (SAN) of the root certificate authority. Each entry must be prefixed by its type, for example `DNS:example.local` or `IP:10.11.12.13`.
## @param certificate_authority_root_ca_not_after Time in the future from now when the TLS certificate should expire ## @param certificate_authority_root_ca_not_after Time in the future from now when the TLS certificate should expire
## @param certificate_authority_root_ca_not_before Time in the past from now when the TLS certificate should be valid. ## @param certificate_authority_root_ca_not_before Time in the past from now when the TLS certificate should be valid.
certificate_authority_root_ca_path: "/etc/ansible-playbook/pki/ca" certificate_authority_root_ca_path: "/etc/ansible-playbook/pki/ca"
@@ -53,7 +53,7 @@ certificate_authority_intermediate_ca_create: true
## @param certificate_authority_intermediate_ca_organization_name Organization name of the intermediate certificate authority owner. ## @param certificate_authority_intermediate_ca_organization_name Organization name of the intermediate certificate authority owner.
## @param certificate_authority_intermediate_ca_organizational_unit_name Organizational unit name of the intermediate certificate authority. ## @param certificate_authority_intermediate_ca_organizational_unit_name Organizational unit name of the intermediate certificate authority.
## @param certificate_authority_intermediate_ca_state_or_province_name State or province name where the owner of the intermediate certificate authority is located. ## @param certificate_authority_intermediate_ca_state_or_province_name State or province name where the owner of the intermediate certificate authority is located.
## @param certificate_authority_intermediate_ca_subject_alternative_names Subject Alternative Names (SAN) of the intermediate certificate authority. ## @param certificate_authority_intermediate_ca_subject_alternative_names Subject Alternative Names (SAN) of the intermediate certificate authority. Each entry must be prefixed by its type, for example `DNS:example.local` or `IP:10.11.12.13`.
## @param certificate_authority_intermediate_ca_not_after Time in the future from now when the TLS certificate should expire ## @param certificate_authority_intermediate_ca_not_after Time in the future from now when the TLS certificate should expire
## @param certificate_authority_intermediate_ca_not_before Time in the past from now when the TLS certificate should be valid. ## @param certificate_authority_intermediate_ca_not_before Time in the past from now when the TLS certificate should be valid.
certificate_authority_intermediate_ca_path: "/etc/ansible-playbook/pki/intermediate" certificate_authority_intermediate_ca_path: "/etc/ansible-playbook/pki/intermediate"
@@ -91,7 +91,7 @@ certificate_authority_client_create: true
## @param certificate_authority_client_organization_name Organization name of the client certificate owner. ## @param certificate_authority_client_organization_name Organization name of the client certificate owner.
## @param certificate_authority_client_organizational_unit_name Common Name (CN) of the client certificate. ## @param certificate_authority_client_organizational_unit_name Common Name (CN) of the client certificate.
## @param certificate_authority_client_state_or_province_name State or province name where the owner of the client certificate is located. ## @param certificate_authority_client_state_or_province_name State or province name where the owner of the client certificate is located.
## @param certificate_authority_client_subject_alternative_names Subject Alternative Names (SAN) of the client certificate. ## @param certificate_authority_client_subject_alternative_names Subject Alternative Names (SAN) of the client certificate. Each entry must be prefixed by its type, for example `DNS:example.local` or `IP:10.11.12.13`.
## @param certificate_authority_client_not_after Time in the future from now when the TLS certificate should expire ## @param certificate_authority_client_not_after Time in the future from now when the TLS certificate should expire
## @param certificate_authority_client_not_before Time in the past from now when the TLS certificate should be valid. ## @param certificate_authority_client_not_before Time in the past from now when the TLS certificate should be valid.
certificate_authority_client_path: "/etc/ansible-playbook/pki/client" certificate_authority_client_path: "/etc/ansible-playbook/pki/client"
+3 -3
View File
@@ -49,7 +49,7 @@ argument_specs:
type: str type: str
default: "" default: ""
certificate_authority_root_ca_subject_alternative_names: certificate_authority_root_ca_subject_alternative_names:
description: "Subject Alternative Names (SAN) of the root certificate authority." description: "Subject Alternative Names (SAN) of the root certificate authority. Each entry must be prefixed by its type, for example DNS:example.local or IP:10.11.12.13."
type: list type: list
elements: str elements: str
default: [] default: []
@@ -121,7 +121,7 @@ argument_specs:
type: str type: str
default: "" default: ""
certificate_authority_intermediate_ca_subject_alternative_names: certificate_authority_intermediate_ca_subject_alternative_names:
description: "Subject Alternative Names (SAN) of the intermediate certificate authority." description: "Subject Alternative Names (SAN) of the intermediate certificate authority. Each entry must be prefixed by its type, for example DNS:example.local or IP:10.11.12.13."
type: list type: list
elements: str elements: str
default: [] default: []
@@ -193,7 +193,7 @@ argument_specs:
type: str type: str
default: "" default: ""
certificate_authority_client_subject_alternative_names: certificate_authority_client_subject_alternative_names:
description: "Subject Alternative Names (SAN) of the client certificate." description: "Subject Alternative Names (SAN) of the client certificate. Each entry must be prefixed by its type, for example DNS:example.local or IP:10.11.12.13."
type: list type: list
elements: str elements: str
default: [] default: []
+2 -23
View File
@@ -7,7 +7,7 @@
passphrase: "{{ certificate_authority_client_tls_key_passphrase }}" passphrase: "{{ certificate_authority_client_tls_key_passphrase }}"
cipher: auto cipher: auto
- name: Create a certificate signing request (CSR) for client certificate without subject alternative names (SANs) - name: Create a certificate signing request (CSR) for client certificate
community.crypto.openssl_csr: community.crypto.openssl_csr:
common_name: "{{ certificate_authority_client_common_name }}" common_name: "{{ certificate_authority_client_common_name }}"
countryName: "{{ certificate_authority_client_country_name }}" countryName: "{{ certificate_authority_client_country_name }}"
@@ -21,28 +21,7 @@
privatekey_passphrase: "{{ certificate_authority_client_tls_key_passphrase }}" privatekey_passphrase: "{{ certificate_authority_client_tls_key_passphrase }}"
privatekey_path: "{{ certificate_authority_client_path }}/privkey.pem" privatekey_path: "{{ certificate_authority_client_path }}/privkey.pem"
state_or_province_name: "{{ certificate_authority_client_state_or_province_name }}" state_or_province_name: "{{ certificate_authority_client_state_or_province_name }}"
when: | subject_alt_name: "{{ certificate_authority_client_subject_alternative_names if certificate_authority_client_subject_alternative_names | length > 0 else omit }}"
certificate_authority_client_subject_alternative_names is not defined or
(certificate_authority_client_subject_alternative_names is defined and
certificate_authority_client_subject_alternative_names | length <= 0)
- name: Create a certificate signing request (CSR) for client certificate with subject alternative names (SANs)
community.crypto.openssl_csr:
common_name: "{{ certificate_authority_client_common_name }}"
countryName: "{{ certificate_authority_client_country_name }}"
email_address: "{{ certificate_authority_client_email_address }}"
extendedKeyUsage:
- clientAuth
- serverAuth
organization_name: "{{ certificate_authority_client_organization_name }}"
organizational_unit_name: "{{ certificate_authority_client_organizational_unit_name }}"
path: "{{ certificate_authority_client_path }}/cert-req.pem"
privatekey_path: "{{ certificate_authority_client_path }}/privkey.pem"
privatekey_passphrase: "{{ certificate_authority_client_tls_key_passphrase }}"
state_or_province_name: "{{ certificate_authority_client_state_or_province_name }}"
subject_alt_name: "{{ certificate_authority_client_subject_alternative_names | map('regex_replace', '^', 'DNS:') | list | join(',') | quote }}"
when: certificate_authority_client_subject_alternative_names is defined and
certificate_authority_client_subject_alternative_names | length > 0
- name: Create signed client certificate - unprotected intermediate Certificate Authority (CA) - name: Create signed client certificate - unprotected intermediate Certificate Authority (CA)
community.crypto.x509_certificate: community.crypto.x509_certificate:
+2 -22
View File
@@ -5,7 +5,7 @@
path: "{{ certificate_authority_client_path }}/privkey.pem" path: "{{ certificate_authority_client_path }}/privkey.pem"
type: "{{ certificate_authority_client_tls_key_type }}" type: "{{ certificate_authority_client_tls_key_type }}"
- name: Create a certificate signing request (CSR) for client certificate without subject alternative names (SANs) - name: Create a certificate signing request (CSR) for client certificate
community.crypto.openssl_csr: community.crypto.openssl_csr:
common_name: "{{ certificate_authority_client_common_name }}" common_name: "{{ certificate_authority_client_common_name }}"
countryName: "{{ certificate_authority_client_country_name }}" countryName: "{{ certificate_authority_client_country_name }}"
@@ -18,27 +18,7 @@
path: "{{ certificate_authority_client_path }}/cert-req.pem" path: "{{ certificate_authority_client_path }}/cert-req.pem"
privatekey_path: "{{ certificate_authority_client_path }}/privkey.pem" privatekey_path: "{{ certificate_authority_client_path }}/privkey.pem"
state_or_province_name: "{{ certificate_authority_client_state_or_province_name }}" state_or_province_name: "{{ certificate_authority_client_state_or_province_name }}"
when: | subject_alt_name: "{{ certificate_authority_client_subject_alternative_names if certificate_authority_client_subject_alternative_names | length > 0 else omit }}"
certificate_authority_client_subject_alternative_names is not defined or
(certificate_authority_client_subject_alternative_names is defined and
certificate_authority_client_subject_alternative_names | length <= 0)
- name: Create a certificate signing request (CSR) for client certificate with subject alternative names (SANs)
community.crypto.openssl_csr:
common_name: "{{ certificate_authority_client_common_name }}"
countryName: "{{ certificate_authority_client_country_name }}"
email_address: "{{ certificate_authority_client_email_address }}"
extendedKeyUsage:
- clientAuth
- serverAuth
organization_name: "{{ certificate_authority_client_organization_name }}"
organizational_unit_name: "{{ certificate_authority_client_organizational_unit_name }}"
path: "{{ certificate_authority_client_path }}/cert-req.pem"
privatekey_path: "{{ certificate_authority_client_path }}/privkey.pem"
state_or_province_name: "{{ certificate_authority_client_state_or_province_name }}"
subject_alt_name: "{{ certificate_authority_client_subject_alternative_names | map('regex_replace', '^', 'DNS:') | list | join(',') | quote }}"
when: certificate_authority_client_subject_alternative_names is defined and
certificate_authority_client_subject_alternative_names | length > 0
- name: Create signed client certificate - unprotected intermediate Certificate Authority (CA) - name: Create signed client certificate - unprotected intermediate Certificate Authority (CA)
community.crypto.x509_certificate: community.crypto.x509_certificate:
@@ -20,6 +20,7 @@
privatekey_passphrase: "{{ certificate_authority_intermediate_ca_tls_key_passphrase }}" privatekey_passphrase: "{{ certificate_authority_intermediate_ca_tls_key_passphrase }}"
privatekey_path: "{{ certificate_authority_intermediate_ca_path }}/privkey.pem" privatekey_path: "{{ certificate_authority_intermediate_ca_path }}/privkey.pem"
state_or_province_name: "{{ certificate_authority_intermediate_ca_state_or_province_name }}" state_or_province_name: "{{ certificate_authority_intermediate_ca_state_or_province_name }}"
subject_alt_name: "{{ certificate_authority_intermediate_ca_subject_alternative_names if certificate_authority_intermediate_ca_subject_alternative_names | length > 0 else omit }}"
use_common_name_for_san: false use_common_name_for_san: false
- name: Create signed client certificate - unprotected root Certificate Authority (CA) - name: Create signed client certificate - unprotected root Certificate Authority (CA)
@@ -17,6 +17,7 @@
path: "{{ certificate_authority_intermediate_ca_path }}/cert-req.pem" path: "{{ certificate_authority_intermediate_ca_path }}/cert-req.pem"
privatekey_path: "{{ certificate_authority_intermediate_ca_path }}/privkey.pem" privatekey_path: "{{ certificate_authority_intermediate_ca_path }}/privkey.pem"
state_or_province_name: "{{ certificate_authority_intermediate_ca_state_or_province_name }}" state_or_province_name: "{{ certificate_authority_intermediate_ca_state_or_province_name }}"
subject_alt_name: "{{ certificate_authority_intermediate_ca_subject_alternative_names if certificate_authority_intermediate_ca_subject_alternative_names | length > 0 else omit }}"
use_common_name_for_san: false use_common_name_for_san: false
- name: Create signed client certificate - unprotected root Certificate Authority (CA) - name: Create signed client certificate - unprotected root Certificate Authority (CA)
@@ -20,6 +20,7 @@
privatekey_passphrase: "{{ certificate_authority_root_ca_tls_key_passphrase }}" privatekey_passphrase: "{{ certificate_authority_root_ca_tls_key_passphrase }}"
privatekey_path: "{{ certificate_authority_root_ca_path }}/privkey.pem" privatekey_path: "{{ certificate_authority_root_ca_path }}/privkey.pem"
state_or_province_name: "{{ certificate_authority_root_ca_state_or_province_name }}" state_or_province_name: "{{ certificate_authority_root_ca_state_or_province_name }}"
subject_alt_name: "{{ certificate_authority_root_ca_subject_alternative_names if certificate_authority_root_ca_subject_alternative_names | length > 0 else omit }}"
use_common_name_for_san: false use_common_name_for_san: false
- name: Create self-signed certificate for root CA - name: Create self-signed certificate for root CA
@@ -17,6 +17,7 @@
path: "{{ certificate_authority_root_ca_path }}/cert-req.pem" path: "{{ certificate_authority_root_ca_path }}/cert-req.pem"
privatekey_path: "{{ certificate_authority_root_ca_path }}/privkey.pem" privatekey_path: "{{ certificate_authority_root_ca_path }}/privkey.pem"
state_or_province_name: "{{ certificate_authority_root_ca_state_or_province_name }}" state_or_province_name: "{{ certificate_authority_root_ca_state_or_province_name }}"
subject_alt_name: "{{ certificate_authority_root_ca_subject_alternative_names if certificate_authority_root_ca_subject_alternative_names | length > 0 else omit }}"
use_common_name_for_san: false use_common_name_for_san: false
- name: Create self-signed certificate for root CA - name: Create self-signed certificate for root CA