diff --git a/all.yml b/all.yml index 1dcfc31..7fa4fa8 100644 --- a/all.yml +++ b/all.yml @@ -5,34 +5,35 @@ tasks: # TODO: multiple roles - - name: get primary role - set_fact: - host_primary_role: "{%- if primary_role is defined -%}{{ primary_role }}\ - {%- elif hostvars[inventory_hostname]['primary_role'] is defined -%}{{ hostvars[inventory_hostname]['primary_role'] }}\ - {%- else -%}{{ inventory_hostname }}\ - {%- endif -%}" - + - block: + - name: get primary role + set_fact: + host_primary_role: "{%- if primary_role is defined -%}{{ primary_role }}\ + {%- elif hostvars[inventory_hostname]['primary_role'] is defined -%}{{ hostvars[inventory_hostname]['primary_role'] }}\ + {%- else -%}{{ inventory_hostname }}\ + {%- endif -%}" - - name: check if role info file exists - stat: - path: "{{ (playbook_dir, 'roles', host_primary_role, 'tasks', 'info.yml') | path_join }}" - delegate_to: localhost - register: result - no_log: yes + - name: check if role info file exists + stat: + path: "{{ (playbook_dir, 'roles', host_primary_role, 'tasks', 'info.yml') | path_join }}" + delegate_to: localhost + register: result + no_log: yes + - name: get role info + include_role: + name: "{{ host_primary_role }}" + tasks_from: info.yml + apply: + no_log: yes + when: result.stat.exists - - name: get role info - include_role: - name: "{{ host_primary_role }}" - tasks_from: info.yml - apply: - no_log: yes - when: result.stat.exists + when: no_primary_role | d(false) == false - name: build role dependency list and stage info set_fact: - role_deps: "{{ (([{ 'stage': 6, 'role': host_primary_role }] if role_dependency is not defined else [role_dependency]) + + role_deps: "{{ (([] if no_primary_role | d(false) == true else ([{ 'stage': 6, 'role': host_primary_role }] if role_dependency is not defined else [role_dependency])) + ([role_dependency_common] if (role_dependency_no_common | d(false) == false) else []) + ([{ 'stage': 1, 'role': 'container' }] if 'containers' in group_names else []) + ([{ 'stage': 3, 'role': 'postgres', 'tasks_from': 'integrate.yml' }] if role_use_database | d(false) == true else []) @@ -46,7 +47,7 @@ - name: show deployment info debug: - msg: "deploying role \"{{ host_primary_role }}\" on host \"{{ inventory_hostname }}\", {{ + msg: "deploying role {{ host_primary_role | d('(no role)') }}\" on host \"{{ inventory_hostname }}\", {{ (('stages ' if (selected_stages | length > 1) else 'stage ') ~ (selected_stages | join(', '))) if ([1,2,3,4,5,6,7,8,9] | symmetric_difference(selected_stages) | list | length > 0) else 'all stages' }}\n\n{{ 'dependencies:\n' ~ (role_deps | map(attribute='stage') | list | zip( diff --git a/group_vars/all.yml b/group_vars/all.yml index d9bf205..68c0b48 100644 --- a/group_vars/all.yml +++ b/group_vars/all.yml @@ -4,14 +4,6 @@ ansible_key_dir: keys mac_prefix: 02:FF -default_container_hardware: - cores: 1 - cpus: 1 - cpuunits: 1024 - memory: 128 - swap: 128 - disk: 0.4 - known_external_ca: - url: letsencrypt.org wildcard: no @@ -40,7 +32,7 @@ role_dependency_common: - {stage: 5, role: 'mail-user'} - {stage: 8, role: 'rproxy'} - {stage: 8, role: 'iptables'} - - {stage: 9, role: 'backup', function: 'setup'} + - {stage: 9, role: 'backup'} reverse_proxy_type: caddy @@ -51,3 +43,4 @@ acme_default_config: resolver: 1.1.1.1 renew_at_days: 45 preferred_chain: 'ISRG Root X1' + type: ec384 \ No newline at end of file diff --git a/roles/acme-dns/defaults/main.yml b/roles/acme-dns/defaults/main.yml index ed9beab..49096a3 100644 --- a/roles/acme-dns/defaults/main.yml +++ b/roles/acme-dns/defaults/main.yml @@ -5,7 +5,7 @@ acme_dns_dir: /opt/acmedns acme_dns_tld: "acme-dns.{{ acme_tld | d(tld) }}" acme_dns_ns: "ns.acme-dns.{{ acme_tld | d(tld) }}" acme_dns_admin: "{{ maintainer_email | d('admin@' ~ (acme_tld | d(tld))) }}" - +acme_dns_external_ipv4: "{{ hostvars[selected_node]['external_ipv4'] }}" acme_dns_api_port: 8080 acme_dns_default_config: @@ -31,12 +31,12 @@ acme_dns_default_config: port: "{{ acme_dns_api_port }}" disable_registration: no tls: none - use_header: no - - notification_email: "{{ maintainer_email }}" + use_header: yes + header_name: X-Forwarded-For corsorigins: - "*" + logconfig: loglevel: debug logtype: stdout diff --git a/roles/acme-dns/tasks/info.yml b/roles/acme-dns/tasks/info.yml index 07af2ac..62f9391 100644 --- a/roles/acme-dns/tasks/info.yml +++ b/roles/acme-dns/tasks/info.yml @@ -6,7 +6,13 @@ cores: 2 memory: 128 swap: 64 - disk: 0.3 + disk: 0.6 role_use_reverse_proxy: yes role_use_database: yes + + role_firewall_config: + filter: + input: + - { protocol: tcp, dst_port: [53], action: accept } + - { protocol: udp, dst_port: [53], action: accept } diff --git a/roles/acme-dns/tasks/main.yml b/roles/acme-dns/tasks/main.yml index 2b193b4..654d83e 100644 --- a/roles/acme-dns/tasks/main.yml +++ b/roles/acme-dns/tasks/main.yml @@ -93,8 +93,8 @@ port: "{{ acme_dns_api_port }}" acme: server: "http://127.0.0.1:{{ acme_dns_api_port }}" - nginx: rproxy_nginx.j2 - caddy_reverse_proxy_handlers: + nginx_rproxy: rproxy_nginx.j2 + caddy_rproxy: - handler: reverse_proxy upstreams: - dial: "127.0.0.1:{{ acme_dns_api_port }}" diff --git a/roles/acme-dns/templates/init.j2 b/roles/acme-dns/templates/init.j2 index a8e94ba..8d3fc14 100644 --- a/roles/acme-dns/templates/init.j2 +++ b/roles/acme-dns/templates/init.j2 @@ -1,7 +1,7 @@ #!/sbin/openrc-run name="$SVCNAME" -command="{{ (acme_dns_dir, '$SVCNAME') | path_join }}" +command="{{ (acme_dns_dir, 'acme-dns') | path_join }}" directory="{{ acme_dns_dir }}" command_user="{{ acme_dns_user ~ ':' ~ acme_dns_group }}" pidfile="/var/run/$SVCNAME.pid" @@ -11,8 +11,10 @@ start_stop_daemon_args="--stdout-logger logger --stderr-logger logger" depend() { need net use dns + before nginx caddy + after mariadb postgresql } start_pre() { - setcap 'cap_net_bind_service=+ep' {{ (acme_dns_dir, '$SVCNAME') | path_join | quote }} + setcap 'cap_net_bind_service=+ep' {{ (acme_dns_dir, 'acme-dns') | path_join | quote }} } diff --git a/roles/acme/templates/renewal.j2 b/roles/acme/templates/renewal.j2 index 19414e3..4e85119 100644 --- a/roles/acme/templates/renewal.j2 +++ b/roles/acme/templates/renewal.j2 @@ -9,7 +9,7 @@ {% if (acme_cert is string) and (acme_cert | length > 0) and (acme_use_symlinks | d(true) == false) -%} - cp -fpT {{ (acme_directory ~ '/live/' ~ acme_cert_name ~ '/fullchain.pem') | quote }} {{ acme_cert | quote }} + cp -fpT {{ (lego_cert_dir ~ '/live/' ~ acme_cert_name ~ '/fullchain.pem') | quote }} {{ acme_cert | quote }} {% if (acme_owner is not string) and (acme_group is string) -%} chgrp -f {{ acme_group }} {{ acme_cert | quote }} {% elif acme_owner is defined -%} diff --git a/roles/backup/tasks/main.yml b/roles/backup/tasks/main.yml index 40e2af7..af59e0b 100644 --- a/roles/backup/tasks/main.yml +++ b/roles/backup/tasks/main.yml @@ -1,7 +1,3 @@ -- fail: - when: function is defined - - - name: notify that backups are not supported debug: msg: backup host is missing, will not set up backups diff --git a/roles/caddy/defaults/main.yml b/roles/caddy/defaults/main.yml index ff84a36..b9d5c23 100644 --- a/roles/caddy/defaults/main.yml +++ b/roles/caddy/defaults/main.yml @@ -9,6 +9,11 @@ caddy_xcaddy_dir: /opt/xcaddy caddy_conf_file: "{{ (caddy_conf_dir, 'caddy.json') | path_join }}" +caddy_ecc384_cert: "{{ (caddy_cert_dir, 'ecc384.crt') | path_join }}" +caddy_ecc384_key: "{{ (caddy_cert_dir, 'ecc384.key') | path_join }}" +caddy_rsa2048_cert: "{{ (caddy_cert_dir, 'rsa2048.crt') | path_join }}" +caddy_rsa2048_key: "{{ (caddy_cert_dir, 'rsa2048.key') | path_join }}" + caddy_default_plugins: [] caddy_domains: - "{{ host_fqdn }}" diff --git a/roles/caddy/tasks/main.yml b/roles/caddy/tasks/main.yml index d330f8e..f32c820 100644 --- a/roles/caddy/tasks/main.yml +++ b/roles/caddy/tasks/main.yml @@ -6,6 +6,7 @@ - name: import vars for unmanaged tls include_vars: file: tls.yml + when: host_tls - name: import reverse proxy vars @@ -60,18 +61,6 @@ when: not caddy_custom_build -- name: template caddy config - template: - src: caddy.j2 - dest: "{{ caddy_conf_file }}" - force: yes - owner: "{{ caddy_user }}" - group: "{{ caddy_group }}" - mode: 0400 - validate: "{{ (caddy_bin_dir, 'caddy') | path_join }} validate --config %s" - notify: restart caddy - - - name: template systemd file template: src: systemd.j2 @@ -103,17 +92,34 @@ notify: restart caddy -- name: deploy certificates - include_role: - name: certs - vars: - certs: - cert: "{{ (caddy_cert_dir, 'ecc384.crt') | path_join }}" - key: "{{ (caddy_cert_dir, 'ecc384.key') | path_join }}" - ecc: yes - post_hook: service caddy restart - owner: "{{ caddy_user }}" - group: "{{ caddy_group }}" +- block: + - name: deploy certificates through lego + include_role: + name: lego + vars: + acme: + domains: "{{ caddy_domains }}" + cert: "{{ caddy_ecc384_cert }}" + key: "{{ caddy_ecc384_key }}" + owner: "{{ caddy_user }}" + group: "{{ caddy_group }}" + run_after_renew: service caddy restart + notify: restart caddy + acme2: "{{ caddy_acme_config | d({}) }}" + + when: host_tls + + +- name: template caddy config + template: + src: caddy.j2 + dest: "{{ caddy_conf_file }}" + force: yes + owner: "{{ caddy_user }}" + group: "{{ caddy_group }}" + mode: 0400 + validate: "{{ (caddy_bin_dir, 'caddy') | path_join }} validate --config %s" + notify: restart caddy - name: flush handlers diff --git a/roles/caddy/vars/reverse_proxy.yml b/roles/caddy/vars/reverse_proxy.yml index 7cae61c..d29f8d4 100644 --- a/roles/caddy/vars/reverse_proxy.yml +++ b/roles/caddy/vars/reverse_proxy.yml @@ -7,9 +7,8 @@ caddy_reverse_proxy_config: http: servers: rproxy: - listen: - - "tcp4/:443" - - "tcp6/:443" + listen: "{{ ['tcp4/:443', 'tcp6/:443'] if host_tls else ['tcp4/:80', 'tcp6/:80'] }}" + listener_wrappers: "{{ [{'wrapper': 'http_redirect'}, {'wrapper': 'tls'}] if host_tls else [] }}" automatic_https: disable: yes tls_connection_policies: @@ -18,8 +17,5 @@ caddy_reverse_proxy_config: - "{{ host_fqdn }}" default_sni: "{{ host_fqdn }}" routes: - - match: - - host: - - "{{ host_fqdn }}" - handle: "{{ (caddy_reverse_proxy_handlers | d([])) + caddy_reverse_proxy_default_handler }}" + - handle: "{{ (caddy_reverse_proxy_handlers | d([])) + caddy_reverse_proxy_default_handler }}" terminal: true diff --git a/roles/caddy/vars/tls.yml b/roles/caddy/vars/tls.yml index 0fc1cb9..077ff5a 100644 --- a/roles/caddy/vars/tls.yml +++ b/roles/caddy/vars/tls.yml @@ -3,6 +3,6 @@ caddy_tls_config: tls: certificates: load_files: - - certificate: "{{ (caddy_cert_dir, 'certificates', caddy_domains[0] ~ '.crt') | path_join }}" - key: "{{ (caddy_cert_dir, 'certificates', caddy_domains[0] ~ '.key') | path_join }}" + - certificate: "{{ caddy_ecc384_cert }}" + key: "{{ caddy_ecc384_key }}" tags: "{{ caddy_domains }}" diff --git a/roles/container/defaults/main.yml b/roles/container/defaults/main.yml index 702c3e2..a7d8d45 100644 --- a/roles/container/defaults/main.yml +++ b/roles/container/defaults/main.yml @@ -4,4 +4,4 @@ container_pool: production container_distro: alpine container_template: alpine: alpine-3.17-default_20221129_amd64.tar.xz - debian: debian-11-standard_11.3-1_amd64.tar.zst + debian: debian-11-standard_11.6-1_amd64.tar.zst diff --git a/roles/container/tasks/main.yml b/roles/container/tasks/main.yml index c1f027b..ffecb1f 100644 --- a/roles/container/tasks/main.yml +++ b/roles/container/tasks/main.yml @@ -4,7 +4,7 @@ pm_api_user: "{{ hostvars[selected_node]['api_user'] | d('root@pam') }}" pm_api_password: "{{ hostvars[selected_node]['api_password'] | d(hostvars[selected_node]['host_password'] | d(hostvars[selected_node]['ansible_password'])) }}" - pm_lxc_storage: "{{ hostvars[selected_node]['lxc_storage'] | d('local-zfs') }}" + pm_lxc_storage: "{{ container_storage | d(hostvars[selected_node]['lxc_storage'] | d('local-zfs')) }}" no_log: yes diff --git a/roles/iptables/defaults/main.yml b/roles/iptables/defaults/main.yml index 123db06..eb86f0e 100644 --- a/roles/iptables/defaults/main.yml +++ b/roles/iptables/defaults/main.yml @@ -1,5 +1,5 @@ iptables_dir: /etc/iptables -iptables_file: "{{ (iptables_dir, 'rules-save') | path_join }}" +iptables_file: "{{ (iptables_dir, 'rules-save' if ansible_distribution == 'Alpine' else 'rules.v4') | path_join }}" iptables_mappings: state: { module: 'state', param: 'state', upper: yes, join: ',' } diff --git a/roles/iptables/tasks/main.yml b/roles/iptables/tasks/main.yml index 232d108..addfc7f 100644 --- a/roles/iptables/tasks/main.yml +++ b/roles/iptables/tasks/main.yml @@ -22,6 +22,7 @@ path: /etc/conf.d/iptables regexp: "^IPTABLES_SAVE=" line: "IPTABLES_SAVE=\"{{ iptables_file }}\"" + when: ansible_distribution == 'Alpine' - name: template iptables schema @@ -47,5 +48,14 @@ name: iptables enabled: yes state: started + when: ansible_distribution == 'Alpine' + + + - name: start and enable netfilter-persistent + service: + name: netfilter-persistent + enabled: yes + state: started + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' when: firewall is mapping or role_firewall_config is mapping or (host_firewall | d(false) == true) \ No newline at end of file diff --git a/roles/lego/defaults/main.yml b/roles/lego/defaults/main.yml index ced1a4f..a188481 100644 --- a/roles/lego/defaults/main.yml +++ b/roles/lego/defaults/main.yml @@ -1,11 +1,3 @@ lego_dir: /opt/lego -lego_lastrun_file: "{{ (lego_dir, 'lastrun') | path_join }}" - -lego_user: root -lego_group: root - -# hardcoded in acmedns_client and cannot be changed -lego_acmedns_client_dir: /etc/acmedns -lego_acmedns_client_file: /etc/acmedns/clientstorage.json -lego_acmedns_client_bin_dir: /opt/acme-client - +lego_cert_dir: /etc/lego +lego_accounts_file: "{{ (lego_cert_dir, 'accounts.conf') | path_join }}" diff --git a/roles/lego/tasks/acme-client.yml b/roles/lego/tasks/acme-client.yml deleted file mode 100644 index 46453ec..0000000 --- a/roles/lego/tasks/acme-client.yml +++ /dev/null @@ -1,88 +0,0 @@ -- name: determine host architecture - include_tasks: tasks/get_host_arch.yml - - -- name: create acme-client directory - file: - path: "{{ item }}" - state: directory - mode: 0700 - owner: "{{ caddy_user }}" - group: "{{ caddy_group }}" - loop: - - "{{ caddy_acmedns_client_bin_dir }}" - - "{{ caddy_acmedns_client_dir }}" - - -- name: get and extract latest acme-dns-client version - include_tasks: tasks/get_lastversion.yml - vars: - package: - name: acme-dns/acme-dns-client - location: github - assets: yes - asset_filter: "{{ 'linux_' ~ host_architecture ~ '.tar.gz$' }}" - file: "{{ (caddy_acmedns_client_bin_dir, 'last_acme_client_version') | path_join }}" - extract: "{{ caddy_acmedns_client_bin_dir }}" - user: "{{ caddy_user }}" - group: "{{ caddy_group }}" - - -- name: ensure acme-dns-client binary has executable bit set - file: - path: "{{ (caddy_acmedns_client_bin_dir, 'acme-dns-client') | path_join }}" - mode: "+x" - - -- block: - - name: remove unnecessary files - file: - path: "{{ (caddy_acmedns_client_bin_dir, item) | path_join }}" - state: absent - loop: - - LICENSE - - README.md - rescue: - - meta: noop - - -- name: clear acme-dns-client domain fact - set_fact: - acmedns_current_domains: "{{ [] }}" - - -- name: check if acme-dns-client config exists - stat: - path: "{{ caddy_acmedns_client_file }}" - get_checksum: no - get_attributes: no - get_mime: no - register: result - - -- block: - - name: get acme-dns-client config file - slurp: - path: "{{ caddy_acmedns_client_file }}" - register: file_content - - - name: set acme-dns-client domain fact - set_fact: - acmedns_current_domains: "{{ file_content.content | b64decode | from_json | dict2items | map(attribute='key') | list }}" - - when: result.stat.exists - no_log: yes - - -- name: show domain information - debug: - msg: | - acme-dns-client currently manages these FQDNs: {{ '(none)' if acmedns_current_domains | length == 0 else acmedns_current_domains | join(', ') }} - acme-dns-client does not yet manage these FQDNs: {{ caddy_domains | difference(acmedns_current_domains) | join(', ') }} - - -- name: register a record with acme-dns-client for each unmanaged domain - include_tasks: register_acme_domain.yml - vars: - domain: "{{ item }}" - loop: "{{ caddy_domains | difference(acmedns_current_domains) }}" diff --git a/roles/lego/tasks/main.yml b/roles/lego/tasks/main.yml index 9f05ec5..4f9f280 100644 --- a/roles/lego/tasks/main.yml +++ b/roles/lego/tasks/main.yml @@ -1,27 +1,29 @@ +- name: fail if acme is not defined + fail: + msg: acme must be a mapping + when: acme is not mapping + + - name: set acme_cfg set_fact: acme_cfg: "{{ acme_default_config | d({}) | - combine(acme_config | d({}), recursive=true) }}" + combine(acme_config | d({}), recursive=true) | + combine(acme2 | d({}), recursive=true) | + combine(acme, recursive=true) }}" - name: determine host architecture include_tasks: tasks/get_host_arch.yml -- name: create user and group - include_tasks: tasks/create_user.yml - vars: - user: - name: "{{ lego_user }}" - group: "{{ lego_group }}" - create_home: no - - -- name: create lego working dir +- name: create lego dirs file: - path: "{{ lego_dir }}" + path: "{{ item }}" state: directory mode: 0700 + loop: + - "{{ lego_dir }}" + - "{{ lego_cert_dir }}" - name: get and extract latest lego version @@ -32,7 +34,7 @@ location: github assets: yes asset_filter: "{{ 'linux_' ~ host_architecture ~ '.tar.gz$' }}" - file: "{{ lego_dir }}/last_version" + file: "{{ (lego_dir, 'last_version') | path_join }}" extract: "{{ lego_dir }}" @@ -48,16 +50,38 @@ - meta: noop +- name: set job name + set_fact: + lego_job_name: "{{ acme_cfg.domains[0] ~ '-' ~ acme_cfg.type }}" + + +- name: set initial lego facts + set_fact: + lego_must_reissue: yes + lego_renew_script: "{{ (lego_cert_dir, 'cron-' ~ lego_job_name ~ '.sh') | path_join }}" + lego_lastrun_file: "{{ (lego_cert_dir, 'lastrun-' ~ lego_job_name) | path_join }}" + + +- name: create custom renewal script file + template: + src: renewal.j2 + dest: "{{ lego_renew_script }}" + force: yes + mode: 0500 + lstrip_blocks: yes + + - name: set lego parameters set_fact: lego_params: "{{ [ - ([] | zip_longest(acme_domains | d([]) | select() | map('quote'), fillvalue='--domains ') | map('join') | list), + ([] | zip_longest(acme_cfg.domains | d([]) | select() | map('quote'), fillvalue='--domains ') | map('join') | list), '--server ' ~ ((acme_cfg.endpoint_staging if acme_cfg.staging else acme_cfg.endpoint_prod) | quote), '--accept-tos', + '--filename ' ~ (lego_job_name | quote), '--email ' ~ (acme_cfg.email | d(maintainer_email) | quote), - '--key-type ec384', - '--path ' ~ (lego_dir | quote), + '--key-type ' ~ (acme_cfg.type | quote), + '--path ' ~ (lego_cert_dir | quote), '--dns acme-dns', '--dns.resolvers ' ~ (acme_cfg.resolver | d('1.1.1.1') | quote), '--dns.disable-cp' @@ -66,11 +90,18 @@ [ (('--days ' ~ (acme_cfg.renew_at_days | quote)) if acme_cfg.renew_at_days is defined else ''), ('--reuse-key' if acme_cfg.reuse_key | d(false) == true else ''), - ('--no-random-sleep' if acme_cfg.no_random_sleep | d(true) == true else '') + ('--no-random-sleep' if acme_cfg.no_random_sleep | d(true) == true else ''), + ('--renew-hook ' ~ (lego_renew_script | quote)) ] | flatten(levels=1) | select() | list | join(' ') }}" lego_preferred_chain: "{{ '--preferred-chain ' ~ (acme_cfg.preferred_chain | quote) if acme_cfg.preferred_chain is defined else '' }}" +- name: set lego command facts + set_fact: + lego_full_command: "{{ (lego_dir, 'lego') | path_join }} {{ lego_params }} run {{ lego_preferred_chain }}" + lego_renew_command: "{{ (lego_dir, 'lego') | path_join }} {{ lego_params }} renew {{ lego_preferred_chain }} {{ lego_renewal_params }}" + + - name: check if lastrun file exists stat: path: "{{ lego_lastrun_file }}" @@ -79,13 +110,6 @@ register: result -- name: set initial reissue value - set_fact: - lego_must_reissue: yes - lego_full_command: "{{ (lego_dir, 'lego') | path_join }} {{ lego_params }} run {{ lego_preferred_chain }}" - lego_renew_command: "{{ (lego_dir, 'lego') | path_join }} {{ lego_params }} renew {{ lego_preferred_chain }} {{ lego_renewal_params }}" - - - block: - name: get lastrun file contents slurp: @@ -107,16 +131,13 @@ chdir: "{{ lego_dir }}" environment: ACME_DNS_API_BASE: "{{ acme_cfg.server }}" - ACME_DNS_STORAGE_PATH: "{{ lego_accounts_file | d((lego_dir, 'accounts.conf') | path_join) }}" + ACME_DNS_STORAGE_PATH: "{{ lego_accounts_file }}" register: result - become: yes - become_method: "{{ 'su' if ansible_distribution == 'Alpine' else 'sudo' }}" - become_user: "{{ lego_user }}" when: lego_must_reissue rescue: - pause: - when: interactive | d(false) == true + when: interactive | d(true) == true - name: retry issuing cert with dns mode shell: @@ -124,11 +145,8 @@ chdir: "{{ lego_dir }}" environment: ACME_DNS_API_BASE: "{{ acme_cfg.server }}" - ACME_DNS_STORAGE_PATH: "{{ lego_accounts_file | d((lego_dir, 'accounts.conf') | path_join) }}" + ACME_DNS_STORAGE_PATH: "{{ lego_accounts_file }}" register: result - become: yes - become_method: "{{ 'su' if ansible_distribution == 'Alpine' else 'sudo' }}" - become_user: "{{ lego_user }}" - block: @@ -138,18 +156,32 @@ dest: "{{ lego_lastrun_file }}" remote_src: yes - - name: defer service restart debug: msg: deferring service restart changed_when: yes - notify: "{{ lego_notify }}" - when: lego_notify is defined + notify: "{{ acme_cfg.notify }}" + when: acme_cfg.notify is defined + + - name: copy certificates to their intended locations + copy: + src: "{{ (lego_cert_dir, 'certificates', lego_job_name ~ '.' ~ item.src_ext) | path_join }}" + dest: "{{ item.dest }}" + remote_src: yes + mode: 0600 + owner: "{{ acme_cfg.owner | d(omit) }}" + group: "{{ acme_cfg.group | d(omit) }}" + when: item.dest != None + loop: + - { src_ext: 'crt', dest: "{{ acme_cfg.cert | d(None) }}" } + - { src_ext: 'key', dest: "{{ acme_cfg.key | d(None) }}" } + notify: "{{ acme_cfg.notify | d(omit) }}" when: lego_must_reissue -- block: +- name: configure systemd service and timer + block: - name: template systemd files template: src: "{{ item.src }}.j2" @@ -161,7 +193,6 @@ - { src: 'lego_timer', dst: 'lego.timer' } notify: reload systemd daemons - - name: enable lego timer systemd: name: lego.timer @@ -171,4 +202,13 @@ when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' -# TODO: restart script \ No newline at end of file +- name: configure crontab entry + cron: + name: "certificate renewal ({{ lego_job_name ~ ' on ' ~ acme_cfg.server }})" + job: "ACME_DNS_API_BASE={{ acme_cfg.server | quote }} ; \ + ACME_DNS_STORAGE_PATH={{ lego_accounts_file | quote }} ; \ + cd {{ lego_dir | quote }} ; \ + {{ lego_renew_command }}" + hour: "{{ 4 | random(start=1, seed=(host_name ~ lego_job_name)) }}" + minute: "{{ 59 | random(seed=(host_name ~ lego_job_name)) }}" + when: ansible_distribution == 'Alpine' diff --git a/roles/lego/tasks/register_acme_domain.yml b/roles/lego/tasks/register_acme_domain.yml deleted file mode 100644 index 2e7f49b..0000000 --- a/roles/lego/tasks/register_acme_domain.yml +++ /dev/null @@ -1,19 +0,0 @@ -- name: call acme-dns-client - expect: - command: "./acme-dns-client register -d {{ domain | quote }} -s {{ acme_dns_server_url | quote }}" - chdir: "{{ caddy_acmedns_client_bin_dir }}" - echo: yes - responses: - 'Do you want acme-dns-client to monitor the CNAME record change?': 'n' - 'Do you wish to set up a CAA record now?': 'n' - become: yes - become_method: "{{ 'su' if ansible_distribution == 'Alpine' else 'sudo' }}" - become_user: "{{ caddy_user }}" - register: result - changed_when: yes - failed_when: not ('successfully registered' in result.stdout) - - -- name: pause if acme-dns-client registered a new record - pause: - when: result.changed and (interactive | d(true) == true) diff --git a/roles/lego/templates/renewal.j2 b/roles/lego/templates/renewal.j2 new file mode 100644 index 0000000..3a8572f --- /dev/null +++ b/roles/lego/templates/renewal.j2 @@ -0,0 +1,22 @@ +{%- set cert_base = (lego_cert_dir, 'certificates', lego_job_name ~ '.') | path_join -%} + + +#!/bin/sh + +{{ (acme_cfg.run_before_renew ~ ' &>/dev/null') if acme_cfg.run_before_renew is defined else '' }} + +{%- if acme_cfg.cert is defined %} + cp -fpT {{ (cert_base ~ 'crt') | quote }} {{ acme_cfg.cert | quote }} + {% if acme_cfg.owner is defined -%} + chown -f {{ acme_cfg.owner ~ ((':' ~ acme_cfg.group) if acme_cfg.group is defined else '') }} {{ acme_cfg.cert | quote }} + {% endif -%} +{% endif -%} + +{% if acme_cfg.key is defined -%} + cp -fpT {{ (cert_base ~ 'key') | quote }} {{ acme_cfg.key | quote }} + {% if acme_cfg.owner is defined -%} + chown -f {{ acme_cfg.owner ~ ((':' ~ acme_cfg.group) if acme_cfg.group is defined else '') }} {{ acme_cfg.key | quote }} + {% endif -%} +{% endif -%} + +{{ (acme_cfg.run_after_renew ~ ' &>/dev/null &') if acme_cfg.run_after_renew is defined else '' }} diff --git a/roles/postgres/tasks/add_database.yml b/roles/postgres/tasks/add_database.yml index 7392d63..6b3e051 100644 --- a/roles/postgres/tasks/add_database.yml +++ b/roles/postgres/tasks/add_database.yml @@ -54,6 +54,15 @@ role: "{{ database.user }}" +- name: grant privileges to public schema + community.postgresql.postgresql_privs: + database: "{{ database.name }}" + privs: USAGE,CREATE + type: schema + objs: public + role: "{{ database.user }}" + + - name: add line to postgres hba community.postgresql.postgresql_pg_hba: dest: "{{ (postgresql_conf_dir, 'pg_hba.conf') | path_join }}" diff --git a/roles/rproxy/tasks/main.yml b/roles/rproxy/tasks/main.yml index c359548..137515e 100644 --- a/roles/rproxy/tasks/main.yml +++ b/roles/rproxy/tasks/main.yml @@ -11,7 +11,7 @@ vars: nginx: servers: - - conf: rproxy_collected_configs[0].nginx + - conf: rproxy_collected_configs[0].nginx_rproxy certs: "{{ host_tls }}" acme_server: "{{ (rproxy_collected_configs[0].acme | d({}))['server'] | d(None) }}" when: reverse_proxy_type == 'nginx' @@ -22,7 +22,8 @@ name: caddy vars: caddy_config: "{{ rproxy_collected_configs[0].caddy | d({}) }}" - caddy_reverse_proxy_handlers: "{{ rproxy_collected_configs[0].caddy_reverse_proxy_handlers | d([]) }}" + caddy_reverse_proxy_handlers: "{{ rproxy_collected_configs[0].caddy_rproxy | d([]) }}" + caddy_acme_config: "{{ rproxy_collected_configs[0].acme | d({}) }}" when: reverse_proxy_type == 'caddy' @@ -34,8 +35,8 @@ firewall_config: filter: input: - - { protocol: tcp, dst_port: [80, 443], action: accept } - - { protocol: udp, dst_port: [80, 443], action: accept } + - { protocol: tcp, dst_port: "{{ [80, 443] if host_tls else [80] }}", action: accept } + - { protocol: udp, dst_port: "{{ [80, 443] if host_tls else [80] }}", action: accept } when: rproxy_collected_configs is defined and rproxy_collected_configs | length > 0 and role_use_reverse_proxy | d(true) == true diff --git a/roles/vault/tasks/main.yml b/roles/vault/tasks/main.yml index f78ab4a..1872d1e 100644 --- a/roles/vault/tasks/main.yml +++ b/roles/vault/tasks/main.yml @@ -157,8 +157,8 @@ vars: rproxy_config: port: "{{ vault_port }}" - nginx: rproxy_nginx.j2 - caddy_reverse_proxy_handlers: + nginx_rproxy: rproxy_nginx.j2 + caddy_rproxy: - handler: reverse_proxy upstreams: - dial: "127.0.0.1:{{ vault_port }}" diff --git a/tasks/pre_tasks.yml b/tasks/pre_tasks.yml index 2eab15e..3e55ad0 100644 --- a/tasks/pre_tasks.yml +++ b/tasks/pre_tasks.yml @@ -44,9 +44,16 @@ - name: set hardware information set_fact: - host_hardware: "{{ (default_container_hardware | combine(role_hardware | d({})) | - combine(host_hardware | d({}))) if host_is_container - else (host_hardware | d({})) }}" + host_hardware: "{{ ( + { + 'cores': 4, + 'cpus': 1, + 'cpuunits': 1024, + 'memory': 128, + 'swap': 128, + 'disk': 0.4 + } | combine(role_hardware | d({})) | + combine(host_hardware | d({}))) if host_is_container else (host_hardware | d({})) }}" - name: clamp hardware cores to max node number