From ac4de942baf16a70547333a6204c4777de16004c Mon Sep 17 00:00:00 2001 From: dave Date: Thu, 5 Jan 2023 12:01:31 +0300 Subject: [PATCH] fix: various fixes --- all.yml | 2 +- group_vars/all.yml | 11 +- handlers/main.yml | 3 + roles/acme-dns/defaults/main.yml | 3 +- roles/acme-dns/tasks/info.yml | 12 ++ roles/acme-dns/tasks/main.yml | 43 +++-- roles/acme-dns/templates/init.j2 | 6 +- .../{nginx_server.j2 => rproxy_nginx.j2} | 0 roles/backup/tasks/main.yml | 39 +++- roles/backup/tasks/setup.yml | 31 ---- roles/caddy/defaults/main.yml | 16 +- roles/caddy/handlers/main.yml | 5 - roles/caddy/tasks/build_caddy.yml | 12 +- roles/caddy/tasks/main.yml | 41 ++--- roles/caddy/tasks/setup_lego.yml | 158 ---------------- roles/caddy/templates/init.j2 | 2 +- roles/caddy/vars/reverse_proxy.yml | 14 +- roles/caddy/vars/{tls_lego.yml => tls.yml} | 6 +- roles/caddy/vars/tls_caddy.yml | 33 ---- roles/certs/tasks/acme_dns.yml | 1 + roles/common/defaults/main.yml | 3 +- roles/container/tasks/preconf.yml | 12 +- roles/coredns/tasks/install.yml | 0 roles/iptables/tasks/add.yml | 5 + roles/iptables/tasks/main.yml | 1 + roles/lego/defaults/main.yml | 11 ++ .../tasks/acme-client.yml} | 0 roles/lego/tasks/main.yml | 174 ++++++++++++++++++ .../tasks/register_acme_domain.yml | 4 +- roles/nginx/tasks/main.yml | 1 + roles/postgres/defaults/main.yml | 19 +- roles/postgres/tasks/add_database.yml | 2 +- roles/postgres/tasks/install.yml | 15 +- roles/postgres/tasks/integrate.yml | 11 +- roles/rproxy/tasks/add.yml | 5 + roles/rproxy/tasks/main.yml | 41 +++++ roles/vault/tasks/info.yml | 12 ++ roles/vault/tasks/main.yml | 33 ++-- .../{nginx_server.j2 => rproxy_nginx.j2} | 0 39 files changed, 427 insertions(+), 360 deletions(-) create mode 100644 handlers/main.yml create mode 100644 roles/acme-dns/tasks/info.yml rename roles/acme-dns/templates/{nginx_server.j2 => rproxy_nginx.j2} (100%) delete mode 100644 roles/backup/tasks/setup.yml delete mode 100644 roles/caddy/tasks/setup_lego.yml rename roles/caddy/vars/{tls_lego.yml => tls.yml} (52%) delete mode 100644 roles/caddy/vars/tls_caddy.yml create mode 100644 roles/coredns/tasks/install.yml create mode 100644 roles/iptables/tasks/add.yml create mode 100644 roles/lego/defaults/main.yml rename roles/{caddy/tasks/setup_acme_client.yml => lego/tasks/acme-client.yml} (100%) create mode 100644 roles/lego/tasks/main.yml rename roles/{caddy => lego}/tasks/register_acme_domain.yml (86%) create mode 100644 roles/rproxy/tasks/add.yml create mode 100644 roles/rproxy/tasks/main.yml create mode 100644 roles/vault/tasks/info.yml rename roles/vault/templates/{nginx_server.j2 => rproxy_nginx.j2} (100%) diff --git a/all.yml b/all.yml index 340f23c..1dcfc31 100644 --- a/all.yml +++ b/all.yml @@ -35,7 +35,7 @@ role_deps: "{{ (([{ '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', 'function': 'integrate' }] if role_dependency_use_db | d(false) == true else []) + ([{ 'stage': 3, 'role': 'postgres', 'tasks_from': 'integrate.yml' }] if role_use_database | d(false) == true else []) ) | flatten(levels=1) | sort(attribute='stage') }}" selected_stages: "{%- if stage is defined and ((stage | string) is search(',')) -%}{{ stage | string | split(',') | list | map('int') | list }}\ {%- elif (stage is not defined) or ((stage | int) == 0) -%}{{ [1,2,3,4,5,6,7,8,9] }}\ diff --git a/group_vars/all.yml b/group_vars/all.yml index f1ac5e8..d9bf205 100644 --- a/group_vars/all.yml +++ b/group_vars/all.yml @@ -1,7 +1,6 @@ ansible_user: root ansible_dir: /etc/ansible ansible_key_dir: keys -alpine_version: "3.17" mac_prefix: 02:FF @@ -39,6 +38,16 @@ role_dependency_common: - {stage: 2, role: 'common'} - {stage: 4, role: 'ns'} - {stage: 5, role: 'mail-user'} + - {stage: 8, role: 'rproxy'} - {stage: 8, role: 'iptables'} - {stage: 9, role: 'backup', function: 'setup'} +reverse_proxy_type: caddy + +acme_default_config: + endpoint_prod: https://acme-v02.api.letsencrypt.org/directory + endpoint_staging: https://acme-staging-v02.api.letsencrypt.org/directory + staging: no + resolver: 1.1.1.1 + renew_at_days: 45 + preferred_chain: 'ISRG Root X1' diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..312678f --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,3 @@ +- name: restart systemd daemons + systemd: + daemon_reload: yes \ No newline at end of file diff --git a/roles/acme-dns/defaults/main.yml b/roles/acme-dns/defaults/main.yml index 4bc76a0..ed9beab 100644 --- a/roles/acme-dns/defaults/main.yml +++ b/roles/acme-dns/defaults/main.yml @@ -8,7 +8,6 @@ acme_dns_admin: "{{ maintainer_email | d('admin@' ~ (acme_tld | d(tld))) }}" acme_dns_api_port: 8080 - acme_dns_default_config: general: listen: ":53" @@ -34,7 +33,7 @@ acme_dns_default_config: tls: none use_header: no - notification_email: "{{ letsencrypt_email | d(maintainer_email) }}" + notification_email: "{{ maintainer_email }}" corsorigins: - "*" diff --git a/roles/acme-dns/tasks/info.yml b/roles/acme-dns/tasks/info.yml new file mode 100644 index 0000000..07af2ac --- /dev/null +++ b/roles/acme-dns/tasks/info.yml @@ -0,0 +1,12 @@ +- name: set role information + set_fact: + role_dependency_index: 0 + + role_hardware: + cores: 2 + memory: 128 + swap: 64 + disk: 0.3 + + role_use_reverse_proxy: yes + role_use_database: yes diff --git a/roles/acme-dns/tasks/main.yml b/roles/acme-dns/tasks/main.yml index 5308e5f..2b193b4 100644 --- a/roles/acme-dns/tasks/main.yml +++ b/roles/acme-dns/tasks/main.yml @@ -1,6 +1,7 @@ - name: set acme_dns_cfg set_fact: - acme_dns_cfg: "{{ acme_dns_default_config | d({}) | combine(acme_dns_config | d({}), recursive=true) }}" + acme_dns_cfg: "{{ acme_dns_default_config | d({}) | + combine(acme_dns_config | d({}), recursive=true) }}" - name: install dependencies @@ -27,7 +28,7 @@ location: github assets: yes asset_filter: 'Linux_amd64.tar.gz$' - file: "{{ acme_dns_dir }}/last_version" + file: "{{ (acme_dns_dir, 'last_version') | path_join }}" extract: "{{ acme_dns_dir }}" user: "{{ acme_dns_user }}" group: "{{ acme_dns_group }}" @@ -36,7 +37,7 @@ - name: delete unnecessary files file: - path: "{{ acme_dns_dir }}/{{ item }}" + path: "{{ (acme_dns_dir, item) | path_join }}" state: absent loop: - CHANGELOG.md @@ -47,7 +48,7 @@ - name: template acme-dns config template: src: config.j2 - dest: "{{ acme_dns_dir }}/config.cfg" + dest: "{{ (acme_dns_dir, 'config.cfg') | path_join }}" force: yes mode: 0400 owner: "{{ acme_dns_user }}" @@ -63,45 +64,47 @@ force: yes mode: "+x" notify: restart acme-dns + when: ansible_distribution == 'Alpine' - name: ensure acme-dns binary has executable bit set file: - path: "{{ acme_dns_dir }}/acme-dns" + path: "{{ (acme_dns_dir, 'acme-dns') | path_join }}" mode: "+x" - name: add cap_net_bind_service to acme-dns executable community.general.capabilities: - path: "{{ acme_dns_dir }}/acme-dns" + path: "{{ (acme_dns_dir, 'acme-dns') | path_join }}" capability: cap_net_bind_service+ep changed_when: no -- name: set acme server address - set_fact: - acme_server: "http://127.0.0.1:{{ acme_dns_api_port }}" +- name: flush handlers + meta: flush_handlers -- name: install and configure nginx +- name: add reverse proxy config include_role: - name: nginx + name: rproxy + tasks_from: add.yml vars: - nginx: - servers: - - conf: nginx_server - certs: "{{ host_tls }}" - - -- name: flush handlers - meta: flush_handlers + rproxy_config: + port: "{{ acme_dns_api_port }}" + acme: + server: "http://127.0.0.1:{{ acme_dns_api_port }}" + nginx: rproxy_nginx.j2 + caddy_reverse_proxy_handlers: + - handler: reverse_proxy + upstreams: + - dial: "127.0.0.1:{{ acme_dns_api_port }}" - name: add directories to backup plan include_role: name: backup + tasks_from: add.yml vars: - function: add backup_items: - "{{ acme_dns_dir }}" diff --git a/roles/acme-dns/templates/init.j2 b/roles/acme-dns/templates/init.j2 index 683e567..a8e94ba 100644 --- a/roles/acme-dns/templates/init.j2 +++ b/roles/acme-dns/templates/init.j2 @@ -1,9 +1,9 @@ #!/sbin/openrc-run name="$SVCNAME" -command="{{ acme_dns_dir }}/$SVCNAME" +command="{{ (acme_dns_dir, '$SVCNAME') | path_join }}" directory="{{ acme_dns_dir }}" -command_user="{{ acme_dns_user }}:{{ acme_dns_group }}" +command_user="{{ acme_dns_user ~ ':' ~ acme_dns_group }}" pidfile="/var/run/$SVCNAME.pid" command_background=true start_stop_daemon_args="--stdout-logger logger --stderr-logger logger" @@ -14,5 +14,5 @@ depend() { } start_pre() { - setcap 'cap_net_bind_service=+ep' {{ acme_dns_dir }}/$SVCNAME + setcap 'cap_net_bind_service=+ep' {{ (acme_dns_dir, '$SVCNAME') | path_join | quote }} } diff --git a/roles/acme-dns/templates/nginx_server.j2 b/roles/acme-dns/templates/rproxy_nginx.j2 similarity index 100% rename from roles/acme-dns/templates/nginx_server.j2 rename to roles/acme-dns/templates/rproxy_nginx.j2 diff --git a/roles/backup/tasks/main.yml b/roles/backup/tasks/main.yml index e8dff7e..40e2af7 100644 --- a/roles/backup/tasks/main.yml +++ b/roles/backup/tasks/main.yml @@ -1,8 +1,35 @@ -- name: add to backup plan - include_tasks: add.yml - when: function is defined and function == 'add' +- fail: + when: function is defined -- name: setup backups - include_tasks: setup.yml - when: function is defined and function == 'setup' +- name: notify that backups are not supported + debug: + msg: backup host is missing, will not set up backups + when: services.backup is not mapping + + +- name: install restic with custom configuration + block: + - include_role: + name: restic + vars: + backup: "{{ backup_cfg }}" + + when: services.backup is mapping and backup_cfg is mapping + + +- name: install restic with default configuration + block: + - include_role: + name: restic + vars: + backup: + dirs: "{{ collected_backup_dirs }}" + password: "{{ backup_password }}" + tags: automated + filter: + - "*.log" + - "node_modules" + - ".npm" + + when: services.backup is mapping and backup_cfg is not defined and backup_password is defined diff --git a/roles/backup/tasks/setup.yml b/roles/backup/tasks/setup.yml deleted file mode 100644 index af59e0b..0000000 --- a/roles/backup/tasks/setup.yml +++ /dev/null @@ -1,31 +0,0 @@ -- name: notify that backups are not supported - debug: - msg: backup host is missing, will not set up backups - when: services.backup is not mapping - - -- name: install restic with custom configuration - block: - - include_role: - name: restic - vars: - backup: "{{ backup_cfg }}" - - when: services.backup is mapping and backup_cfg is mapping - - -- name: install restic with default configuration - block: - - include_role: - name: restic - vars: - backup: - dirs: "{{ collected_backup_dirs }}" - password: "{{ backup_password }}" - tags: automated - filter: - - "*.log" - - "node_modules" - - ".npm" - - when: services.backup is mapping and backup_cfg is not defined and backup_password is defined diff --git a/roles/caddy/defaults/main.yml b/roles/caddy/defaults/main.yml index 168d797..ff84a36 100644 --- a/roles/caddy/defaults/main.yml +++ b/roles/caddy/defaults/main.yml @@ -4,27 +4,15 @@ caddy_group: caddy caddy_conf_dir: /etc/caddy caddy_asset_dir: /opt/caddy-assets caddy_bin_dir: /usr/sbin +caddy_cert_dir: /etc/caddy/certs caddy_xcaddy_dir: /opt/xcaddy -caddy_acmedns_client_bin_dir: /opt/acme-client caddy_conf_file: "{{ (caddy_conf_dir, 'caddy.json') | path_join }}" -caddy_default_plugins: - - github.com/caddy-dns/acmedns - -# hardcoded in acmedns_client and cannot be changed -caddy_acmedns_client_dir: /etc/acmedns -caddy_acmedns_client_file: /etc/acmedns/clientstorage.json - -caddy_use_lego: no -caddy_lego_dir: /opt/lego -caddy_lego_lastrun_file: "{{ (caddy_lego_dir, 'lastrun') | path_join }}" - +caddy_default_plugins: [] caddy_domains: - "{{ host_fqdn }}" -caddy_acme_endpoint: https://acme-staging-v02.api.letsencrypt.org/directory - caddy_default_config: admin: disabled: yes diff --git a/roles/caddy/handlers/main.yml b/roles/caddy/handlers/main.yml index e2a1091..4e68c71 100644 --- a/roles/caddy/handlers/main.yml +++ b/roles/caddy/handlers/main.yml @@ -2,8 +2,3 @@ service: name: caddy state: restarted - - -- name: reload systemd daemons - systemd: - daemon_reload: yes diff --git a/roles/caddy/tasks/build_caddy.yml b/roles/caddy/tasks/build_caddy.yml index 42a9686..6af6096 100644 --- a/roles/caddy/tasks/build_caddy.yml +++ b/roles/caddy/tasks/build_caddy.yml @@ -36,7 +36,9 @@ - name: install xcaddy package: - name: xcaddy + name: + - xcaddy + - golang when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' @@ -78,12 +80,12 @@ remote_src: yes mode: "+x" - when: ansible_distribution == 'Alpine' + - name: install golang + package: + name: go -- name: install golang - package: - name: golang + when: ansible_distribution == 'Alpine' - name: get latest caddy version diff --git a/roles/caddy/tasks/main.yml b/roles/caddy/tasks/main.yml index bb177c6..d330f8e 100644 --- a/roles/caddy/tasks/main.yml +++ b/roles/caddy/tasks/main.yml @@ -1,32 +1,24 @@ - name: determine if custom caddy build should be used set_fact: caddy_custom_build: "{{ (((caddy_default_plugins | d([])) + (caddy_custom_plugins | d([]))) | length > 0) or (caddy_use_xcaddy | d(false) == true) }}" - caddy_has_reverse_proxy: "{{ reverse_proxy_port is number or reverse_proxy_port is string }}" -- name: import vars for automatic caddy tls +- name: import vars for unmanaged tls include_vars: - file: tls_caddy.yml - when: not caddy_use_lego - - -- name: import vars for lego tls - include_vars: - file: tls_lego.yml - when: caddy_use_lego + file: tls.yml - name: import reverse proxy vars include_vars: file: reverse_proxy.yml - when: caddy_has_reverse_proxy + when: caddy_reverse_proxy_handlers is defined - name: set caddy_cfg set_fact: caddy_cfg: "{{ caddy_default_config | d({}) | combine(caddy_tls_config | d({}), recursive=true) | - combine(caddy_reverse_proxy_config | d({}), recursive=true) | + combine(caddy_reverse_proxy_config | d({}), recursive=true, list_merge='replace') | combine(caddy_config | d({}), recursive=true) }}" @@ -49,6 +41,7 @@ loop: - "{{ caddy_conf_dir }}" - "{{ caddy_asset_dir }}" + - "{{ caddy_cert_dir }}" - name: create caddy bin dir @@ -67,16 +60,6 @@ when: not caddy_custom_build -- name: setup acme-dns-client for auto-tls - include_tasks: setup_acme_client.yml - when: not caddy_use_lego - - -- name: setup lego for unmanaged tls - include_tasks: setup_lego.yml - when: caddy_use_lego - - - name: template caddy config template: src: caddy.j2 @@ -120,6 +103,19 @@ 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 }}" + + - name: flush handlers meta: flush_handlers @@ -132,6 +128,7 @@ backup_items: - "{{ caddy_asset_dir }}" - "{{ caddy_conf_dir }}" + - "{{ caddy_cert_dir }}" - name: enable and start caddy diff --git a/roles/caddy/tasks/setup_lego.yml b/roles/caddy/tasks/setup_lego.yml deleted file mode 100644 index ad52960..0000000 --- a/roles/caddy/tasks/setup_lego.yml +++ /dev/null @@ -1,158 +0,0 @@ -- name: determine host architecture - include_tasks: tasks/get_host_arch.yml - - -- name: create lego working dir - file: - path: "{{ caddy_lego_dir }}" - state: directory - mode: 0700 - owner: "{{ caddy_user }}" - group: "{{ caddy_group }}" - - -- name: get and extract latest lego version - include_tasks: tasks/get_lastversion.yml - vars: - package: - name: go-acme/lego - location: github - assets: yes - asset_filter: "{{ 'linux_' ~ host_architecture ~ '.tar.gz$' }}" - file: "{{ caddy_lego_dir }}/last_version" - extract: "{{ caddy_lego_dir }}" - user: "{{ caddy_user }}" - group: "{{ caddy_group }}" - - -- block: - - name: remove unnecessary files - file: - path: "{{ (caddy_lego_dir, item) | path_join }}" - state: absent - loop: - - LICENSE - - CHANGELOG.md - rescue: - - meta: noop - - -- name: set lego parameters - set_fact: - lego_params: "{{ - [ - ([] | zip_longest(caddy_domains | d([]) | select() | map('quote'), fillvalue='--domains ') | map('join') | list), - '--server ' ~ (acme_endpoint | quote), - '--accept-tos', - '--email ' ~ (acme_email | quote), - '--key-type ec384', - '--path ' ~ (caddy_lego_dir | quote), - '--dns acme-dns', - '--dns.resolvers 9.9.9.9', - '--dns.disable-cp' - ] | flatten(levels=1) | select() | list | join(' ') }}" - lego_renewal_params: "{{ - [ - (('--days ' ~ (acme_renewal_days | quote)) if acme_renewal_days is defined else ''), - ('--reuse-key' if acme_reuse_key | d(false) == true else ''), - ('--no-random-sleep' if acme_no_random_sleep | d(true) == true else '') - ] | flatten(levels=1) | select() | list | join(' ') }}" - lego_preferred_chain: "{{ '--preferred-chain ' ~ (acme_preferred_chain | quote) if acme_preferred_chain is defined else '' }}" - - -- name: check if lastrun file exists - stat: - path: "{{ caddy_lego_lastrun_file }}" - get_checksum: no - get_mime: no - register: result - - -- name: set initial reissue value - set_fact: - lego_must_reissue: yes - lego_full_command: "{{ (caddy_lego_dir, 'lego') | path_join }} {{ lego_params }} run {{ lego_preferred_chain }}" - lego_renew_command: "{{ (caddy_lego_dir, 'lego') | path_join }} {{ lego_params }} renew {{ lego_preferred_chain }} {{ lego_renewal_params }}" - - -- block: - - name: get lastrun file contents - slurp: - path: "{{ caddy_lego_lastrun_file }}" - register: file_content - no_log: yes - - - name: set acme-dns-client domain fact - set_fact: - lego_must_reissue: "{{ (file_content.content | b64decode) != lego_full_command }}" - - when: result.stat.exists - - -- block: - - name: issue cert with dns mode - shell: - cmd: "{{ lego_full_command }}" - chdir: "{{ caddy_lego_dir }}" - environment: - ACME_DNS_API_BASE: "{{ acme_dns_server }}" - ACME_DNS_STORAGE_PATH: "{{ (caddy_lego_dir, 'accounts.conf') | path_join }}" - register: result - become: yes - become_method: "{{ 'su' if ansible_distribution == 'Alpine' else 'sudo' }}" - become_user: "{{ caddy_user }}" - - when: lego_must_reissue - rescue: - - pause: - - - name: retry issuing cert with dns mode - shell: - cmd: "{{ lego_full_command }}" - chdir: "{{ caddy_lego_dir }}" - environment: - ACME_DNS_API_BASE: "{{ acme_dns_server }}" - ACME_DNS_STORAGE_PATH: "{{ (caddy_lego_dir, 'accounts.conf') | path_join }}" - register: result - become: yes - become_method: "{{ 'su' if ansible_distribution == 'Alpine' else 'sudo' }}" - become_user: "{{ caddy_user }}" - - -- block: - - name: save data to lastrun file - copy: - content: "{{ lego_full_command }}" - dest: "{{ caddy_lego_lastrun_file }}" - remote_src: yes - - - - name: defer caddy restart - debug: - msg: deferring caddy restart - changed_when: yes - notify: restart caddy - - when: lego_must_reissue - - -- name: template systemd files - template: - src: "{{ item.src }}.j2" - dest: "/etc/systemd/system/{{ item.dst }}" - force: yes - lstrip_blocks: yes - loop: - - { src: 'lego_systemd', dst: 'lego.service' } - - { src: 'lego_timer', dst: 'lego.timer' } - notify: reload systemd daemons - - -- name: enable lego timer - systemd: - name: lego.timer - state: started - enabled: yes - - -# TODO: restart script \ No newline at end of file diff --git a/roles/caddy/templates/init.j2 b/roles/caddy/templates/init.j2 index 8ecc368..b13055d 100644 --- a/roles/caddy/templates/init.j2 +++ b/roles/caddy/templates/init.j2 @@ -4,7 +4,7 @@ name="$SVCNAME" directory="{{ caddy_conf_dir }}" -command=/usr/sbin/caddy +command="{{ (caddy_bin_dir, 'caddy') | path_join }}" command_args="run --environ $caddy_opts" command_user="{{ caddy_user ~ ':' ~ caddy_group }}" pidfile="/var/run/$SVCNAME.pid" diff --git a/roles/caddy/vars/reverse_proxy.yml b/roles/caddy/vars/reverse_proxy.yml index 0c11d0a..7cae61c 100644 --- a/roles/caddy/vars/reverse_proxy.yml +++ b/roles/caddy/vars/reverse_proxy.yml @@ -1,3 +1,7 @@ +caddy_reverse_proxy_default_handler: + - handler: static_response + status_code: 404 + caddy_reverse_proxy_config: apps: http: @@ -7,7 +11,7 @@ caddy_reverse_proxy_config: - "tcp4/:443" - "tcp6/:443" automatic_https: - disable: "{{ caddy_use_lego }}" + disable: yes tls_connection_policies: - match: sni: @@ -17,11 +21,5 @@ caddy_reverse_proxy_config: - match: - host: - "{{ host_fqdn }}" - handle: - - handler: subroute - routes: - - handle: - - handler: reverse_proxy - upstreams: - - dial: "127.0.0.1:{{ reverse_proxy_port }}" + handle: "{{ (caddy_reverse_proxy_handlers | d([])) + caddy_reverse_proxy_default_handler }}" terminal: true diff --git a/roles/caddy/vars/tls_lego.yml b/roles/caddy/vars/tls.yml similarity index 52% rename from roles/caddy/vars/tls_lego.yml rename to roles/caddy/vars/tls.yml index 2a92368..0fc1cb9 100644 --- a/roles/caddy/vars/tls_lego.yml +++ b/roles/caddy/vars/tls.yml @@ -1,8 +1,8 @@ -caddy_unmanaged_tls_config: +caddy_tls_config: apps: tls: certificates: load_files: - - certificate: "{{ (caddy_lego_dir, 'certificates', caddy_domains[0] ~ '.crt') | path_join }}" - key: "{{ (caddy_lego_dir, 'certificates', caddy_domains[0] ~ '.key') | path_join }}" + - certificate: "{{ (caddy_cert_dir, 'certificates', caddy_domains[0] ~ '.crt') | path_join }}" + key: "{{ (caddy_cert_dir, 'certificates', caddy_domains[0] ~ '.key') | path_join }}" tags: "{{ caddy_domains }}" diff --git a/roles/caddy/vars/tls_caddy.yml b/roles/caddy/vars/tls_caddy.yml deleted file mode 100644 index ff1cda9..0000000 --- a/roles/caddy/vars/tls_caddy.yml +++ /dev/null @@ -1,33 +0,0 @@ -caddy_auto_tls_config: - apps: - tls: - automation: - policies: - - subjects: "{{ caddy_domains }}" - issuers: - - module: acme - ca: "{{ caddy_acme_endpoint }}" - email: "{{ maintainer_email | d(None) }}" - acme_timeout: 5m - challenges: - http: - disabled: yes - tls-alpn: - disabled: yes - dns: - resolvers: - - 1.1.1.1 - - 8.8.8.8 - provider: - name: acmedns - config_file_path: "{{ caddy_acmedns_client_file }}" - propagation_delay: 15s - propagation_timeout: -1 - preferred_chains: - root_common_name: - - ISRG Root X1 - must_staple: yes - key_type: p384 - renew_interval: 1h - certificates: - automate: "{{ caddy_domains }}" diff --git a/roles/certs/tasks/acme_dns.yml b/roles/certs/tasks/acme_dns.yml index 9a00d4b..ee8bffa 100644 --- a/roles/certs/tasks/acme_dns.yml +++ b/roles/certs/tasks/acme_dns.yml @@ -22,3 +22,4 @@ acme_tld: "{{ combined.tld | d(None) }}" acme_fqdn: "{{ combined.fqdn | d(None) }}" acme_hosts: "{{ combined.hosts | d(None) }}" + acme_server: "{{ combined.acme_server | d(None) }}" \ No newline at end of file diff --git a/roles/common/defaults/main.yml b/roles/common/defaults/main.yml index aa0d8da..5ad1e0d 100644 --- a/roles/common/defaults/main.yml +++ b/roles/common/defaults/main.yml @@ -1 +1,2 @@ -dropbear_dir: /etc/dropbear \ No newline at end of file +dropbear_dir: /etc/dropbear +alpine_version: "3.17" \ No newline at end of file diff --git a/roles/container/tasks/preconf.yml b/roles/container/tasks/preconf.yml index a322bd0..aef903d 100644 --- a/roles/container/tasks/preconf.yml +++ b/roles/container/tasks/preconf.yml @@ -13,20 +13,11 @@ - pct_command: rc-update add dropbear chg_substr: added to runlevel - - name: install dropbear-scp if this is not an ansible controller - include_tasks: tasks/pct_command.yml - vars: - pct_command: apk add dropbear-scp - chg_substr: Installing - when: (inventory_hostname != 'ansible') and ((primary_role is not defined) or (primary_role != 'ansible')) - and alpine_version is version('3.15', '<=') - - - name: install openssh-sftp-server due to openssh 9 scp deprecation + - name: install openssh-sftp-server include_tasks: tasks/pct_command.yml vars: pct_command: apk add openssh-sftp-server chg_substr: Installing - when: alpine_version is version('3.16', '>=') - name: start dropbear include_tasks: tasks/pct_command.yml @@ -57,7 +48,6 @@ vars: pct_command: "sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config" - - name: start sshd include_tasks: tasks/pct_command.yml vars: diff --git a/roles/coredns/tasks/install.yml b/roles/coredns/tasks/install.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/iptables/tasks/add.yml b/roles/iptables/tasks/add.yml new file mode 100644 index 0000000..f974876 --- /dev/null +++ b/roles/iptables/tasks/add.yml @@ -0,0 +1,5 @@ +- name: collect firewall config + set_fact: + firewall_collected_configs: "{{ firewall_collected_configs | d([]) | + combine(firewall_config, recursive=true, list_merge='append') }}" + when: firewall_config is mapping diff --git a/roles/iptables/tasks/main.yml b/roles/iptables/tasks/main.yml index aa37003..232d108 100644 --- a/roles/iptables/tasks/main.yml +++ b/roles/iptables/tasks/main.yml @@ -4,6 +4,7 @@ firewall_cfg: "{{ firewall_default_config | d({}) | combine(firewall_ssh_config if (firewall_use_ssh | d(true) == true) else {}, recursive=true, list_merge='append') | combine(role_firewall_config | d({}), recursive=true, list_merge='append') | + combine(firewall_collected_configs | d({}), recursive=true, list_merge='append') | combine(firewall | d({}), recursive=true, list_merge='append') }}" diff --git a/roles/lego/defaults/main.yml b/roles/lego/defaults/main.yml new file mode 100644 index 0000000..ced1a4f --- /dev/null +++ b/roles/lego/defaults/main.yml @@ -0,0 +1,11 @@ +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 + diff --git a/roles/caddy/tasks/setup_acme_client.yml b/roles/lego/tasks/acme-client.yml similarity index 100% rename from roles/caddy/tasks/setup_acme_client.yml rename to roles/lego/tasks/acme-client.yml diff --git a/roles/lego/tasks/main.yml b/roles/lego/tasks/main.yml new file mode 100644 index 0000000..9f05ec5 --- /dev/null +++ b/roles/lego/tasks/main.yml @@ -0,0 +1,174 @@ +- name: set acme_cfg + set_fact: + acme_cfg: "{{ acme_default_config | d({}) | + combine(acme_config | d({}), 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 + file: + path: "{{ lego_dir }}" + state: directory + mode: 0700 + + +- name: get and extract latest lego version + include_tasks: tasks/get_lastversion.yml + vars: + package: + name: go-acme/lego + location: github + assets: yes + asset_filter: "{{ 'linux_' ~ host_architecture ~ '.tar.gz$' }}" + file: "{{ lego_dir }}/last_version" + extract: "{{ lego_dir }}" + + +- block: + - name: remove unnecessary files + file: + path: "{{ (lego_dir, item) | path_join }}" + state: absent + loop: + - LICENSE + - CHANGELOG.md + rescue: + - meta: noop + + +- name: set lego parameters + set_fact: + lego_params: "{{ + [ + ([] | zip_longest(acme_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', + '--email ' ~ (acme_cfg.email | d(maintainer_email) | quote), + '--key-type ec384', + '--path ' ~ (lego_dir | quote), + '--dns acme-dns', + '--dns.resolvers ' ~ (acme_cfg.resolver | d('1.1.1.1') | quote), + '--dns.disable-cp' + ] | flatten(levels=1) | select() | list | join(' ') }}" + lego_renewal_params: "{{ + [ + (('--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 '') + ] | 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: check if lastrun file exists + stat: + path: "{{ lego_lastrun_file }}" + get_checksum: no + get_mime: no + 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: + path: "{{ lego_lastrun_file }}" + register: file_content + no_log: yes + + - name: determine if cert should be reissued + set_fact: + lego_must_reissue: "{{ (file_content.content | b64decode) != lego_full_command }}" + + when: result.stat.exists + + +- block: + - name: issue cert with dns mode + shell: + cmd: "{{ lego_full_command }}" + 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) }}" + 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 + + - name: retry issuing cert with dns mode + shell: + cmd: "{{ lego_full_command }}" + 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) }}" + register: result + become: yes + become_method: "{{ 'su' if ansible_distribution == 'Alpine' else 'sudo' }}" + become_user: "{{ lego_user }}" + + +- block: + - name: save data to lastrun file + copy: + content: "{{ lego_full_command }}" + 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 + + when: lego_must_reissue + + +- block: + - name: template systemd files + template: + src: "{{ item.src }}.j2" + dest: "{{ ('/etc/systemd/system', item.dst) | path_join }}" + force: yes + lstrip_blocks: yes + loop: + - { src: 'lego_systemd', dst: 'lego.service' } + - { src: 'lego_timer', dst: 'lego.timer' } + notify: reload systemd daemons + + + - name: enable lego timer + systemd: + name: lego.timer + state: started + enabled: yes + + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' + + +# TODO: restart script \ No newline at end of file diff --git a/roles/caddy/tasks/register_acme_domain.yml b/roles/lego/tasks/register_acme_domain.yml similarity index 86% rename from roles/caddy/tasks/register_acme_domain.yml rename to roles/lego/tasks/register_acme_domain.yml index 5a6f8e3..2e7f49b 100644 --- a/roles/caddy/tasks/register_acme_domain.yml +++ b/roles/lego/tasks/register_acme_domain.yml @@ -1,6 +1,6 @@ - name: call acme-dns-client expect: - command: "./acme-dns-client register -d {{ domain | quote }} -s {{ acme_dns_server | quote }}" + command: "./acme-dns-client register -d {{ domain | quote }} -s {{ acme_dns_server_url | quote }}" chdir: "{{ caddy_acmedns_client_bin_dir }}" echo: yes responses: @@ -16,4 +16,4 @@ - name: pause if acme-dns-client registered a new record pause: - when: result.changed + when: result.changed and (interactive | d(true) == true) diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml index fbb8d96..69c8cf6 100644 --- a/roles/nginx/tasks/main.yml +++ b/roles/nginx/tasks/main.yml @@ -148,6 +148,7 @@ notify: restart nginx stapling: "{{ nginx_cfg.must_staple | d(nginx_cfg.enable_stapling) | d(false) }}" hosts: "{{ nginx_cfg.domains | d(None) }}" + acme_server: "{{ nginx_cfg.acme_server | d(None) }}" certs: - id: "{{ host_name ~ '-nginx-ecc' }}" cert: "{{ nginx_cfg.conf_dir }}/tls/{{ nginx_cfg.cert_ecc_name }}.crt" diff --git a/roles/postgres/defaults/main.yml b/roles/postgres/defaults/main.yml index 08f0150..bb0c62b 100644 --- a/roles/postgres/defaults/main.yml +++ b/roles/postgres/defaults/main.yml @@ -1,10 +1,9 @@ postgresql_user: postgres postgresql_group: postgres -postgresql_data_dir: /db +postgresql_data_dir: /opt/postgresql postgresql_conf_dir: /etc/postgresql -postgresql_tls_dir: "{{ postgresql_conf_dir }}/tls" -postgresql_dhparam_file: dhparam.pem +postgresql_tls_dir: "{{ (postgresql_conf_dir, 'tls') | path_join }}" postgresql_db: [] @@ -50,9 +49,9 @@ postgresql_default_config: log_timezone: "{{ timezone }}" timezone: "{{ timezone }}" - shared_buffers: "{{ ((hardware.memory | d(512) | int) * (1024/2)) | int }}kB" - work_mem: "{{ ((hardware.memory | d(512) | int) * (1024/35)) | round(1, 'ceil') | int }}kB" - max_wal_size: "{{ ((hardware.disk | d(2) | float) * (1024 / 2)) | int }}MB" + shared_buffers: "{{ ((host_hardware.memory | d(512) | int) * (1024/2)) | int }}kB" + work_mem: "{{ ((host_hardware.memory | d(512) | int) * (1024/35)) | round(1, 'ceil') | int }}kB" + max_wal_size: "{{ ((host_hardware.disk | d(2) | float) * (1024 / 2)) | int }}MB" postgresql_tls_config: @@ -61,7 +60,7 @@ postgresql_tls_config: ssl_prefer_server_ciphers: yes ssl_min_protocol_version: TLSv1.2 ssl_ecdh_curve: secp384r1 - ssl_ca_file: "{{ postgresql_tls_dir }}/root.crt" - ssl_cert_file: "{{ postgresql_tls_dir }}/ecc384.crt" - ssl_key_file: "{{ postgresql_tls_dir }}/ecc384.key" - ssl_dh_params_file: "{{ postgresql_tls_dir ~ '/' ~ postgresql_dhparam_file }}" + ssl_ca_file: "{{ (postgresql_tls_dir, 'root.crt') | path_join }}" + ssl_cert_file: "{{ (postgresql_tls_dir, 'ecc384.crt') | path_join }}" + ssl_key_file: "{{ (postgresql_tls_dir, 'ecc384.key') | path_join }}" + ssl_dh_params_file: "{{ (postgresql_tls_dir, 'dhparam.pem') | path_join }}" diff --git a/roles/postgres/tasks/add_database.yml b/roles/postgres/tasks/add_database.yml index bc46743..7392d63 100644 --- a/roles/postgres/tasks/add_database.yml +++ b/roles/postgres/tasks/add_database.yml @@ -56,7 +56,7 @@ - name: add line to postgres hba community.postgresql.postgresql_pg_hba: - dest: "{{ postgresql_conf_dir }}/pg_hba.conf" + dest: "{{ (postgresql_conf_dir, 'pg_hba.conf') | path_join }}" contype: "{{ 'host' if (database.ssl | d(false) == false) else 'hostssl' }}" databases: "{{ database.name }}" users: "{{ database.user }}" diff --git a/roles/postgres/tasks/install.yml b/roles/postgres/tasks/install.yml index a39ec65..b630546 100644 --- a/roles/postgres/tasks/install.yml +++ b/roles/postgres/tasks/install.yml @@ -1,6 +1,7 @@ - name: set pg_cfg set_fact: - pg_cfg: "{{ postgresql_default_config | d({}) | combine(postgresql_config | d({}), recursive=true) }}" + pg_cfg: "{{ postgresql_default_config | d({}) | + combine(postgresql_config | d({}), recursive=true) }}" - name: install dependencies @@ -29,6 +30,18 @@ group: "{{ postgresql_group }}" +- block: + - name: create data directory + file: + path: "{{ postgresql_data_dir }}" + state: directory + mode: 0700 + owner: "{{ postgresql_user }}" + group: "{{ postgresql_group }}" + rescue: + - meta: noop + + - name: include custom config in default postgres config lineinfile: path: "{{ postgresql_conf_dir }}/postgresql.conf" diff --git a/roles/postgres/tasks/integrate.yml b/roles/postgres/tasks/integrate.yml index 56f6a12..ff99058 100644 --- a/roles/postgres/tasks/integrate.yml +++ b/roles/postgres/tasks/integrate.yml @@ -1,19 +1,16 @@ - name: install postgres for self-hosted deployment - include_role: - name: postgres - vars: - function: install + include_tasks: install.yml when: database_self_hosted | d(false) == true - name: add database - include_role: - name: postgres + include_tasks: + file: add_database.yml apply: delegate_to: "{{ inventory_hostname if (database_self_hosted | d(false) == true) else services.db.hostname }}" vars: - function: add_database database: name: "{{ database_name }}" user: "{{ database_user }}" pass: "{{ database_pass }}" + self_hosted: "{{ database_self_hosted | d(false) }}" \ No newline at end of file diff --git a/roles/rproxy/tasks/add.yml b/roles/rproxy/tasks/add.yml new file mode 100644 index 0000000..c33f630 --- /dev/null +++ b/roles/rproxy/tasks/add.yml @@ -0,0 +1,5 @@ +- name: collect rproxy config + set_fact: + rproxy_collected_configs: "{{ (rproxy_collected_configs | d([])) + + ([rproxy_config] if rproxy_config is mapping else rproxy_config) }}" + when: rproxy_config is defined and ((rproxy_config | type_debug == 'list') or rproxy_config is mapping) diff --git a/roles/rproxy/tasks/main.yml b/roles/rproxy/tasks/main.yml new file mode 100644 index 0000000..c359548 --- /dev/null +++ b/roles/rproxy/tasks/main.yml @@ -0,0 +1,41 @@ +- block: + - name: fail if more than one reverse proxy config was collected + fail: + msg: more than one reverse proxy config was collected, this is not supported yet + when: rproxy_collected_configs | length > 1 + + + - name: install nginx + include_role: + name: nginx + vars: + nginx: + servers: + - conf: rproxy_collected_configs[0].nginx + certs: "{{ host_tls }}" + acme_server: "{{ (rproxy_collected_configs[0].acme | d({}))['server'] | d(None) }}" + when: reverse_proxy_type == 'nginx' + + + - name: install caddy + include_role: + name: caddy + vars: + caddy_config: "{{ rproxy_collected_configs[0].caddy | d({}) }}" + caddy_reverse_proxy_handlers: "{{ rproxy_collected_configs[0].caddy_reverse_proxy_handlers | d([]) }}" + when: reverse_proxy_type == 'caddy' + + + - name: add firewall entries + include_role: + name: iptables + tasks_from: add.yml + vars: + firewall_config: + filter: + input: + - { protocol: tcp, dst_port: [80, 443], action: accept } + - { protocol: udp, dst_port: [80, 443], 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/info.yml b/roles/vault/tasks/info.yml new file mode 100644 index 0000000..089639b --- /dev/null +++ b/roles/vault/tasks/info.yml @@ -0,0 +1,12 @@ +- name: set role information + set_fact: + role_dependency_index: 2 + + role_hardware: + cores: 4 + memory: 128 + swap: 64 + disk: 1.2 + + role_use_reverse_proxy: yes + role_use_database: yes diff --git a/roles/vault/tasks/main.yml b/roles/vault/tasks/main.yml index 5721586..f78ab4a 100644 --- a/roles/vault/tasks/main.yml +++ b/roles/vault/tasks/main.yml @@ -76,8 +76,8 @@ - name: move vaultwarden to vault dir copy: - src: "{{ (vault_extract_dir, 'output', 'valutwarden') | path_join }}" - dest: "{{ (vault_dir, 'valutwarden') | path_join }}" + src: "{{ (vault_extract_dir, 'output', 'vaultwarden') | path_join }}" + dest: "{{ (vault_dir, 'vaultwarden') | path_join }}" force: yes remote_src: yes owner: "{{ vault_user }}" @@ -94,7 +94,7 @@ - name: ensure vaultwarden has executable bit set file: - path: "{{ (vault_dir, 'valutwarden') | path_join }}" + path: "{{ (vault_dir, 'vaultwarden') | path_join }}" mode: "+x" @@ -106,7 +106,7 @@ location: github assets: yes asset_filter: '.tar.gz$' - file: "{{ vault_dir }}/last_version" + file: "{{ (vault_dir, 'last_version') | path_join }}" extract: "{{ vault_dir }}" user: "{{ vault_user }}" group: "{{ vault_group }}" @@ -132,6 +132,7 @@ force: yes mode: "+x" notify: restart vaultwarden + when: ansible_distribution == 'Alpine' - name: ensure correct ownership in vault dir @@ -145,20 +146,24 @@ notify: restart vaultwarden -- name: install and configure nginx - include_role: - name: nginx - vars: - nginx: - servers: - - conf: nginx_server - certs: "{{ host_tls }}" - - - name: flush handlers meta: flush_handlers +- name: add reverse proxy config + include_role: + name: rproxy + tasks_from: add.yml + vars: + rproxy_config: + port: "{{ vault_port }}" + nginx: rproxy_nginx.j2 + caddy_reverse_proxy_handlers: + - handler: reverse_proxy + upstreams: + - dial: "127.0.0.1:{{ vault_port }}" + + - name: add directories to backup plan include_role: name: backup diff --git a/roles/vault/templates/nginx_server.j2 b/roles/vault/templates/rproxy_nginx.j2 similarity index 100% rename from roles/vault/templates/nginx_server.j2 rename to roles/vault/templates/rproxy_nginx.j2