created tasks to handle minimal-ca, self-signed and letsencrypt-certbot certificates
This commit is contained in:
		
							parent
							
								
									84c2277e80
								
							
						
					
					
						commit
						066d95535e
					
				
							
								
								
									
										114
									
								
								README.md
								
								
								
								
							
							
						
						
									
										114
									
								
								README.md
								
								
								
								
							|  | @ -1,23 +1,28 @@ | |||
| # Ansible Role for certificate generation | ||||
| # Certificate Generator Role | ||||
| 
 | ||||
| **Tested:** | ||||
| * Debian 11 | ||||
| 
 | ||||
| ## Functionality | ||||
| 
 | ||||
| * Package installation | ||||
| * **Package installation** | ||||
|   * Ansible dependencies (_minimal_) | ||||
|   *  | ||||
| * Configuration | ||||
|   * Two Possible Modes | ||||
|     * Generate Self-Signed certificate | ||||
|     * Create an internal-ca and generate certificates using it | ||||
|   * Default config: | ||||
|   * Crypto Dependencies | ||||
| 
 | ||||
| 
 | ||||
| * **Configuration** | ||||
|   * **Four Possible Modes**: | ||||
|     * Generate **Self-Signed** certificate | ||||
|     * Use a **minimal Certificate Authority** to create signed certificates | ||||
|     * Configure **LetsEncrypt-Certbot** to generate publicly valid certificates | ||||
|       * Supported for Nginx and Apache | ||||
|       * Host needs to have a valid public dns record pointed at it | ||||
|       * Needs to be publicly reachable over port 80/tcp | ||||
|     * _Use a proper **Certificate Authority** (_full PKI_) to create **signed certificates**_ => not yet available | ||||
| 
 | ||||
| 
 | ||||
|   * **Default config**: | ||||
|     * Mode => Self-Signed | ||||
|   * Default opt-ins: | ||||
|     *  | ||||
|   * Default opt-outs: | ||||
|     *  | ||||
| 
 | ||||
| 
 | ||||
| ## Info | ||||
|  | @ -28,6 +33,10 @@ | |||
| * **Note:** Most of this functionality can be opted in or out using the main defaults file and variables! | ||||
| 
 | ||||
| 
 | ||||
| * **Note:** The certificate file-name (_name variable as defined or else CommonName_) will be updated: | ||||
|   * spaces are transformed into underlines | ||||
|   * all Characters except "0-9a-zA-Z." are removed | ||||
|   * the file-extension (_crt/chain.crt/key/csr_) will be appended | ||||
| 
 | ||||
| ## Requirements | ||||
| 
 | ||||
|  | @ -36,20 +45,89 @@ | |||
| 
 | ||||
| ## Usage | ||||
| 
 | ||||
| Define the config as needed: | ||||
| ### Notes | ||||
| The **self-signed and minimal-ca** modes will only create a single certificate per run. | ||||
| 
 | ||||
| Re-runs can save some overhead by using the 'certs' tag. | ||||
| 
 | ||||
| 
 | ||||
| The **LetsEncrypt** mode will create/remove multiple certificates as defined. | ||||
| 
 | ||||
| 
 | ||||
| ### Config | ||||
| 
 | ||||
| Example for LetsEncrypt config: | ||||
| 
 | ||||
| ```yaml | ||||
| app: | ||||
| 
 | ||||
| certs: | ||||
|   mode: 'le_certbot' | ||||
|   path: '/etc/apache2/ssl' | ||||
|   letsencrypt: | ||||
|     certs: | ||||
|       myNiceSite: | ||||
|         domains: ['myRandomSite.net', 'ansibleguy.net'] | ||||
|         email: 'certs@template.ansibleguy.net' | ||||
|     service: 'apache' | ||||
| ``` | ||||
| 
 | ||||
| Example for Self-Signed config: | ||||
| 
 | ||||
| ```yaml | ||||
| certs: | ||||
|   mode: 'selfsigned' | ||||
|   path: '/etc/nginx/ssl' | ||||
|   group_key: 'nginx' | ||||
|   owner_cert: 'nginx' | ||||
|   cert: | ||||
|     cn: 'My great certificate!' | ||||
|     org: 'AnsibleGuy' | ||||
|     country: 'AT' | ||||
|     email: 'certs@template.ansibleguy.net' | ||||
|     domains: ['mySoGreat.site', 'ansibleguy.net'] | ||||
|     ips: ['192.168.44.2'] | ||||
|     pwd: !vault ... | ||||
| ``` | ||||
| 
 | ||||
| Example for minimal-CA config: | ||||
| 
 | ||||
| ```yaml | ||||
| certs: | ||||
|   mode: 'ca' | ||||
|   path: '/etc/ca/certs' | ||||
|   mode_key: '0400' | ||||
|   cert: | ||||
|     name: 'custom_file_name'  # extension will be appended | ||||
|     cn: 'My great certificate!' | ||||
|     org: 'AnsibleGuy' | ||||
|     country: 'AT' | ||||
|     email: 'certs@template.ansibleguy.net' | ||||
|     domains: ['mySoGreat.site', 'ansibleguy.net'] | ||||
|   ca: | ||||
|     path: '/etc/ca' | ||||
|     cn: 'SUPER CertificateAuthority' | ||||
|     org: 'AnsibleGuy' | ||||
|     country: 'AT' | ||||
|     email: 'certs@template.ansibleguy.net' | ||||
|     pwd: !vault ... | ||||
| ``` | ||||
| 
 | ||||
| Using the minimal-CA you can create multiple certificates signed by the CA by re-running the role with changed 'cert' settings. | ||||
| 
 | ||||
| 
 | ||||
| You might want to use 'ansible-vault' to encrypt your passwords: | ||||
| ```bash | ||||
| ansible-vault encrypt_string | ||||
| ``` | ||||
| 
 | ||||
| ### Execution | ||||
| 
 | ||||
| Run the playbook: | ||||
| ```bash | ||||
| ansible-playbook -K -D -i inventory/hosts.yml playbook.yml | ||||
| ansible-playbook -K -D -i inventory/hosts.yml playbook.yml --ask-vault-pass | ||||
| ``` | ||||
| 
 | ||||
| There are also some useful **tags** available: | ||||
| * base => only configure basics; sites will not be touched | ||||
| * sites | ||||
| * certs => ignore ca tasks; only generate certs | ||||
| * selfsigned | ||||
| * config | ||||
| * certs | ||||
|  |  | |||
|  | @ -1,8 +1,85 @@ | |||
| --- | ||||
| 
 | ||||
| # default config => is overwritten by provided config | ||||
| default_app: {} | ||||
| default_certs: | ||||
|   mode: 'selfsigned'  # selfsigned, ca, ca_min, le_certbot | ||||
|   path: '/etc/certs' | ||||
| 
 | ||||
| APP_CONFIG: "{{ default_app | combine(app, recursive=true) }}" | ||||
|   cert: | ||||
|     name: | ||||
|     key_size: 4096  # 1024, 2048, 4096 | ||||
|     key_type: 'RSA' | ||||
|     cipher: 'AES-256-CBC'  # see: 'openssl list -cipher-algorithms' | ||||
|     digest: 'sha256' | ||||
|     regenerate: 'partial_idempotence' | ||||
|     pwd: | ||||
|     domains: [] | ||||
|     ips: [] | ||||
| 
 | ||||
| default_instance_config: {} | ||||
|     # certificate config | ||||
|     cn: 'Ansible Certificate' | ||||
|     org: | ||||
|     ou: | ||||
|     country: | ||||
|     state: | ||||
|     locality: | ||||
|     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: [] | ||||
| #      - full_name: | ||||
| #        - "URI:https://ca.example.com/revocations.crl" | ||||
| #        crl_issuer: | ||||
| #          - "URI:https://ca.example.com/" | ||||
| #        reasons: | ||||
| #          - key_compromise | ||||
| #          - ca_compromise | ||||
| #          - cessation_of_operation | ||||
|     valid_days: 730 | ||||
| 
 | ||||
|   mode_key: '0640' | ||||
|   mode_cert: '0644' | ||||
| 
 | ||||
|   owner_key: 'root' | ||||
|   group_key: 'root' | ||||
|   owner_cert: 'root' | ||||
|   group_cert: 'root' | ||||
| 
 | ||||
|   extension_cert: 'crt' | ||||
|   extension_key: 'key' | ||||
|   extension_csr: 'csr' | ||||
| 
 | ||||
|   letsencrypt: | ||||
|     path: '/etc/letsencrypt' | ||||
|     service:  # apache, nginx | ||||
|     renew_timer: 'Mon *-*-* 01:00:00' | ||||
|     verbosity: 'v' | ||||
|     certs: {}  # see 'default_le_certbot_cert_config' | ||||
|     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' | ||||
|     valid_days: 7300 | ||||
|     key_size: 8192  # 1024, 2048, 4096, 8192 | ||||
|     key_type: 'RSA' | ||||
|     cipher: 'AES-256-CBC'  # see: 'openssl list -cipher-algorithms' | ||||
|     digest: 'sha512' | ||||
|     regenerate: 'partial_idempotence' | ||||
|     pwd: | ||||
| 
 | ||||
|     # certificate config | ||||
|     cn: 'CA Certificate' | ||||
|     org: | ||||
|     ou: | ||||
|     country: | ||||
|     state: | ||||
|     locality: | ||||
|     email: | ||||
| 
 | ||||
| 
 | ||||
| CERT_CONFIG: "{{ default_certs | combine(certs, recursive=true) }}" | ||||
| 
 | ||||
| default_le_certbot_cert_config: | ||||
|   domains: [] | ||||
|   state: 'present' | ||||
|   email: | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| from re import sub as regex_replace | ||||
| from re import match as regex_match | ||||
| 
 | ||||
| 
 | ||||
| class FilterModule(object): | ||||
|  | @ -6,16 +7,35 @@ class FilterModule(object): | |||
|     def filters(self): | ||||
|         return { | ||||
|             "safe_key": self.safe_key, | ||||
|             "fallback": self.fallback, | ||||
|             "valid_domain": self.valid_domain, | ||||
|             "valid_ip": self.valid_ip, | ||||
|             "check_email": self.check_email, | ||||
|         } | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def safe_key(key: str) -> str: | ||||
|         return regex_replace('[^0-9a-zA-Z]+', '', key.replace(' ', '_')) | ||||
|         return regex_replace(r'[^0-9a-zA-Z\.]+', '', key.replace(' ', '_')) | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def fallback(opt1: str, opt2: str) -> str: | ||||
|         if opt1 not in [None, '', 'None', 'none', ' ']: | ||||
|             return opt1 | ||||
|     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 | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def valid_ip(ip: str) -> bool: | ||||
|         expr_ipv4 = r'^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$' | ||||
|         expr_ipv6 = r'^(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}$' | ||||
| 
 | ||||
|         if regex_match(expr_ipv4, ip) is not None or regex_match(expr_ipv6, ip) is not None: | ||||
|             return True | ||||
| 
 | ||||
|         return False | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def check_email(certs: dict) -> bool: | ||||
|         for settings in certs.values(): | ||||
|             if 'email' not in settings or settings['email'] in ['', ' ', None, 'null', 'None']: | ||||
|                 return False | ||||
| 
 | ||||
|         return True | ||||
| 
 | ||||
|         return opt2 | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| --- | ||||
| 
 | ||||
| # ansible-playbook -K -D -i inventory/hosts.yml playbook.yml | ||||
| # ansible-playbook -K -D -i inventory/hosts.yml playbook.yml --ask-vault-pass | ||||
| 
 | ||||
| - hosts: all   # should be limited | ||||
| - hosts: localhost   # should be limited | ||||
|   become: true | ||||
|   gather_facts: yes | ||||
|   roles: | ||||
|   - ansibleguy.ROLE | ||||
|   - ansibleguy.infra_certs | ||||
|  |  | |||
|  | @ -2,5 +2,5 @@ | |||
| # install: ansible-galaxy install -r requirements.yml | ||||
| 
 | ||||
| collections: [] | ||||
| #  - name: 'community.general' | ||||
| #    source: 'https://galaxy.ansible.com' | ||||
|   - name: 'community.crypto' | ||||
|     source: 'https://galaxy.ansible.com' | ||||
|  |  | |||
|  | @ -0,0 +1,9 @@ | |||
| --- | ||||
| 
 | ||||
| # creating ca with full pki | ||||
| # to be continued (; | ||||
| 
 | ||||
| - name: Certificates | Debian | Internal | CA | Not yet implemented | ||||
|   ansible.builtin.debug: | ||||
|     msg: "The certificate mode 'ca_full' is not yet implemented!" | ||||
|   tags: ca | ||||
|  | @ -0,0 +1,29 @@ | |||
| --- | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Apache | Install package | ||||
|   ansible.builtin.package: | ||||
|     name: ['python3-certbot-apache'] | ||||
|     state: present | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Apache | Checking sites | ||||
|   ansible.builtin.shell: 'ls /etc/apache2/sites-enabled/' | ||||
|   changed_when: false | ||||
|   register: enabled_apache_sites | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Apache | Deploying temporary apache site | ||||
|   ansible.builtin.template: | ||||
|     src: 'templates/etc/apache2/sites-enabled/le_dummy.conf.j2' | ||||
|     dest: '/etc/apache2/sites-enabled/tmp_le_dummy.conf' | ||||
|     owner: 'root' | ||||
|     group: 'root' | ||||
|     mode: 0644 | ||||
|   register: tmp_site_enable | ||||
|   when: enabled_apache_sites.stdout == '' | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Apache | Reloading apache | ||||
|   ansible.builtin.systemd: | ||||
|     name: 'apache2.service' | ||||
|     state: reloaded | ||||
|   when: | ||||
|     - enabled_apache_sites.stdout == '' | ||||
|     - tmp_site_enable.changed | ||||
|  | @ -0,0 +1,13 @@ | |||
| --- | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Apache | Disable temporary site | ||||
|   ansible.builtin.file: | ||||
|     state: absent | ||||
|     path: '/etc/apache2/sites-enabled/tmp_le_dummy.conf' | ||||
|   register: tmp_site_disable | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Apache | Reloading apache | ||||
|   ansible.builtin.systemd: | ||||
|     name: 'apache2.service' | ||||
|     state: reloaded | ||||
|   when: tmp_site_disable.changed | ||||
|  | @ -0,0 +1,23 @@ | |||
| --- | ||||
| 
 | ||||
| - name: Apache | Debian | LetsEncrypt Certbot | Dependencies | Deploying temporary apache site | ||||
|   ansible.builtin.template: | ||||
|     src: 'templates/etc/apache2/sites-available/le_dummy.conf.j2' | ||||
|     dest: '/etc/apache2/sites-available/tmp_le_dummy.conf' | ||||
|     owner: 'root' | ||||
|     group: 'root' | ||||
|     mode: 0644 | ||||
| 
 | ||||
| - name: Apache | Debian | LetsEncrypt Certbot | Dependencies | Enable apache site | ||||
|   ansible.builtin.file: | ||||
|     state: link | ||||
|     src: '/etc/apache2/sites-available/tmp_le_dummy.conf' | ||||
|     dest: '/etc/apache2/sites-enabled/tmp_le_dummy.conf' | ||||
|     owner: 'root' | ||||
|     group: 'root' | ||||
|     mode: 0644 | ||||
| 
 | ||||
| - name: Apache | Debian | LetsEncrypt Certbot | Dependencies | Reload apache | ||||
|   ansible.builtin.systemd: | ||||
|     name: 'apache2.service' | ||||
|     state: reloaded | ||||
|  | @ -0,0 +1,58 @@ | |||
| --- | ||||
| 
 | ||||
| # todo: check domains registered in current certificate (certbot certificates) and remove it if there are more than configured before re-configuring it | ||||
| 
 | ||||
| - name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | Creating directory" | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ le_path }}" | ||||
|     state: directory | ||||
|     owner: 'root' | ||||
|     group: 'root' | ||||
|     mode: 0755 | ||||
| 
 | ||||
| - name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | Command to be executed" | ||||
|   ansible.builtin.debug: | ||||
|     msg: "certbot certonly --non-interactive --agree-tos --no-redirect  | ||||
|     --{{ 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 %} | ||||
|     {% if le_cert.email is not none %}--email {{ le_cert.email }} {% elif CERT_CONFIG.cert.email | default(none, true) is not none %}--email {{ CERT_CONFIG.cert.email }} {% endif %}" | ||||
|   when: existing_certs_raw.stdout.find(name) == -1 | ||||
| 
 | ||||
| - name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | Starting certbot" | ||||
|   ansible.builtin.command: "certbot certonly --non-interactive --agree-tos --no-redirect | ||||
|     --{{ 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 %} | ||||
|     {% if le_cert.email is not none %}--email {{ le_cert.email }} {% elif CERT_CONFIG.cert.email | default(none, true) is not none %}--email {{ CERT_CONFIG.cert.email }} {% endif %}" | ||||
|   when: existing_certs_raw.stdout.find(name) == -1 | ||||
| 
 | ||||
| - name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | Linking cert" | ||||
|   ansible.builtin.file: | ||||
|     state: link | ||||
|     src: "{{ item.src }}" | ||||
|     dest: "{{ item.dst }}" | ||||
|     mode: "{{ CERT_CONFIG.mode_cert }}" | ||||
|     owner: "{{ CERT_CONFIG.owner_cert }}" | ||||
|     group: "{{ CERT_CONFIG.group_cert }}" | ||||
|     follow: true | ||||
|     force: true | ||||
|   loop: | ||||
|     - {'dst': "{{ CERT_CONFIG.path }}/{{ le_name }}.{{ CERT_CONFIG.extension_cert }}", 'src': "{{ le_path }}/cert.pem"} | ||||
|     - {'dst': "{{ CERT_CONFIG.path }}/{{ le_name }}.chain.{{ CERT_CONFIG.extension_cert }}", 'src': "{{ le_path }}/chain.pem"} | ||||
|     - {'dst': "{{ CERT_CONFIG.path }}/{{ le_name }}.fullchain.{{ CERT_CONFIG.extension_cert }}", 'src': "{{ le_path }}/fullchain.pem"} | ||||
| 
 | ||||
| - name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | Linking key" | ||||
|   ansible.builtin.file: | ||||
|     state: link | ||||
|     src: "{{ le_path }}/privkey.pem" | ||||
|     dest: "{{ CERT_CONFIG.path }}/{{ le_name }}.{{ CERT_CONFIG.extension_key }}" | ||||
|     mode: "{{ CERT_CONFIG.mode_key }}" | ||||
|     owner: "{{ CERT_CONFIG.owner_key }}" | ||||
|     group: "{{ CERT_CONFIG.group_key }}" | ||||
|     follow: true | ||||
|     force: true | ||||
|  | @ -0,0 +1,86 @@ | |||
| --- | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Checking config | ||||
|   ansible.builtin.fail: | ||||
|     msg: "The required configuration was not provided! | ||||
|     Needed: 'certs.letsencrypt.certs', 'certs.letsencrypt.service', | ||||
|     'certs.letsencrypt.email or certs.letsencrypt.email.certs.email'" | ||||
|   when: > | ||||
|     CERT_CONFIG.letsencrypt.certs | length == 0 or | ||||
|     CERT_CONFIG.letsencrypt.service is none | default(none, true) or | ||||
|     (CERT_CONFIG.letsencrypt.email | default(none, true) is none and not CERT_CONFIG.letsencrypt.certs|check_email) | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Checking service | ||||
|   ansible.builtin.fail: | ||||
|     msg: "You need to supply a supported LetsEncrypt Certbot service to use! (apache/nginx)" | ||||
|   when: "CERT_CONFIG.letsencrypt.service | default(none, true) is none or CERT_CONFIG.letsencrypt.service not in ['apache', 'nginx']" | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Configure for Apache2 | ||||
|   ansible.builtin.import_tasks: apache.yml | ||||
|   when: CERT_CONFIG.letsencrypt.service == 'apache' | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Configure for Nginx | ||||
|   ansible.builtin.import_tasks: nginx.yml | ||||
|   when: CERT_CONFIG.letsencrypt.service == 'nginx' | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Pulling existing certs | ||||
|   ansible.builtin.shell: 'certbot certificates' | ||||
|   register: existing_certs_raw | ||||
|   changed_when: false | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Adding certificates | ||||
|   ansible.builtin.include_tasks: cert.yml | ||||
|   when: | ||||
|     - le_cert.domains | length > 0 | ||||
|     - le_cert.state == 'present' | ||||
|   vars: | ||||
|     le_cert: "{{ default_le_certbot_cert_config | combine(cert_item.value, recursive=true) }}" | ||||
|     le_name: "{{ cert_item.key | safe_key }}" | ||||
|     le_path: "{{ CERT_CONFIG.letsencrypt.path }}/live/{{ name }}" | ||||
|   loop_control: | ||||
|     loop_var: cert_item | ||||
|   with_dict: "{{ CERT_CONFIG.letsencrypt.certs }}" | ||||
| 
 | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Removing certificates | ||||
|   ansible.builtin.command: "certbot revoke --cert-name {{ le_name }} && certbot delete --cert-name {{ le_name }}" | ||||
|   when: | ||||
|     - le_cert.state != 'present' | ||||
|     - existing_certs_raw.stdout.find(le_name) != -1 | ||||
|   vars: | ||||
|     le_cert: "{{ default_le_certbot_cert_config | combine(cert_item.value, recursive=true) }}" | ||||
|     le_name: "{{ cert_item.key | safe_key }}" | ||||
|   loop_control: | ||||
|     loop_var: cert_item | ||||
|   with_dict: "{{ CERT_CONFIG.letsencrypt.certs }}" | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Cleanup for Apache2 | ||||
|   ansible.builtin.import_tasks: apache_cleanup.yml | ||||
|   when: CERT_CONFIG.letsencrypt.service == 'apache' | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Cleanup for Nginx | ||||
|   ansible.builtin.import_tasks: nginx_cleanup.yml | ||||
|   when: CERT_CONFIG.letsencrypt.service == 'nginx' | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Adding service for certbot renewal | ||||
|   ansible.builtin.template: | ||||
|     src: "templates/etc/systemd/system/{{ item }}.j2" | ||||
|     dest: "/etc/systemd/system/{{ item }}" | ||||
|     owner: 'root' | ||||
|     group: 'root' | ||||
|     mode: 0644 | ||||
|   with_items: | ||||
|     - 'ansibleguy.infra_certs.LetsEncryptCertbot.service' | ||||
|     - 'ansibleguy.infra_certs.LetsEncryptCertbot.timer' | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Enabling cert-renewal timer | ||||
|   ansible.builtin.systemd: | ||||
|     daemon_reload: yes | ||||
|     name: 'ansibleguy.infra_certs.LetsEncryptCertbot.timer' | ||||
|     enabled: yes | ||||
|     state: started | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Running renewal | ||||
|   ansible.builtin.command: 'certbot renew --force-renewal' | ||||
|   when: CERT_CONFIG.letsencrypt.renew | ||||
|   ignore_errors: true | ||||
|  | @ -0,0 +1,29 @@ | |||
| --- | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Nginx | Install package | ||||
|   ansible.builtin.package: | ||||
|     name: ['python3-certbot-nginx'] | ||||
|     state: present | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Nginx | Checking sites | ||||
|   ansible.builtin.shell: 'ls /etc/nginx/sites-enabled/' | ||||
|   changed_when: false | ||||
|   register: enabled_nginx_sites | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Nginx | Deploying temporary apache site | ||||
|   ansible.builtin.template: | ||||
|     src: 'templates/etc/nginx/sites-enabled/le_dummy.j2' | ||||
|     dest: '/etc/nginx/sites-enabled/tmp_le_dummy' | ||||
|     owner: 'root' | ||||
|     group: 'root' | ||||
|     mode: 0644 | ||||
|   register: tmp_site_enable | ||||
|   when: enabled_nginx_sites.stdout == '' | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Nginx | Reloading apache | ||||
|   ansible.builtin.systemd: | ||||
|     name: 'nginx.service' | ||||
|     state: reloaded | ||||
|   when: | ||||
|     - enabled_nginx_sites.stdout == '' | ||||
|     - tmp_site_enable.changed | ||||
|  | @ -0,0 +1,13 @@ | |||
| --- | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Nginx | Disable temporary site | ||||
|   ansible.builtin.file: | ||||
|     state: absent | ||||
|     path: '/etc/nginx/sites-enabled/tmp_le_dummy' | ||||
|   register: tmp_site_disable | ||||
| 
 | ||||
| - name: Certificates | Debian | LetsEncrypt Certbot | Nginx | Reloading apache | ||||
|   ansible.builtin.systemd: | ||||
|     name: 'nginx.service' | ||||
|     state: reloaded | ||||
|   when: tmp_site_disable.changed | ||||
|  | @ -1,6 +0,0 @@ | |||
| --- | ||||
| 
 | ||||
| - name: ROLE | Debian | Task | ||||
|   ansible.builtin.apt: | ||||
|     pkg: "{{ something }}" | ||||
|   tags: [base] | ||||
|  | @ -0,0 +1,106 @@ | |||
| --- | ||||
| 
 | ||||
| # creating a minimal ca | ||||
| 
 | ||||
| - name: Certificates | Internal | Minimal CA | Creating ca directory | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ CERT_CONFIG.ca.path }}" | ||||
|     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 }}" | ||||
|   no_log: true | ||||
|   when: CERT_CONFIG.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 }}" | ||||
|   no_log: true | ||||
|   when: CERT_CONFIG.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 }}" | ||||
|     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 }}" | ||||
|   no_log: true | ||||
|   when: CERT_CONFIG.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 }}" | ||||
|     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 }}" | ||||
|   no_log: true | ||||
|   when: CERT_CONFIG.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 }}" | ||||
|     provider: selfsigned | ||||
|     valid_in: "{{ CERT_CONFIG.ca.valid_days }}d" | ||||
|     mode: "{{ CERT_CONFIG.mode_cert }}" | ||||
|     owner: "{{ CERT_CONFIG.owner_cert }}" | ||||
|     group: "{{ CERT_CONFIG.group_cert }}" | ||||
|   no_log: true | ||||
|   when: CERT_CONFIG.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 }}" | ||||
|     provider: selfsigned | ||||
|     valid_in: "{{ CERT_CONFIG.ca.valid_days }}d" | ||||
|     mode: "{{ CERT_CONFIG.mode_cert }}" | ||||
|     owner: "{{ CERT_CONFIG.owner_cert }}" | ||||
|     group: "{{ CERT_CONFIG.group_cert }}" | ||||
|   no_log: true | ||||
|   when: CERT_CONFIG.ca.pwd | default(none, true) is none | ||||
|  | @ -0,0 +1,202 @@ | |||
| --- | ||||
| 
 | ||||
| - 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 }}" | ||||
|   no_log: true | ||||
|   when: CERT_CONFIG.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 }}" | ||||
|   no_log: true | ||||
|   when: CERT_CONFIG.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 %} | ||||
|     {% endfor %} | ||||
|     {% for ip in CERT_CONFIG.cert.ips %} | ||||
|     {% if ip | valid_ip %},IP:{{ ip }}{% endif %} | ||||
|     {% endfor %}" | ||||
| 
 | ||||
| - 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 }}" | ||||
|   no_log: true | ||||
|   when: CERT_CONFIG.cert.pwd | default(none, true) is not none | ||||
| 
 | ||||
| - 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 }}" | ||||
|   no_log: true | ||||
|   when: CERT_CONFIG.cert.pwd | default(none, true) is none | ||||
| 
 | ||||
| - 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 }}" | ||||
|     provider: selfsigned | ||||
|     valid_in: "{{ CERT_CONFIG.cert.valid_days }}d" | ||||
|     mode: "{{ CERT_CONFIG.mode_cert }}" | ||||
|     owner: "{{ CERT_CONFIG.owner_cert }}" | ||||
|     group: "{{ CERT_CONFIG.group_cert }}" | ||||
|   no_log: true | ||||
|   when: | ||||
|     - CERT_CONFIG.cert.pwd | default(none, true) is not none | ||||
|     - CERT_CONFIG.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 }}" | ||||
|     provider: selfsigned | ||||
|     valid_in: "{{ CERT_CONFIG.cert.valid_days }}d" | ||||
|     mode: "{{ CERT_CONFIG.mode_cert }}" | ||||
|     owner: "{{ CERT_CONFIG.owner_cert }}" | ||||
|     group: "{{ CERT_CONFIG.group_cert }}" | ||||
|   no_log: true | ||||
|   when: | ||||
|     - CERT_CONFIG.cert.pwd | default(none, true) is none | ||||
|     - CERT_CONFIG.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 }}" | ||||
|     valid_in: "{{ CERT_CONFIG.cert.valid_days }}d" | ||||
|     provider: ownca | ||||
|     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 }}" | ||||
|   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' | ||||
| 
 | ||||
| - 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 }}" | ||||
|     valid_in: "{{ CERT_CONFIG.cert.valid_days }}d" | ||||
|     provider: ownca | ||||
|     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 }}" | ||||
|   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' | ||||
| 
 | ||||
| - 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 }}" | ||||
|     valid_in: "{{ CERT_CONFIG.cert.valid_days }}d" | ||||
|     provider: ownca | ||||
|     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 }}" | ||||
|   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' | ||||
| 
 | ||||
| - 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 }}" | ||||
|     valid_in: "{{ CERT_CONFIG.cert.valid_days }}d" | ||||
|     provider: ownca | ||||
|     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 }}" | ||||
|   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' | ||||
| 
 | ||||
| - 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 }}" | ||||
|   args: | ||||
|     creates: "{{ CERT_CONFIG.path }}/{{ name }}.chain.{{ CERT_CONFIG.extension_cert }}" | ||||
|   when: CERT_CONFIG.mode == 'ca' | ||||
| 
 | ||||
| - 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' | ||||
|  | @ -0,0 +1,22 @@ | |||
| --- | ||||
| 
 | ||||
| - name: Certificates | Internal | Installing dependencies | ||||
|   ansible.builtin.package: | ||||
|     pkg: ['python3-cryptography'] | ||||
|   tags: [certs, ca] | ||||
| 
 | ||||
| - name: Certificates | Internal | Creating cert directory | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ CERT_CONFIG.path }}" | ||||
|     state: directory | ||||
|   tags: [certs, ca] | ||||
| 
 | ||||
| - name: Certificates | Internal | Minimal CA | ||||
|   ansible.builtin.import_tasks: ca_minimal.yml | ||||
|   when: CERT_CONFIG.mode == 'ca' | ||||
|   tags: [ca] | ||||
| 
 | ||||
| - name: Certificates | Internal | Cert | ||||
|   ansible.builtin.import_tasks: cert.yml | ||||
|   when: "CERT_CONFIG.mode in ['ca', 'selfsigned']" | ||||
|   tags: [certs] | ||||
|  | @ -1,5 +1,25 @@ | |||
| --- | ||||
| 
 | ||||
| - name: ROLE | Processing debian config | ||||
|   ansible.builtin.import_tasks: debian/main.yml | ||||
|   when: "ansible_distribution|lower in ['debian', 'ubuntu']" | ||||
| - name: Certificates | Checking config | ||||
|   ansible.builtin.fail: | ||||
|     msg: "The required configuration was not provided! | ||||
|     Needed: 'certs'" | ||||
|   when: certs is undefined | ||||
| 
 | ||||
| - name: Certificates | Setting name | ||||
|   ansible.builtin.set_fact: | ||||
|     name: "{% if CERT_CONFIG.cert.name is not none %}{{ CERT_CONFIG.cert.name | safe_key }}{% else %}{{ CERT_CONFIG.cert.cn | safe_key }}{% endif %}" | ||||
| 
 | ||||
| - name: Certificates | Internal signed | ||||
|   ansible.builtin.include_tasks: internal/main.yml | ||||
|   when: "CERT_CONFIG.mode in ['ca_full', 'ca', 'selfsigned']" | ||||
| 
 | ||||
| - name: Certificates | Internal | CA | ||||
|   ansible.builtin.include_tasks: debian/ca_full.yml | ||||
|   when: CERT_CONFIG.mode == 'ca_full' | ||||
| 
 | ||||
| - name: Certificates | Debian | Letsencrypt | ||||
|   ansible.builtin.include_tasks: debian/letsencrypt/main.yml | ||||
|   when: | ||||
|     - CERT_CONFIG.mode == 'le_certbot' | ||||
|     - "ansible_distribution|lower in ['debian', 'ubuntu']" | ||||
|  |  | |||
|  | @ -0,0 +1,9 @@ | |||
| # {{ ansible_managed }} | ||||
| # ansibleguy.infra_certs - dummy site used for letsencrypt certbot | ||||
| 
 | ||||
| <VirtualHost *:80> | ||||
|   ServerName dummy.letsencrypt.localhost | ||||
|   ServerAdmin webmaster@localhost | ||||
|   ErrorLog {{ APACHE_CONFIG.log.path }}/error.log | ||||
|   CustomLog {{ APACHE_CONFIG.log.path }}/access.log combined | ||||
| </VirtualHost> | ||||
|  | @ -0,0 +1,9 @@ | |||
| # {{ ansible_managed }} | ||||
| # ansibleguy.infra_certs - dummy site used for letsencrypt certbot | ||||
| 
 | ||||
| server { | ||||
|   listen 80; | ||||
|   server_name dummy.letsencrypt.localhost; | ||||
|   error_log {{ NGINX_CONFIG.log.path }}/error.log; | ||||
|   access_log {{ NGINX_CONFIG.log.path }}/access.log; | ||||
| } | ||||
|  | @ -0,0 +1,10 @@ | |||
| # {{ ansible_managed }} | ||||
| # ansibleguy.infra_certs | ||||
| 
 | ||||
| [Unit] | ||||
| Description=Service to renew LetsEncrypt Certificates using certbot | ||||
| 
 | ||||
| [Service] | ||||
| Type=oneshot | ||||
| ExecStart=certbot renew -{{ CERT_CONFIG.letsencrypt.verbosity }} --non-interactive --agree-tos --renew-with-new-domains | ||||
| SuccessExitStatus=0 | ||||
|  | @ -0,0 +1,13 @@ | |||
| # {{ ansible_managed }} | ||||
| # ansibleguy.infra_certs | ||||
| 
 | ||||
| [Unit] | ||||
| Description=Timer to renew LetsEncrypt Certificates using certbot | ||||
| 
 | ||||
| [Timer] | ||||
| OnCalendar={{ CERT_CONFIG.letsencrypt.renew_timer }} | ||||
| Persistent=false | ||||
| WakeSystem=false | ||||
| 
 | ||||
| [Install] | ||||
| WantedBy=multi-user.target | ||||
		Loading…
	
		Reference in New Issue