- include_tasks: prepare_item.yml - name: define combined options set_fact: ca_combined: "{{ ca_options | d({}) | combine(item) }}" - name: define cert parameters set_fact: key_path: "{%- if item.key is defined -%}{{ item.key }}\ {%- else -%}{{ ca_combined.path ~ '/' ~ kt.name ~ '.' ~ ca_key_ext }}\ {%- endif -%}" cert_path: "{%- if item.cert is defined -%}{{ item.cert }}\ {%- else -%}{{ ca_combined.path ~ '/' ~ kt.name ~ '.' ~ ca_crt_ext }}\ {%- endif -%}" use_acme: "{{ ca_combined.acme | d(has_acme | d(false)) }}" - name: define tld and presets set_fact: ca_tld: "{{ ca_combined.tld | d(host_tld) }}" ca_presets: web: cn: FQDN eku: ['clientAuth', 'serverAuth'] ku: ['digitalSignature', 'keyEncipherment', 'keyAgreement'] san: FQDN psh: cn: FQDN eku: ['serverAuth'] ku: ['digitalSignature', 'keyEncipherment', 'keyAgreement'] san: FQDN - name: select a preset set_fact: ca_preset: > {% if item.preset is defined -%}{{ ca_presets[item.preset] }} {%- elif ca_options.preset is defined -%}{{ ca_presets[ca_options.preset] }} {%- else -%}{{ None }} {%- endif %} - name: generate private key community.crypto.openssl_privatekey: path: "{{ key_path }}" size: "{{ kt.size | d(omit) }}" curve: "{{ kt.curve | d(omit) }}" type: "{{ kt.type }}" backup: yes force: no format: pkcs8 format_mismatch: convert regenerate: never mode: "{{ k_mode | d(omit) }}" owner: "{{ k_owner | d(omit) }}" group: "{{ k_group | d(omit) }}" notify: "{{ ca_options.notify | d(omit) }}" - name: generate in-memory csr request for private key community.crypto.openssl_csr_pipe: basic_constraints: - 'CA:FALSE' basic_constraints_critical: yes digest: "{{ kt.digest | d(omit) }}" key_usage_critical: yes privatekey_path: "{{ key_path }}" common_name: "{%- if item.cn is defined -%}{{ item.cn }}\ {%- elif ca_options.cn is defined -%}{{ ca_options.cn }}\ {%- elif ca_preset.cn == 'FQDN' -%}{{ host_name ~ '.' ~ ca_tld }}\ {%- elif ca_preset.cn is defined -%}{{ ca_preset.cn }}\ {%- endif -%}" extended_key_usage: "{%- if item.eku is defined -%}{{ item.eku }}\ {%- elif ca_options.eku is defined -%}{{ ca_options.eku }}\ {%- elif ca_preset.eku is defined -%}{{ ca_preset.eku }}\ {%- endif -%}" key_usage: "{%- if item.ku is defined -%}{{ item.ku }}\ {%- elif ca_options.ku is defined -%}{{ ca_options.ku }}\ {%- elif ca_preset.ku is defined -%}{{ ca_preset.ku }}\ {%- else -%}{{ ['digitalSignature', 'keyEncipherment', 'keyAgreement'] }}\ {%- endif -%}" subject_alt_name: "{%- if item.san is defined -%}{{ item.san }}\ {%- elif ca_options.san is defined -%}{{ ca_options.san }}\ {%- elif item.cn is defined -%}{{ ['DNS:' ~ item.cn] }}\ {%- elif ca_options.cn is defined -%}{{ ['DNS:' ~ ca_options.cn] }}\ {%- elif ca_preset.san == 'FQDN' -%}{{ ['DNS:' ~ host_name ~ '.' ~ ca_tld] }}\ {%- elif ca_preset.san is defined -%}{{ ca_preset.san }}\ {%- endif -%}" ocsp_must_staple: "{{ (has_acme | d(false)) and (ca_options.ocsp_must_staple | d(false)) }}" register: csr changed_when: no - name: check if cert already exists stat: path: "{{ cert_path }}" register: cert_exists - name: slurp cert if exists slurp: src: "{{ cert_path }}" when: cert_exists.stat.exists register: cert - name: check if the cert validity period is about to expire community.crypto.x509_certificate_info: content: "{{ cert.content | b64decode }}" valid_at: reissue_period: "+{%- if has_acme | d(false) == true -%}45d\ {%- else -%}{{ ca_reissue_period | d('8w') }}\ {%- endif -%}" when: cert_exists.stat.exists register: cert_info - block: - name: generate certificate on ca community.crypto.x509_certificate_pipe: content: "{{ (cert.content | b64decode) if cert_exists.stat.exists else omit }}" csr_content: "{{ csr.csr }}" provider: ownca ownca_not_after: "{{ item.duration | d('+365d') }}" ownca_not_before: -1d ownca_digest: "{{ kt.digest | d(omit) }}" ownca_path: "{{ ca_dir }}/{{ ca_ip }}{{ kt.name }}.{{ ca_crt_ext }}" ownca_privatekey_path: "{{ ca_dir }}/{{ ca_ip }}{{ kt.name }}.{{ ca_key_ext }}" ownca_privatekey_passphrase: "{{ ca_pk_inter_password }}" force: "{{ cert_exists.stat.exists and not cert_info.valid_at.reissue_period }}" register: cert delegate_to: "{{ services.ca.hostname }}" notify: "{{ ca_options.notify | d(omit) }}" - name: save new cert if it was changed copy: dest: "{{ cert_path }}" content: "{{ cert.certificate }}" mode: "{{ k_mode | d(omit) }}" owner: "{{ k_owner | d(omit) }}" group: "{{ k_group | d(omit) }}" follow: "{{ (ca_options | combine(item)).follow_symlinks | d(omit) }}" when: cert is changed notify: "{{ ca_options.notify | d(omit) }}" when: has_acme | d(false) == false - name: generate acme certificate include_tasks: gen_acme.yml when: has_acme | d(false) == true - block: - name: slurp certificate slurp: src: "{{ cert_path }}" register: cert - name: complete certificate chain community.crypto.certificate_complete_chain: input_chain: "{{ ((cert.content | b64decode).split('\n\n'))[0] }}" root_certificates: /etc/ssl/certs register: chain - name: save chain to file copy: dest: "{{ item.chain }}" content: | {% set result = chain.complete_chain %} {% set _ = result.pop(0) %} {{ result | join('') }} mode: "{{ k_mode | d(omit) }}" owner: "{{ k_owner | d(omit) }}" group: "{{ k_group | d(omit) }}" follow: "{{ (ca_options | combine(item)).follow_symlinks | d(omit) }}" notify: "{{ ca_options.notify | d(omit) }}" when: item.chain is string - block: - name: slurp intermediate from ca slurp: src: "{{ ca_dir }}/{{ ca_ip }}{{ kt.name }}.{{ ca_crt_ext }}" register: inter delegate_to: "{{ services.ca.hostname }}" - name: add intermediate cert if requested blockinfile: block: "{{ inter.content | b64decode }}" insertafter: EOF marker: "" path: "{{ cert_path }}" notify: "{{ ca_options.notify | d(omit) }}" when: (use_acme | d(false) == false) and (cert is changed) and ((ca_options | combine(item)).concat_inter | d(true) == true) - block: - name: slurp root from ca slurp: src: "{{ ca_dir }}/{{ ca_rp }}{{ kt.name }}.{{ ca_crt_ext }}" register: root delegate_to: "{{ services.ca.hostname }}" - name: add root cert if requested blockinfile: block: "{{ root.content | b64decode }}" insertafter: EOF marker: "" path: "{{ cert_path }}" notify: "{{ ca_options.notify | d(omit) }}" when: (use_acme | d(false) == false) and (cert is changed) and ((ca_options | combine(item)).concat_root | d(false) == true)