From 0fa55af4845c8908699da9e566ddf47c085eee2a Mon Sep 17 00:00:00 2001 From: AnsibleGuy Date: Sat, 13 Aug 2022 22:10:28 +0200 Subject: [PATCH] added molecule tests, fixes and checks for tests --- defaults/main.yml | 23 +- filter_plugins/utils.py | 30 +- .../default/Dockerfile_debian11_systemd.j2 | 14 + molecule/default/Usage.md | 10 + molecule/default/converge.yml | 134 ++++++++ molecule/default/molecule.yml | 60 ++++ molecule/default/prepare.yml | 19 ++ tasks/debian/letsencrypt/cert.yml | 19 +- tasks/debian/letsencrypt/main.yml | 15 +- tasks/debian/letsencrypt/test.yml | 30 ++ tasks/internal/ca_minimal.yml | 132 ++++---- tasks/internal/cert.yml | 302 ++++++++++-------- tasks/internal/main.yml | 11 + tasks/main.yml | 8 + 14 files changed, 589 insertions(+), 218 deletions(-) create mode 100644 molecule/default/Dockerfile_debian11_systemd.j2 create mode 100644 molecule/default/Usage.md create mode 100644 molecule/default/converge.yml create mode 100644 molecule/default/molecule.yml create mode 100644 molecule/default/prepare.yml create mode 100644 tasks/debian/letsencrypt/test.yml diff --git a/defaults/main.yml b/defaults/main.yml index 29ee7e6..8b4bdd0 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -9,12 +9,13 @@ defaults_certs: name: key_size: 4096 # 1024, 2048, 4096 key_type: 'RSA' - cipher: 'AES-256-CBC' # see: 'openssl list -cipher-algorithms' + cipher: 'auto' digest: 'sha256' regenerate: 'partial_idempotence' pwd: domains: [] ips: [] + backend: 'auto' # certificate config cn: 'Ansible Certificate' @@ -23,10 +24,11 @@ defaults_certs: country: state: locality: + san_other: # other RAW values to set as subject alternative name => MUST BE VALID email: # if using letsencrypt you might pass an email per domain => see letsencrypt-certs key_usage: 'serverAuth' # serverAuth, clientAuth, codeSigning, emailProtection, timeStamping, ocspSigning ocsp_staple: false - crl_distribution: [] + crl_distribution: [] # list of dicts # - full_name: # - "URI:https://ca.example.com/revocations.crl" # crl_issuer: @@ -54,18 +56,19 @@ defaults_certs: service: # apache, nginx renew_timer: 'Mon *-*-* 01:00:00' verbosity: 'v' - certs: {} # see 'default_le_certbot_cert_config' + certs: {} # see 'default_le_certbot_cert' below renew: false # if a renewal should be started by the role; the renewal service will auto-renew the certificates otherwise ca: - path: '/etc/certs/ca' + path: valid_days: 7300 key_size: 8192 # 1024, 2048, 4096, 8192 key_type: 'RSA' - cipher: 'AES-256-CBC' # see: 'openssl list -cipher-algorithms' + cipher: 'auto' digest: 'sha512' regenerate: 'partial_idempotence' pwd: + backend: 'auto' # certificate config cn: 'CA Certificate' @@ -83,4 +86,14 @@ default_le_certbot_cert: state: 'present' email: +# letsencrypt example: +#certs: +# example1: +# domains: ['example1.ansibleguy.net'] +# email: 'dummy@ansibleguy.net' +# example2: +# domains: ['example2.ansibleguy.net'] +# email: 'dummy@ansibleguy.net' + debug: false +testing: false diff --git a/filter_plugins/utils.py b/filter_plugins/utils.py index 3c8f5c2..a69ebab 100644 --- a/filter_plugins/utils.py +++ b/filter_plugins/utils.py @@ -1,5 +1,6 @@ from re import sub as regex_replace from re import match as regex_match +from re import compile as regex_compile class FilterModule(object): @@ -7,10 +8,11 @@ class FilterModule(object): def filters(self): return { "safe_key": self.safe_key, - "valid_domain": self.valid_domain, + "valid_hostname": self.valid_hostname, "valid_ip": self.valid_ip, "check_email": self.check_email, "le_domains_changed": self.le_domains_changed, + "ensure_list": self.ensure_list, } @staticmethod @@ -18,9 +20,19 @@ class FilterModule(object): return regex_replace(r'[^0-9a-zA-Z\.]+', '', key.replace(' ', '_')) @staticmethod - def valid_domain(domain: str) -> bool: - expr = r'^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$' - return True if regex_match(expr, domain) is not None else False + def valid_hostname(name: str) -> bool: + # see: https://validators.readthedocs.io/en/latest/_modules/validators/domain.html + domain = regex_compile( + r'^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|' + r'([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|' + r'([a-zA-Z0-9][-_.a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\.' + r'([a-zA-Z]{2,13}|[a-zA-Z0-9-]{2,30}.[a-zA-Z]{2,3})$' + ) + valid_domain = True if domain.match(name) is not None else False + # see: https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names + expr_hostname = r'^[a-zA-Z0-9-\.]{1,253}$' + valid_hostname = True if regex_match(expr_hostname, name) is not None else False + return all([valid_domain, valid_hostname]) @staticmethod def valid_ip(ip: str) -> bool: @@ -49,6 +61,7 @@ class FilterModule(object): # removing wildcards try: config_domains.remove(non_domain) + except ValueError: pass @@ -76,3 +89,12 @@ class FilterModule(object): break return changed + + @staticmethod + def ensure_list(data: (str, dict, list)) -> list: + # if user supplied a string instead of a list => convert it to match our expectations + if type(data) == list: + return data + + else: + return [data] diff --git a/molecule/default/Dockerfile_debian11_systemd.j2 b/molecule/default/Dockerfile_debian11_systemd.j2 new file mode 100644 index 0000000..24c57ad --- /dev/null +++ b/molecule/default/Dockerfile_debian11_systemd.j2 @@ -0,0 +1,14 @@ +# docker build -t mantest - < ./Dockerfile.j2 +# docker run -it --privileged --name mantest mantest:latest /sbin/init --tmpfs /tmp --tmpfs /run --tmpfs /run/lock + +FROM debian:11-slim + +ENV container docker +ENV LC_ALL C +ENV DEBIAN_FRONTEND noninteractive + +RUN apt-get update \ + && apt-get install -y systemd systemd-sysv python3 sudo \ + && apt-get clean + +CMD ["/sbin/init"] diff --git a/molecule/default/Usage.md b/molecule/default/Usage.md new file mode 100644 index 0000000..560ff97 --- /dev/null +++ b/molecule/default/Usage.md @@ -0,0 +1,10 @@ +# Usage + +Check out the [Molecule Tutorial](https://github.com/ansibleguy/ansible_tutorial/blob/main/Molecule.md) on how to get started! + +# Running + +```bash +cd roles/ansibleguy.ROLE +molecule test +``` diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml new file mode 100644 index 0000000..c925786 --- /dev/null +++ b/molecule/default/converge.yml @@ -0,0 +1,134 @@ +--- + +# todo: test revoking + +- name: Converge Internal + hosts: test-ag-certs-internal-{{ lookup('ansible.builtin.env', 'USER') }} + roles: + - role: ansibleguy.infra_certs + vars: + certs: + mode: 'selfsigned' + path: '/etc/ssl/test1' + + cert: + name: 'self_srv' + domains: ['cert.test.ansibleguy.net'] + ips: ['192.168.0.1'] + cn: 'SelfSigned Server Cert' + org: 'AnsibleGuy Test' + email: 'testmaster@ansibleguy.net' + ou: 'Test' + country: 'AT' + state: 'Styria' + locality: 'Unknown' + valid_days: 5 + key_usage: 'serverAuth' + crl_distribution: + crl_issuer: 'URI:https://ca.template.ansibleguy.net/' + full_name: 'URI:https://ca.template.ansibleguy.net/revocations.crl' + reasons: ['key_compromise', 'ca_compromise'] + + - role: ansibleguy.infra_certs + vars: + certs: + mode: 'selfsigned' + path: '/etc/ssl/test2' + + cert: + name: 'self_cli' + cn: 'SelfSigned Client Cert' + key_usage: 'clientAuth' + + - role: ansibleguy.infra_certs + vars: + certs: + mode: 'selfsigned' + path: '/etc/ssl/test3' + + cert: + name: 'self_other' + san_other: 'DNS:cert.templates.ansibleguy.net,email:other@cert.template.ansibleguy.net' + cn: 'SelfSigned Other Cert' + + - role: ansibleguy.infra_certs + vars: + certs: + mode: 'ca' + path: '/etc/ssl/test3' + + cert: + name: 'self_minca_srv' + domains: ['cert.test.ansibleguy.net'] + ips: ['192.168.0.1'] + cn: 'CA-Signed Server Cert' + org: 'AnsibleGuy Test' + email: 'testmaster@ansibleguy.net' + ou: 'Test' + country: 'AT' + state: 'Styria' + locality: 'Unknown' + valid_days: 5 + key_usage: 'serverAuth' + crl_distribution: + crl_issuer: 'URI:https://ca.template.ansibleguy.net/' + full_name: 'URI:https://ca.template.ansibleguy.net/revocations.crl' + reasons: ['key_compromise', 'ca_compromise'] + + - role: ansibleguy.infra_certs + vars: + certs: + mode: 'ca' + path: '/etc/ssl/test4' + + cert: + name: 'self_minca_cli' + cn: 'CA-Signed Client Cert' + key_usage: 'clientAuth' + + - role: ansibleguy.infra_certs + vars: + certs: + mode: 'ca' + path: '/etc/ssl/test5' + + cert: + name: 'self_minca_pwd' + domains: ['cert.test.ansibleguy.net'] + ips: [ '192.168.0.1' ] + cn: 'CA-Signed Server Cert' + pwd: 'Nope.' + key_usage: 'serverAuth' + crl_distribution: + crl_issuer: 'URI:https://ca.template.ansibleguy.net/' + full_name: 'URI:https://ca.template.ansibleguy.net/revocations.crl' + reasons: ['key_compromise', 'ca_compromise'] + + ca: + path: '/etc/ssl/test5/ca' + pwd: 'YouWantMyTreasure?YouCanHaveIt!SearchForIt-SomewhereOutThere-Hidden-IsTheBiggestTreasureOfTheWorld.' + cn: 'SelfSigned CA Cert' + org: 'AnsibleGuy Test' + email: 'testmaster@ansibleguy.net' + ou: 'Test' + country: 'AT' + state: 'Styria' + locality: 'Unknown' + valid_days: 5 + +- name: Converge LetsEncrypt + hosts: test-ag-certs-letsencrypt-{{ lookup('ansible.builtin.env', 'USER') }} + vars: + testing: true # target letsencrypt-staging + certs: + mode: 'le_certbot' + letsencrypt: + certs: + test: + domains: ['infra-certs.test.ansibleguy.net'] + email: 'testmaster@ansibleguy.net' + path: '/etc/ssl/test' + renew_timer: 'Mon *-*-* 03:00:00' + service: 'nginx' + roles: + - ansibleguy.infra_certs diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml new file mode 100644 index 0000000..d7dee5c --- /dev/null +++ b/molecule/default/molecule.yml @@ -0,0 +1,60 @@ +--- + +references: + docker: + all: &docker_all + docker_host: 'tcp://molecule-docker.local:2375' + # docker_host: 'unix://var/run/docker.sock' # localhost + purge_networks: true + image: 'debian:11-slim' + # for docker systemd config see: https://serverfault.com/questions/1053187/systemd-fails-to-run-in-a-docker-container-when-using-cgroupv2-cgroupns-priva + dockerfile: 'Dockerfile_debian11_systemd.j2' + build_image: yes + tmpfs: ['/tmp', '/run', '/run/lock'] + privileged: true + command: '/sbin/init' + +dependency: + name: galaxy +driver: + name: docker +platforms: + - name: test-ag-certs-internal-${USER} + docker_networks: + - name: 'test-ag-certs-${USER}' + ipam_config: + - subnet: '192.168.6.0/24' + gateway: '192.168.6.254' + networks: + - name: 'test-ag-certs-${USER}' + ipv4_address: '192.168.6.1' + groups: [grp_targets] + <<: *docker_all + + - name: test-ag-certs-letsencrypt-${USER} + networks: + - name: 'test-ag-certs-${USER}' + ipv4_address: '192.168.6.2' + groups: [grp_targets] + <<: *docker_all + exposed_ports: + - '80/tcp' + published_ports: # proxied for LetsEncrypt tests + - '0.0.0.0:8080:80/tcp' + +provisioner: + name: ansible +verifier: + name: ansible +scenario: + name: default + test_sequence: + - lint + - destroy + - syntax + - create + - converge + - verify # MUST NOT make changes + - idempotence + - check + - destroy diff --git a/molecule/default/prepare.yml b/molecule/default/prepare.yml new file mode 100644 index 0000000..645efb8 --- /dev/null +++ b/molecule/default/prepare.yml @@ -0,0 +1,19 @@ +--- + +- name: Prepare + hosts: test-ag-certs-letsencrypt-{{ lookup('ansible.builtin.env', 'USER') }} + tasks: + - name: Installing nginx + ansible.builtin.apt: + name: 'nginx' + + - name: Starting nginx + ansible.builtin.systemd: + name: 'nginx.service' + enabled: yes + state: started + + - name: Checking if nginx is running + wait_for: + port: 80 + timeout: 4 diff --git a/tasks/debian/letsencrypt/cert.yml b/tasks/debian/letsencrypt/cert.yml index 65f684b..df2f723 100644 --- a/tasks/debian/letsencrypt/cert.yml +++ b/tasks/debian/letsencrypt/cert.yml @@ -1,5 +1,12 @@ --- +- name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | Showing debug info" + ansible.builtin.debug: + var: le_cert + when: + - debug is defined + - debug + - name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | Creating directory" ansible.builtin.file: path: "{{ item }}" @@ -13,25 +20,29 @@ - name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | Certbot command (FYI)" ansible.builtin.debug: - msg: "certbot certonly --non-interactive --agree-tos --no-redirect{% if debug %} --staging{% endif %} + msg: "certbot certonly --non-interactive --agree-tos --no-redirect{% if debug or testing %} --staging{% endif %} --{{ CERT_CONFIG.letsencrypt.service }} --cert-name {{ le_name }} -{{ CERT_CONFIG.letsencrypt.verbosity }} --rsa-key-size {{ le_cert.key_size | default(CERT_CONFIG.cert.key_size, true) }} --config-dir {{ CERT_CONFIG.letsencrypt.path }} - {% for domain in le_cert.domains %}{% if domain | valid_domain %}--domain {{ domain }} {% endif %}{% endfor %} + {% for domain in le_cert.domains %}{% if domain | valid_hostname %}--domain {{ domain }} {% endif %}{% endfor %} {% if le_cert.email is not none %}--email {{ le_cert.email }} {% else %}--email {{ CERT_CONFIG.cert.email }} {% endif %}" when: le_changed - name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | Running certbot" - ansible.builtin.command: "certbot certonly --non-interactive --agree-tos --no-redirect{% if debug %} --staging{% endif %} + ansible.builtin.command: "certbot certonly --non-interactive --agree-tos --no-redirect{% if debug or testing %} --staging{% endif %} --{{ CERT_CONFIG.letsencrypt.service }} --cert-name {{ le_name }} -{{ CERT_CONFIG.letsencrypt.verbosity }} --rsa-key-size {{ le_cert.key_size | default(CERT_CONFIG.cert.key_size, true) }} --config-dir {{ CERT_CONFIG.letsencrypt.path }} - {% for domain in le_cert.domains %}{% if domain | valid_domain %}--domain {{ domain }} {% endif %}{% endfor %} + {% for domain in le_cert.domains %}{% if domain | valid_hostname %}--domain {{ domain }} {% endif %}{% endfor %} {% if le_cert.email is not none %}--email {{ le_cert.email }} {% else %}--email {{ CERT_CONFIG.cert.email }} {% endif %}" when: le_changed +- name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | Adding dummy certs" + ansible.builtin.include_tasks: test.yml + when: testing + - name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | Linking cert" ansible.builtin.file: state: link diff --git a/tasks/debian/letsencrypt/main.yml b/tasks/debian/letsencrypt/main.yml index 5b165ea..09ce63b 100644 --- a/tasks/debian/letsencrypt/main.yml +++ b/tasks/debian/letsencrypt/main.yml @@ -17,11 +17,18 @@ when: CERT_CONFIG.letsencrypt.service == 'nginx' - name: Certificates | Debian | LetsEncrypt Certbot | Pulling existing certs - ansible.builtin.command: 'certbot certificates' + ansible.builtin.command: "certbot certificates --config-dir {{ CERT_CONFIG.letsencrypt.path }}{% if debug or testing %} --staging{% endif %}" register: existing_certs_raw changed_when: false check_mode: false +- name: Certificates | Debian | LetsEncrypt Certbot | Existing certificates + ansible.builtin.debug: + var: existing_certs_raw.stdout + when: + - debug is defined + - debug + - name: Certificates | Debian | LetsEncrypt Certbot | Adding certificates ansible.builtin.include_tasks: cert.yml when: @@ -39,7 +46,7 @@ # todo: task gets stuck - name: Certificates | Debian | LetsEncrypt Certbot | Revoking certificates - ansible.builtin.command: "certbot revoke --cert-name {{ le_name }}{% if debug %} --staging{% endif %}" + ansible.builtin.command: "certbot revoke --cert-name {{ le_name }}{% if debug or testing %} --staging{% endif %}" when: - le_cert.state != 'present' - existing_certs_raw.stdout.find(le_name) != -1 @@ -51,7 +58,7 @@ with_dict: "{{ CERT_CONFIG.letsencrypt.certs }}" - name: Certificates | Debian | LetsEncrypt Certbot | Deleting certificates - ansible.builtin.command: "certbot delete --cert-name {{ le_name }}{% if debug %} --staging{% endif %}" + ansible.builtin.command: "certbot delete --cert-name {{ le_name }}{% if debug or testing %} --staging{% endif %}" when: - le_cert.state != 'present' - existing_certs_raw.stdout.find(le_name) != -1 @@ -89,6 +96,6 @@ state: started - name: Certificates | Debian | LetsEncrypt Certbot | Running renewal - ansible.builtin.command: "certbot renew --force-renewal{% if debug %} --staging{% endif %}" + ansible.builtin.command: "certbot renew --force-renewal{% if debug or testing %} --staging{% endif %}" when: CERT_CONFIG.letsencrypt.renew ignore_errors: true diff --git a/tasks/debian/letsencrypt/test.yml b/tasks/debian/letsencrypt/test.yml new file mode 100644 index 0000000..d16d735 --- /dev/null +++ b/tasks/debian/letsencrypt/test.yml @@ -0,0 +1,30 @@ +--- + +# adding dummy certificates for ansible-molecule testing + +- name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | DUMMY | Setting dummy config" + ansible.builtin.set_fact: + dummy_cert: + path: "{{ le_path }}" + cert: + cn: 'Molecule Dummy Cert' + domains: "{{ le_cert.domains }}" + email: "{{ le_cert.email }}" + + ca: + cn: 'Molecule Dummy CA' + email: "{{ le_cert.email }}" + +- name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | DUMMY | Installing dependencies" + ansible.builtin.package: + pkg: ['python3-cryptography'] + +- name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | DUMMY | Creating dummy CA" + ansible.builtin.include_tasks: internal/ca_minimal.yml + vars: + config_ca: "{{ CERT_CONFIG | combine(dummy_cert, recursive=true) }}" + +- name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | DUMMY | Creating dummy Certificate" + ansible.builtin.include_tasks: internal/cert.yml + vars: + config_cert: "{{ CERT_CONFIG | combine(dummy_cert, recursive=true) }}" diff --git a/tasks/internal/ca_minimal.yml b/tasks/internal/ca_minimal.yml index b9e3b84..eec657a 100644 --- a/tasks/internal/ca_minimal.yml +++ b/tasks/internal/ca_minimal.yml @@ -4,103 +4,109 @@ - name: Certificates | Internal | Minimal CA | Creating ca directory ansible.builtin.file: - path: "{{ CERT_CONFIG.ca.path }}" + path: "{{ config_ca.ca.path | default(config_ca.path, true) }}" state: directory - name: Certificates | Internal | Minimal CA | Generate ca private key (encrypted key) community.crypto.openssl_privatekey: - path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}" - passphrase: "{{ CERT_CONFIG.ca.pwd }}" - cipher: "{{ CERT_CONFIG.ca.cipher }}" - size: "{{ CERT_CONFIG.ca.key_size }}" - type: "{{ CERT_CONFIG.ca.key_type }}" - regenerate: "{{ CERT_CONFIG.ca.regenerate }}" - mode: "{{ CERT_CONFIG.mode_key }}" - owner: "{{ CERT_CONFIG.owner_key }}" - group: "{{ CERT_CONFIG.group_key }}" + path: "{{ config_ca.ca.path | default(config_ca.path, true) }}/ca.{{ config_ca.extension_key }}" + passphrase: "{{ config_ca.ca.pwd }}" + select_crypto_backend: "{{ config_ca.ca.backend }}" + cipher: "{{ config_ca.ca.cipher }}" + size: "{{ config_ca.ca.key_size }}" + type: "{{ config_ca.ca.key_type }}" + regenerate: "{{ config_ca.ca.regenerate }}" + mode: "{{ config_ca.mode_key }}" + owner: "{{ config_ca.owner_key }}" + group: "{{ config_ca.group_key }}" no_log: true - when: CERT_CONFIG.ca.pwd | default(none, true) is not none + when: config_ca.ca.pwd | default(none, true) is not none - name: Certificates | Internal | Minimal CA | Generate ca private key (plain key) community.crypto.openssl_privatekey: - path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}" - size: "{{ CERT_CONFIG.ca.key_size }}" - type: "{{ CERT_CONFIG.ca.key_type }}" - regenerate: "{{ CERT_CONFIG.ca.regenerate }}" - mode: "{{ CERT_CONFIG.mode_key }}" - owner: "{{ CERT_CONFIG.owner_key }}" - group: "{{ CERT_CONFIG.group_key }}" + path: "{{ config_ca.ca.path | default(config_ca.path, true) }}/ca.{{ config_ca.extension_key }}" + select_crypto_backend: "{{ config_ca.ca.backend }}" + size: "{{ config_ca.ca.key_size }}" + type: "{{ config_ca.ca.key_type }}" + regenerate: "{{ config_ca.ca.regenerate }}" + mode: "{{ config_ca.mode_key }}" + owner: "{{ config_ca.owner_key }}" + group: "{{ config_ca.group_key }}" no_log: true - when: CERT_CONFIG.ca.pwd | default(none, true) is none + when: config_ca.ca.pwd | default(none, true) is none # NOTE: for details see https://www.openssl.org/docs/man1.0.2/man5/x509v3_config.html - name: Certificates | Internal | Minimal CA | Generating ca signing-request (encrypted key) community.crypto.openssl_csr: - path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_csr }}" - privatekey_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}" - privatekey_passphrase: "{{ CERT_CONFIG.ca.pwd }}" + path: "{{ config_ca.ca.path | default(config_ca.path, true) }}/ca.{{ config_ca.extension_csr }}" + select_crypto_backend: "{{ config_ca.ca.backend }}" + privatekey_path: "{{ config_ca.ca.path | default(config_ca.path, true) }}/ca.{{ config_ca.extension_key }}" + privatekey_passphrase: "{{ config_ca.ca.pwd }}" basic_constraints: ['CA:TRUE', 'pathlen:2'] basic_constraints_critical: true key_usage: ['cRLSign', 'digitalSignature', 'keyCertSign'] key_usage_critical: true - digest: "{{ CERT_CONFIG.ca.digest }}" - common_name: "{{ CERT_CONFIG.ca.cn }}" - organization_name: "{{ CERT_CONFIG.ca.org }}" - country_name: "{{ CERT_CONFIG.ca.country }}" - state_or_province_name: "{{ CERT_CONFIG.ca.state }}" - locality_name: "{{ CERT_CONFIG.ca.locality }}" - email_address: "{{ CERT_CONFIG.ca.email }}" - mode: "{{ CERT_CONFIG.mode_cert }}" - owner: "{{ CERT_CONFIG.owner_cert }}" - group: "{{ CERT_CONFIG.group_cert }}" + digest: "{{ config_ca.ca.digest }}" + common_name: "{{ config_ca.ca.cn }}" + organization_name: "{{ config_ca.ca.org }}" + country_name: "{{ config_ca.ca.country }}" + state_or_province_name: "{{ config_ca.ca.state }}" + locality_name: "{{ config_ca.ca.locality }}" + email_address: "{{ config_ca.ca.email }}" + mode: "{{ config_ca.mode_cert }}" + owner: "{{ config_ca.owner_cert }}" + group: "{{ config_ca.group_cert }}" no_log: true - when: CERT_CONFIG.ca.pwd | default(none, true) is not none + when: config_ca.ca.pwd | default(none, true) is not none - name: Certificates | Internal | Minimal CA | Generating ca signing-request (plain key) community.crypto.openssl_csr: - path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_csr }}" - privatekey_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}" + path: "{{ config_ca.ca.path | default(config_ca.path, true) }}/ca.{{ config_ca.extension_csr }}" + select_crypto_backend: "{{ config_ca.ca.backend }}" + privatekey_path: "{{ config_ca.ca.path | default(config_ca.path, true) }}/ca.{{ config_ca.extension_key }}" basic_constraints: ['CA:TRUE', 'pathlen:2'] basic_constraints_critical: true key_usage: ['cRLSign', 'digitalSignature', 'keyCertSign'] key_usage_critical: true - digest: "{{ CERT_CONFIG.ca.digest }}" - common_name: "{{ CERT_CONFIG.ca.cn }}" - organization_name: "{{ CERT_CONFIG.ca.org }}" - country_name: "{{ CERT_CONFIG.ca.country }}" - state_or_province_name: "{{ CERT_CONFIG.ca.state }}" - locality_name: "{{ CERT_CONFIG.ca.locality }}" - email_address: "{{ CERT_CONFIG.ca.email }}" - mode: "{{ CERT_CONFIG.mode_cert }}" - owner: "{{ CERT_CONFIG.owner_cert }}" - group: "{{ CERT_CONFIG.group_cert }}" + digest: "{{ config_ca.ca.digest }}" + common_name: "{{ config_ca.ca.cn }}" + organization_name: "{{ config_ca.ca.org }}" + country_name: "{{ config_ca.ca.country }}" + state_or_province_name: "{{ config_ca.ca.state }}" + locality_name: "{{ config_ca.ca.locality }}" + email_address: "{{ config_ca.ca.email }}" + mode: "{{ config_ca.mode_cert }}" + owner: "{{ config_ca.owner_cert }}" + group: "{{ config_ca.group_cert }}" no_log: true - when: CERT_CONFIG.ca.pwd | default(none, true) is none + when: config_ca.ca.pwd | default(none, true) is none - name: Certificates | Internal | Minimal CA | Generating ca certificate (encrypted key) community.crypto.x509_certificate: - path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_cert }}" - csr_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_csr }}" - privatekey_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}" - privatekey_passphrase: "{{ CERT_CONFIG.ca.pwd }}" + path: "{{ config_ca.ca.path | default(config_ca.path, true) }}/ca.{{ config_ca.extension_cert }}" + select_crypto_backend: "{{ config_ca.ca.backend }}" + csr_path: "{{ config_ca.ca.path | default(config_ca.path, true) }}/ca.{{ config_ca.extension_csr }}" + privatekey_path: "{{ config_ca.ca.path | default(config_ca.path, true) }}/ca.{{ config_ca.extension_key }}" + privatekey_passphrase: "{{ config_ca.ca.pwd }}" provider: selfsigned - selfsigned_not_after: "+{{ CERT_CONFIG.ca.valid_days }}d" - mode: "{{ CERT_CONFIG.mode_cert }}" - owner: "{{ CERT_CONFIG.owner_cert }}" - group: "{{ CERT_CONFIG.group_cert }}" + selfsigned_not_after: "+{{ config_ca.ca.valid_days }}d" + mode: "{{ config_ca.mode_cert }}" + owner: "{{ config_ca.owner_cert }}" + group: "{{ config_ca.group_cert }}" no_log: true - when: CERT_CONFIG.ca.pwd | default(none, true) is not none + when: config_ca.ca.pwd | default(none, true) is not none - name: Certificates | Internal | Minimal CA | Generating ca certificate (plain key) community.crypto.x509_certificate: - path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_cert }}" - privatekey_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}" - csr_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_csr }}" + path: "{{ config_ca.ca.path | default(config_ca.path, true) }}/ca.{{ config_ca.extension_cert }}" + select_crypto_backend: "{{ config_ca.ca.backend }}" + privatekey_path: "{{ config_ca.ca.path | default(config_ca.path, true) }}/ca.{{ config_ca.extension_key }}" + csr_path: "{{ config_ca.ca.path | default(config_ca.path, true) }}/ca.{{ config_ca.extension_csr }}" provider: selfsigned - selfsigned_not_after: "+{{ CERT_CONFIG.ca.valid_days }}d" - mode: "{{ CERT_CONFIG.mode_cert }}" - owner: "{{ CERT_CONFIG.owner_cert }}" - group: "{{ CERT_CONFIG.group_cert }}" + selfsigned_not_after: "+{{ config_ca.ca.valid_days }}d" + mode: "{{ config_ca.mode_cert }}" + owner: "{{ config_ca.owner_cert }}" + group: "{{ config_ca.group_cert }}" no_log: true - when: CERT_CONFIG.ca.pwd | default(none, true) is none + when: config_ca.ca.pwd | default(none, true) is none diff --git a/tasks/internal/cert.yml b/tasks/internal/cert.yml index b9aa2e9..1d12e09 100644 --- a/tasks/internal/cert.yml +++ b/tasks/internal/cert.yml @@ -2,204 +2,230 @@ - name: Certificates | Internal | Cert | Generate private key (encrypted) community.crypto.openssl_privatekey: - path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}" - cipher: "{{ CERT_CONFIG.cert.cipher }}" - size: "{{ CERT_CONFIG.cert.key_size }}" - type: "{{ CERT_CONFIG.cert.key_type }}" - passphrase: "{{ CERT_CONFIG.cert.pwd }}" - regenerate: "{{ CERT_CONFIG.cert.regenerate }}" - mode: "{{ CERT_CONFIG.mode_key }}" - owner: "{{ CERT_CONFIG.owner_key }}" - group: "{{ CERT_CONFIG.group_key }}" + path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_key }}" + select_crypto_backend: "{{ config_cert.cert.backend }}" + cipher: "{{ config_cert.cert.cipher }}" + size: "{{ config_cert.cert.key_size }}" + type: "{{ config_cert.cert.key_type }}" + passphrase: "{{ config_cert.cert.pwd }}" + regenerate: "{{ config_cert.cert.regenerate }}" + mode: "{{ config_cert.mode_key }}" + owner: "{{ config_cert.owner_key }}" + group: "{{ config_cert.group_key }}" no_log: true - when: CERT_CONFIG.cert.pwd | default(none, true) is not none + when: config_cert.cert.pwd | default(none, true) is not none - name: Certificates | Internal | Cert | Generate private key (plain) community.crypto.openssl_privatekey: - path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}" - size: "{{ CERT_CONFIG.cert.key_size }}" - type: "{{ CERT_CONFIG.cert.key_type }}" - regenerate: "{{ CERT_CONFIG.cert.regenerate }}" - mode: "{{ CERT_CONFIG.mode_key }}" - owner: "{{ CERT_CONFIG.owner_key }}" - group: "{{ CERT_CONFIG.group_key }}" + path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_key }}" + select_crypto_backend: "{{ config_cert.cert.backend }}" + size: "{{ config_cert.cert.key_size }}" + type: "{{ config_cert.cert.key_type }}" + regenerate: "{{ config_cert.cert.regenerate }}" + mode: "{{ config_cert.mode_key }}" + owner: "{{ config_cert.owner_key }}" + group: "{{ config_cert.group_key }}" no_log: true - when: CERT_CONFIG.cert.pwd | default(none, true) is none + when: config_cert.cert.pwd | default(none, true) is none - name: Certificates | Internal | Cert | Setting SAN ansible.builtin.set_fact: - cert_san: "{% for domain in CERT_CONFIG.cert.domains %} - {% if domain | valid_domain %}DNS:{{ domain }}{% if not loop.last %},{% endif %}{% endif %} + cert_san: "{% for domain in config_cert.cert.domains %} + {% if domain | valid_hostname %}DNS:{{ domain }}{% if not loop.last %},{% endif %}{% endif %} {% endfor %} - {% for ip in CERT_CONFIG.cert.ips %} + {% for ip in config_cert.cert.ips %} {% if ip | valid_ip %},IP:{{ ip }}{% endif %} - {% endfor %}" + {% endfor %} + {% if config_cert.cert.san_other %} + {% if config_cert.cert.domains | length > 0 or config_cert.cert.ips | length > 0 %},{% endif %} + {{ config_cert.cert.san_other }} + {% endif %}" + when: > + config_cert.cert.domains | length > 0 or + config_cert.cert.ips | length > 0 or + config_cert.cert.san_other + +- name: Certificates | Internal | Cert | Setting SAN (fallback) + ansible.builtin.set_fact: + cert_san: '' + when: + - config_cert.cert.domains | length == 0 + - config_cert.cert.ips | length == 0 + - not config_cert.cert.san_other - name: Certificates | Internal | Cert | Generating signing-request (encrypted key) community.crypto.openssl_csr: - path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_csr }}" - privatekey_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}" - privatekey_passphrase: "{{ CERT_CONFIG.cert.pwd }}" - digest: "{{ CERT_CONFIG.cert.digest }}" - common_name: "{{ CERT_CONFIG.cert.cn }}" - organization_name: "{{ CERT_CONFIG.cert.org }}" - country_name: "{{ CERT_CONFIG.cert.country }}" - state_or_province_name: "{{ CERT_CONFIG.cert.state }}" - locality_name: "{{ CERT_CONFIG.cert.locality }}" - email_address: "{{ CERT_CONFIG.cert.email }}" - extended_key_usage: "{{ CERT_CONFIG.cert.key_usage }}" - ocsp_must_staple: "{{ CERT_CONFIG.cert.ocsp_staple }}" - crl_distribution_points: "{{ CERT_CONFIG.cert.crl_distribution }}" - subject_alt_name: "{{ cert_san | replace(' ', '') | default('DNS:localhost', true) }}" - mode: "{{ CERT_CONFIG.mode_cert }}" - owner: "{{ CERT_CONFIG.owner_cert }}" - group: "{{ CERT_CONFIG.group_cert }}" + path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_csr }}" + select_crypto_backend: "{{ config_cert.cert.backend }}" + privatekey_path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_key }}" + privatekey_passphrase: "{{ config_cert.cert.pwd }}" + digest: "{{ config_cert.cert.digest }}" + common_name: "{{ config_cert.cert.cn }}" + organization_name: "{{ config_cert.cert.org }}" + country_name: "{{ config_cert.cert.country }}" + state_or_province_name: "{{ config_cert.cert.state }}" + locality_name: "{{ config_cert.cert.locality }}" + email_address: "{{ config_cert.cert.email }}" + extended_key_usage: "{{ config_cert.cert.key_usage }}" + ocsp_must_staple: "{{ config_cert.cert.ocsp_staple }}" + crl_distribution_points: "{{ config_cert.cert.crl_distribution | ensure_list }}" + subject_alt_name: "{{ cert_san | replace(' ', '') | default(omit, true) }}" + mode: "{{ config_cert.mode_cert }}" + owner: "{{ config_cert.owner_cert }}" + group: "{{ config_cert.group_cert }}" no_log: true - when: CERT_CONFIG.cert.pwd | default(none, true) is not none + when: config_cert.cert.pwd | default(none, true) is not none changed_when: false - name: Certificates | Internal | Cert | Generating signing-request (plain key) community.crypto.openssl_csr: - path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_csr }}" - privatekey_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}" - digest: "{{ CERT_CONFIG.cert.digest }}" - common_name: "{{ CERT_CONFIG.cert.cn }}" - organization_name: "{{ CERT_CONFIG.cert.org }}" - country_name: "{{ CERT_CONFIG.cert.country }}" - state_or_province_name: "{{ CERT_CONFIG.cert.state }}" - locality_name: "{{ CERT_CONFIG.cert.locality }}" - email_address: "{{ CERT_CONFIG.cert.email }}" - extended_key_usage: "{{ CERT_CONFIG.cert.key_usage }}" - ocsp_must_staple: "{{ CERT_CONFIG.cert.ocsp_staple }}" - crl_distribution_points: "{{ CERT_CONFIG.cert.crl_distribution }}" - subject_alt_name: "{{ cert_san | replace(' ', '') | default('DNS:localhost', true) }}" - mode: "{{ CERT_CONFIG.mode_cert }}" - owner: "{{ CERT_CONFIG.owner_cert }}" - group: "{{ CERT_CONFIG.group_cert }}" + path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_csr }}" + select_crypto_backend: "{{ config_cert.cert.backend }}" + privatekey_path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_key }}" + digest: "{{ config_cert.cert.digest }}" + common_name: "{{ config_cert.cert.cn }}" + organization_name: "{{ config_cert.cert.org }}" + country_name: "{{ config_cert.cert.country }}" + state_or_province_name: "{{ config_cert.cert.state }}" + locality_name: "{{ config_cert.cert.locality }}" + email_address: "{{ config_cert.cert.email }}" + extended_key_usage: "{{ config_cert.cert.key_usage }}" + ocsp_must_staple: "{{ config_cert.cert.ocsp_staple }}" + crl_distribution_points: "{{ config_cert.cert.crl_distribution | ensure_list }}" + subject_alt_name: "{{ cert_san | replace(' ', '') | default(omit, true) }}" + mode: "{{ config_cert.mode_cert }}" + owner: "{{ config_cert.owner_cert }}" + group: "{{ config_cert.group_cert }}" no_log: true - when: CERT_CONFIG.cert.pwd | default(none, true) is none + when: config_cert.cert.pwd | default(none, true) is none changed_when: false - name: Certificates | Internal | Cert | Self-Signed | Generating certificate (encrypted key) community.crypto.x509_certificate: - path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_cert }}" - privatekey_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}" - privatekey_passphrase: "{{ CERT_CONFIG.cert.pwd }}" - csr_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_csr }}" + path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_cert }}" + select_crypto_backend: "{{ config_cert.cert.backend }}" + privatekey_path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_key }}" + privatekey_passphrase: "{{ config_cert.cert.pwd }}" + csr_path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_csr }}" provider: selfsigned - selfsigned_not_after: "+{{ CERT_CONFIG.cert.valid_days }}d" - mode: "{{ CERT_CONFIG.mode_cert }}" - owner: "{{ CERT_CONFIG.owner_cert }}" - group: "{{ CERT_CONFIG.group_cert }}" + selfsigned_not_after: "+{{ config_cert.cert.valid_days }}d" + mode: "{{ config_cert.mode_cert }}" + owner: "{{ config_cert.owner_cert }}" + group: "{{ config_cert.group_cert }}" no_log: true when: - - CERT_CONFIG.cert.pwd | default(none, true) is not none - - CERT_CONFIG.mode == 'selfsigned' + - config_cert.cert.pwd | default(none, true) is not none + - config_cert.mode == 'selfsigned' - name: Certificates | Internal | Cert | Self-Signed | Generating certificate (plain key) community.crypto.x509_certificate: - path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_cert }}" - privatekey_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}" - csr_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_csr }}" + path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_cert }}" + select_crypto_backend: "{{ config_cert.cert.backend }}" + privatekey_path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_key }}" + csr_path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_csr }}" provider: selfsigned - selfsigned_not_after: "+{{ CERT_CONFIG.cert.valid_days }}d" - mode: "{{ CERT_CONFIG.mode_cert }}" - owner: "{{ CERT_CONFIG.owner_cert }}" - group: "{{ CERT_CONFIG.group_cert }}" + selfsigned_not_after: "+{{ config_cert.cert.valid_days }}d" + mode: "{{ config_cert.mode_cert }}" + owner: "{{ config_cert.owner_cert }}" + group: "{{ config_cert.group_cert }}" no_log: true when: - - CERT_CONFIG.cert.pwd | default(none, true) is none - - CERT_CONFIG.mode == 'selfsigned' + - config_cert.cert.pwd | default(none, true) is none + - config_cert.mode == 'selfsigned' - name: Certificates | Internal | Cert | CA-Signed | Generating certificate (encrypted key; encrypted ca-key) community.crypto.x509_certificate: - path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_cert }}" - privatekey_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}" - privatekey_passphrase: "{{ CERT_CONFIG.cert.pwd }}" - csr_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_csr }}" + path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_cert }}" + select_crypto_backend: "{{ config_cert.cert.backend }}" + privatekey_path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_key }}" + privatekey_passphrase: "{{ config_cert.cert.pwd }}" + csr_path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_csr }}" provider: ownca - ownca_not_after: "+{{ CERT_CONFIG.cert.valid_days }}d" - ownca_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_cert }}" - ownca_privatekey_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}" - ownca_privatekey_passphrase: "{{ CERT_CONFIG.ca.pwd }}" - mode: "{{ CERT_CONFIG.mode_cert }}" - owner: "{{ CERT_CONFIG.owner_cert }}" - group: "{{ CERT_CONFIG.group_cert }}" + ownca_not_after: "+{{ config_cert.cert.valid_days }}d" + ownca_path: "{{ config_cert.ca.path | default(config_cert.path, true) }}/ca.{{ config_cert.extension_cert }}" + ownca_privatekey_path: "{{ config_cert.ca.path | default(config_cert.path, true) }}/ca.{{ config_cert.extension_key }}" + ownca_privatekey_passphrase: "{{ config_cert.ca.pwd }}" + mode: "{{ config_cert.mode_cert }}" + owner: "{{ config_cert.owner_cert }}" + group: "{{ config_cert.group_cert }}" no_log: true when: - - CERT_CONFIG.ca.pwd | default(none, true) is not none - - CERT_CONFIG.cert.pwd | default(none, true) is not none - - CERT_CONFIG.mode == 'ca' + - config_cert.ca.pwd | default(none, true) is not none + - config_cert.cert.pwd | default(none, true) is not none + - config_cert.mode == 'ca' - name: Certificates | Internal | Cert | CA-Signed | Generating certificate (plain key; encrypted ca-key) community.crypto.x509_certificate: - path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_cert }}" - privatekey_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}" - csr_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_csr }}" + path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_cert }}" + select_crypto_backend: "{{ config_cert.cert.backend }}" + privatekey_path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_key }}" + csr_path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_csr }}" provider: ownca - ownca_not_after: "+{{ CERT_CONFIG.cert.valid_days }}d" - ownca_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_cert }}" - ownca_privatekey_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}" - ownca_privatekey_passphrase: "{{ CERT_CONFIG.ca.pwd }}" - mode: "{{ CERT_CONFIG.mode_cert }}" - owner: "{{ CERT_CONFIG.owner_cert }}" - group: "{{ CERT_CONFIG.group_cert }}" + ownca_not_after: "+{{ config_cert.cert.valid_days }}d" + ownca_path: "{{ config_cert.ca.path | default(config_cert.path, true) }}/ca.{{ config_cert.extension_cert }}" + ownca_privatekey_path: "{{ config_cert.ca.path | default(config_cert.path, true) }}/ca.{{ config_cert.extension_key }}" + ownca_privatekey_passphrase: "{{ config_cert.ca.pwd }}" + mode: "{{ config_cert.mode_cert }}" + owner: "{{ config_cert.owner_cert }}" + group: "{{ config_cert.group_cert }}" no_log: true when: - - CERT_CONFIG.ca.pwd | default(none, true) is not none - - CERT_CONFIG.cert.pwd | default(none, true) is none - - CERT_CONFIG.mode == 'ca' + - config_cert.ca.pwd | default(none, true) is not none + - config_cert.cert.pwd | default(none, true) is none + - config_cert.mode == 'ca' - name: Certificates | Internal | Cert | CA-Signed | Generating certificate (encrypted key; plain ca-key) community.crypto.x509_certificate: - path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_cert }}" - privatekey_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}" - privatekey_passphrase: "{{ CERT_CONFIG.cert.pwd }}" - csr_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_csr }}" + path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_cert }}" + select_crypto_backend: "{{ config_cert.cert.backend }}" + privatekey_path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_key }}" + privatekey_passphrase: "{{ config_cert.cert.pwd }}" + csr_path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_csr }}" provider: ownca - ownca_not_after: "+{{ CERT_CONFIG.cert.valid_days }}d" - ownca_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_cert }}" - ownca_privatekey_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}" - mode: "{{ CERT_CONFIG.mode_cert }}" - owner: "{{ CERT_CONFIG.owner_cert }}" - group: "{{ CERT_CONFIG.group_cert }}" + ownca_not_after: "+{{ config_cert.cert.valid_days }}d" + ownca_path: "{{ config_cert.ca.path | default(config_cert.path, true) }}/ca.{{ config_cert.extension_cert }}" + ownca_privatekey_path: "{{ config_cert.ca.path | default(config_cert.path, true) }}/ca.{{ config_cert.extension_key }}" + mode: "{{ config_cert.mode_cert }}" + owner: "{{ config_cert.owner_cert }}" + group: "{{ config_cert.group_cert }}" no_log: true when: - - CERT_CONFIG.ca.pwd | default(none, true) is none - - CERT_CONFIG.cert.pwd | default(none, true) is not none - - CERT_CONFIG.mode == 'ca' + - config_cert.ca.pwd | default(none, true) is none + - config_cert.cert.pwd | default(none, true) is not none + - config_cert.mode == 'ca' - name: Certificates | Internal | Cert | CA-Signed | Generating certificate (plain key; plain ca-key) community.crypto.x509_certificate: - path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_cert }}" - privatekey_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}" - csr_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_csr }}" + path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_cert }}" + select_crypto_backend: "{{ config_cert.cert.backend }}" + privatekey_path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_key }}" + csr_path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_csr }}" provider: ownca - ownca_not_after: "+{{ CERT_CONFIG.cert.valid_days }}d" - ownca_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_cert }}" - ownca_privatekey_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}" - mode: "{{ CERT_CONFIG.mode_cert }}" - owner: "{{ CERT_CONFIG.owner_cert }}" - group: "{{ CERT_CONFIG.group_cert }}" + ownca_not_after: "+{{ config_cert.cert.valid_days }}d" + ownca_path: "{{ config_cert.ca.path | default(config_cert.path, true) }}/ca.{{ config_cert.extension_cert }}" + ownca_privatekey_path: "{{ config_cert.ca.path | default(config_cert.path, true) }}/ca.{{ config_cert.extension_key }}" + mode: "{{ config_cert.mode_cert }}" + owner: "{{ config_cert.owner_cert }}" + group: "{{ config_cert.group_cert }}" no_log: true when: - - CERT_CONFIG.ca.pwd | default(none, true) is none - - CERT_CONFIG.cert.pwd | default(none, true) is none - - CERT_CONFIG.mode == 'ca' + - config_cert.ca.pwd | default(none, true) is none + - config_cert.cert.pwd | default(none, true) is none + - config_cert.mode == 'ca' - name: Certificates | Internal | Cert | CA-Signed | Creating chained certificate - ansible.builtin.shell: "cat {{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_cert }} - {{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_cert }} > - {{ CERT_CONFIG.path }}/{{ name }}.chain.{{ CERT_CONFIG.extension_cert }}" + ansible.builtin.shell: "cat {{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.{{ config_cert.extension_cert }} + {{ config_cert.ca.path | default(config_cert.path, true) }}/ca.{{ config_cert.extension_cert }} > + {{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.chain.{{ config_cert.extension_cert }}" args: - creates: "{{ CERT_CONFIG.path }}/{{ name }}.chain.{{ CERT_CONFIG.extension_cert }}" - when: CERT_CONFIG.mode == 'ca' + creates: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.chain.{{ config_cert.extension_cert }}" + when: config_cert.mode == 'ca' check_mode: false - name: Certificates | Internal | Cert | CA-Signed | Setting privileges on chained certificate ansible.builtin.file: - path: "{{ CERT_CONFIG.path }}/{{ name }}.chain.{{ CERT_CONFIG.extension_cert }}" - mode: "{{ CERT_CONFIG.mode_cert }}" - owner: "{{ CERT_CONFIG.owner_cert }}" - group: "{{ CERT_CONFIG.group_cert }}" - when: CERT_CONFIG.mode == 'ca' + path: "{{ config_cert.path }}/{{ name | default(config_cert.cert.name) }}.chain.{{ config_cert.extension_cert }}" + mode: "{{ config_cert.mode_cert }}" + owner: "{{ config_cert.owner_cert }}" + group: "{{ config_cert.group_cert }}" + when: config_cert.mode == 'ca' diff --git a/tasks/internal/main.yml b/tasks/internal/main.yml index 28b7be1..4c02174 100644 --- a/tasks/internal/main.yml +++ b/tasks/internal/main.yml @@ -1,5 +1,12 @@ --- +- name: Certificates | Internal | Checking config + ansible.builtin.assert: + that: + - CERT_CONFIG.cert.name or name + - CERT_CONFIG.cert.cn + - CERT_CONFIG.mode != 'ca' or CERT_CONFIG.ca.cn + - name: Certificates | Internal | Installing dependencies ansible.builtin.package: pkg: ['python3-cryptography'] @@ -13,10 +20,14 @@ - name: Certificates | Internal | Minimal CA ansible.builtin.import_tasks: ca_minimal.yml + vars: + config_ca: "{{ CERT_CONFIG }}" when: CERT_CONFIG.mode == 'ca' tags: [ca] - name: Certificates | Internal | Cert ansible.builtin.import_tasks: cert.yml + vars: + config_cert: "{{ CERT_CONFIG }}" when: "CERT_CONFIG.mode in ['ca', 'selfsigned']" tags: [certs] diff --git a/tasks/main.yml b/tasks/main.yml index 76e0b7e..82f517c 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,5 +1,7 @@ --- +# todo: allow for a dictionary of certs to be passed + - name: Certificates | Checking config ansible.builtin.assert: that: @@ -19,6 +21,12 @@ - debug is defined - debug +- name: Certificates | Checking for invalid domains/hostnames + ansible.builtin.pause: + prompt: "It seems you have configured an invalid domain/hostname: '{{ item }}' - do you want to continue?" + when: not item | valid_hostname + loop: "{{ CERT_CONFIG.cert.domains }}" + - name: Certificates | Internal signed ansible.builtin.include_tasks: internal/main.yml when: "CERT_CONFIG.mode in ['pki', 'ca', 'selfsigned']"