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:** | **Tested:** | ||||||
| * Debian 11 | * Debian 11 | ||||||
| 
 | 
 | ||||||
| ## Functionality | ## Functionality | ||||||
| 
 | 
 | ||||||
| * Package installation | * **Package installation** | ||||||
|   * Ansible dependencies (_minimal_) |   * Ansible dependencies (_minimal_) | ||||||
|   *  |   * Crypto Dependencies | ||||||
| * Configuration | 
 | ||||||
|   * Two Possible Modes | 
 | ||||||
|     * Generate Self-Signed certificate | * **Configuration** | ||||||
|     * Create an internal-ca and generate certificates using it |   * **Four Possible Modes**: | ||||||
|   * Default config: |     * 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 |     * Mode => Self-Signed | ||||||
|   * Default opt-ins: |  | ||||||
|     *  |  | ||||||
|   * Default opt-outs: |  | ||||||
|     *  |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ## Info | ## Info | ||||||
|  | @ -28,6 +33,10 @@ | ||||||
| * **Note:** Most of this functionality can be opted in or out using the main defaults file and variables! | * **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 | ## Requirements | ||||||
| 
 | 
 | ||||||
|  | @ -36,20 +45,89 @@ | ||||||
| 
 | 
 | ||||||
| ## Usage | ## 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 | ```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: | Run the playbook: | ||||||
| ```bash | ```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: | There are also some useful **tags** available: | ||||||
| * base => only configure basics; sites will not be touched | * certs => ignore ca tasks; only generate certs | ||||||
| * sites | * selfsigned | ||||||
| * config | * config | ||||||
| * certs | * certs | ||||||
|  |  | ||||||
|  | @ -1,8 +1,85 @@ | ||||||
| --- | --- | ||||||
| 
 | 
 | ||||||
| # default config => is overwritten by provided config | # 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 sub as regex_replace | ||||||
|  | from re import match as regex_match | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class FilterModule(object): | class FilterModule(object): | ||||||
|  | @ -6,16 +7,35 @@ class FilterModule(object): | ||||||
|     def filters(self): |     def filters(self): | ||||||
|         return { |         return { | ||||||
|             "safe_key": self.safe_key, |             "safe_key": self.safe_key, | ||||||
|             "fallback": self.fallback, |             "valid_domain": self.valid_domain, | ||||||
|  |             "valid_ip": self.valid_ip, | ||||||
|  |             "check_email": self.check_email, | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def safe_key(key: str) -> str: |     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 |     @staticmethod | ||||||
|     def fallback(opt1: str, opt2: str) -> str: |     def valid_domain(domain: str) -> bool: | ||||||
|         if opt1 not in [None, '', 'None', 'none', ' ']: |         expr = r'^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$' | ||||||
|             return opt1 |         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 |   become: true | ||||||
|   gather_facts: yes |   gather_facts: yes | ||||||
|   roles: |   roles: | ||||||
|   - ansibleguy.ROLE |   - ansibleguy.infra_certs | ||||||
|  |  | ||||||
|  | @ -2,5 +2,5 @@ | ||||||
| # install: ansible-galaxy install -r requirements.yml | # install: ansible-galaxy install -r requirements.yml | ||||||
| 
 | 
 | ||||||
| collections: [] | collections: [] | ||||||
| #  - name: 'community.general' |   - name: 'community.crypto' | ||||||
| #    source: 'https://galaxy.ansible.com' |     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 | - name: Certificates | Checking config | ||||||
|   ansible.builtin.import_tasks: debian/main.yml |   ansible.builtin.fail: | ||||||
|   when: "ansible_distribution|lower in ['debian', 'ubuntu']" |     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