From 5ca943312c70d18382946708043a3194bc399237 Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 4 Jan 2023 19:18:16 +0300 Subject: [PATCH] feat: caddy fix: various fixes --- ansible.cfg | 2 + group_vars/all.yml | 1 + roles/caddy/defaults/main.yml | 53 +++++++ roles/caddy/handlers/main.yml | 9 ++ roles/caddy/tasks/build_caddy.yml | 131 +++++++++++++++ roles/caddy/tasks/install_prebuilt_caddy.yml | 41 +++++ roles/caddy/tasks/main.yml | 141 +++++++++++++++++ roles/caddy/tasks/register_acme_domain.yml | 19 +++ roles/caddy/tasks/setup_acme_client.yml | 88 +++++++++++ roles/caddy/tasks/setup_lego.yml | 158 +++++++++++++++++++ roles/caddy/templates/caddy.j2 | 1 + roles/caddy/templates/init.j2 | 23 +++ roles/caddy/vars/reverse_proxy.yml | 27 ++++ roles/caddy/vars/tls_caddy.yml | 33 ++++ roles/caddy/vars/tls_lego.yml | 8 + roles/coredns/tasks/info.yml | 6 + roles/iptables/defaults/main.yml | 23 ++- roles/iptables/tasks/main.yml | 7 +- roles/iptables/templates/iptables.j2 | 4 +- roles/vault/defaults/main.yml | 4 +- roles/vault/tasks/main.yml | 22 +-- roles/vault/templates/init.j2 | 4 +- tasks/get_host_arch.yml | 7 + 23 files changed, 794 insertions(+), 18 deletions(-) create mode 100644 roles/caddy/defaults/main.yml create mode 100644 roles/caddy/handlers/main.yml create mode 100644 roles/caddy/tasks/build_caddy.yml create mode 100644 roles/caddy/tasks/install_prebuilt_caddy.yml create mode 100644 roles/caddy/tasks/main.yml create mode 100644 roles/caddy/tasks/register_acme_domain.yml create mode 100644 roles/caddy/tasks/setup_acme_client.yml create mode 100644 roles/caddy/tasks/setup_lego.yml create mode 100644 roles/caddy/templates/caddy.j2 create mode 100644 roles/caddy/templates/init.j2 create mode 100644 roles/caddy/vars/reverse_proxy.yml create mode 100644 roles/caddy/vars/tls_caddy.yml create mode 100644 roles/caddy/vars/tls_lego.yml create mode 100644 tasks/get_host_arch.yml diff --git a/ansible.cfg b/ansible.cfg index a83d8df..b4c75bc 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -5,6 +5,8 @@ use_persistent_connections = true forks = 6 internal_poll_interval = 0.01 jinja2_native = true +no_target_syslog = true +verbosity = 1 [ssh_connection] pipelining = true diff --git a/group_vars/all.yml b/group_vars/all.yml index 1f4d11b..f1ac5e8 100644 --- a/group_vars/all.yml +++ b/group_vars/all.yml @@ -41,3 +41,4 @@ role_dependency_common: - {stage: 5, role: 'mail-user'} - {stage: 8, role: 'iptables'} - {stage: 9, role: 'backup', function: 'setup'} + diff --git a/roles/caddy/defaults/main.yml b/roles/caddy/defaults/main.yml new file mode 100644 index 0000000..168d797 --- /dev/null +++ b/roles/caddy/defaults/main.yml @@ -0,0 +1,53 @@ +caddy_user: caddy +caddy_group: caddy + +caddy_conf_dir: /etc/caddy +caddy_asset_dir: /opt/caddy-assets +caddy_bin_dir: /usr/sbin +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_domains: + - "{{ host_fqdn }}" + +caddy_acme_endpoint: https://acme-staging-v02.api.letsencrypt.org/directory + +caddy_default_config: + admin: + disabled: yes + logging: + sink: + writer: + output: stdout + logs: + default: + writer: + output: stdout + encoder: + format: console + level: INFO + storage: + module: file_system + root: "{{ caddy_asset_dir }}" + apps: + tls: + session_tickets: + rotation_interval: 4h + max_keys: 8 + cache: + capacity: 512 + http: + grace_period: 20s diff --git a/roles/caddy/handlers/main.yml b/roles/caddy/handlers/main.yml new file mode 100644 index 0000000..e2a1091 --- /dev/null +++ b/roles/caddy/handlers/main.yml @@ -0,0 +1,9 @@ +- name: restart caddy + 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 new file mode 100644 index 0000000..42a9686 --- /dev/null +++ b/roles/caddy/tasks/build_caddy.yml @@ -0,0 +1,131 @@ +- name: install xcaddy from debian/ubuntu repository + block: + - name: install dependencies + package: + name: + - debian-keyring + - debian-archive-keyring + - apt-transport-https + + + - name: get xcaddy signing key location + set_fact: + caddy_xcaddy_signing_key_file: "{{ (gpg_keyrings_dir, 'xcaddy.asc') | path_join }}" + + + - name: add xcaddy signing key + get_url: + url: "{{ caddy_xcaddy_gpg_key_url }}" + dest: "{{ caddy_xcaddy_signing_key_file }}" + mode: a+r + + + - name: add apt repo + apt_repository: + repo: "deb [signed-by={{ caddy_xcaddy_signing_key_file | quote }}] {{ caddy_xcaddy_repo_url }} any-version main" + register: result + + + - name: update repository index + apt: + force_apt_get: yes + update_cache: yes + changed_when: no + when: result.changed + + + - name: install xcaddy + package: + name: xcaddy + + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' + + +- name: install xcaddy from github + block: + - name: determine host architecture + include_tasks: tasks/get_host_arch.yml + + + - name: create xcaddy dir + file: + path: "{{ caddy_xcaddy_dir }}" + state: directory + + + - name: get latest xcaddy version + include_tasks: tasks/get_lastversion.yml + vars: + package: + name: caddyserver/xcaddy + location: github + file: "{{ (caddy_conf_dir, 'last_xcaddy_version') | path_join }}" + assets: yes + asset_filter: "{{ 'linux_' ~ host_architecture ~ '.tar.gz$' }}" + extract: "{{ caddy_xcaddy_dir }}" + + + - name: ensure xcaddy binary has executable flag set + file: + path: "{{ (caddy_xcaddy_dir, 'xcaddy') | path_join }}" + mode: "+x" + + + - name: copy xcaddy to bin dir + copy: + src: "{{ (caddy_xcaddy_dir, 'xcaddy') | path_join }}" + dest: "{{ (caddy_bin_dir, 'xcaddy') | path_join }}" + remote_src: yes + mode: "+x" + + when: ansible_distribution == 'Alpine' + + +- name: install golang + package: + name: golang + + +- name: get latest caddy version + include_tasks: tasks/get_lastversion.yml + vars: + package: + name: caddyserver/caddy + location: github + file: "{{ (caddy_conf_dir, 'last_caddy_version') | path_join }}" + + +- name: generate build command + set_fact: + caddy_build_command: >- + {{ + 'xcaddy build ' ~ ( + [] | zip_longest((caddy_default_plugins | d([])) + + (caddy_custom_plugins | d([])) | select() | map('quote'), fillvalue='--with ') + | map('join') | list | join(' ') + ) ~ ' --output ' ~ ((caddy_bin_dir, 'caddy') | path_join | quote) }} + + +- name: save build command to a file + copy: + content: "{{ caddy_build_command }}" + dest: "{{ (caddy_conf_dir, 'build_info') | path_join }}" + mode: 0400 + register: result + + +- name: build caddy binary if a new version is found or build command was changed + shell: + cmd: "{{ caddy_build_command }}" + chdir: "{{ caddy_bin_dir }}" + register: result + when: package_changed or result.changed + notify: restart caddy + + +- name: check if caddy version and last version are identical + shell: + cmd: "caddy version | cut -d ' ' -f1 | cut -b2- | diff -wq {{ (caddy_conf_dir, 'last_caddy_version') | path_join | quote }} -" + register: result + failed_when: result.rc != 0 + changed_when: no diff --git a/roles/caddy/tasks/install_prebuilt_caddy.yml b/roles/caddy/tasks/install_prebuilt_caddy.yml new file mode 100644 index 0000000..892c9bb --- /dev/null +++ b/roles/caddy/tasks/install_prebuilt_caddy.yml @@ -0,0 +1,41 @@ +- name: install caddy from debian/ubuntu repository + block: + - name: install dependencies + package: + name: + - debian-keyring + - debian-archive-keyring + - apt-transport-https + + + - name: get caddy signing key location + set_fact: + caddy_signing_key_file: "{{ (gpg_keyrings_dir, 'caddy.asc') | path_join }}" + + + - name: add caddy signing key + get_url: + url: "{{ caddy_gpg_key_url }}" + dest: "{{ caddy_signing_key_file }}" + mode: a+r + + + - name: add apt repo + apt_repository: + repo: "deb [signed-by={{ caddy_signing_key_file | quote }}] {{ caddy_repo_url }} any-version main" + register: result + + + - name: update repository index + apt: + force_apt_get: yes + update_cache: yes + changed_when: no + when: result.changed + + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' + + +- name: install caddy + package: + name: caddy diff --git a/roles/caddy/tasks/main.yml b/roles/caddy/tasks/main.yml new file mode 100644 index 0000000..bb177c6 --- /dev/null +++ b/roles/caddy/tasks/main.yml @@ -0,0 +1,141 @@ +- 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 + 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 + + +- name: import reverse proxy vars + include_vars: + file: reverse_proxy.yml + when: caddy_has_reverse_proxy + + +- 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_config | d({}), recursive=true) }}" + + +- name: create user and group + include_tasks: tasks/create_user.yml + vars: + user: + name: "{{ caddy_user }}" + group: "{{ caddy_group }}" + dir: "{{ caddy_conf_dir }}" + create_home: no + + +- name: create caddy directories + file: + path: "{{ item }}" + state: directory + owner: "{{ caddy_user }}" + group: "{{ caddy_group }}" + loop: + - "{{ caddy_conf_dir }}" + - "{{ caddy_asset_dir }}" + + +- name: create caddy bin dir + file: + path: "{{ caddy_bin_dir }}" + state: directory + + +- name: build caddy + include_tasks: build_caddy.yml + when: caddy_custom_build + + +- name: install prebuilt caddy + include_tasks: install_prebuilt_caddy.yml + 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 + 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 + dest: /etc/systemd/system/caddy.service + force: yes + lstrip_blocks: yes + notify: + - reload systemd daemons + - restart caddy + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' + + +- name: template init script + template: + src: init.j2 + dest: /etc/init.d/caddy + force: yes + mode: 0755 + notify: restart caddy + when: ansible_distribution == 'Alpine' + + +- name: change permissions on asset dir contents + file: + path: "{{ caddy_asset_dir }}" + recurse: yes + owner: "{{ caddy_user }}" + group: "{{ caddy_group }}" + notify: restart caddy + + +- name: flush handlers + meta: flush_handlers + + +- name: add directories to backup plan + include_role: + name: backup + tasks_from: add.yml + vars: + backup_items: + - "{{ caddy_asset_dir }}" + - "{{ caddy_conf_dir }}" + + +- name: enable and start caddy + service: + name: caddy + enabled: yes + state: started diff --git a/roles/caddy/tasks/register_acme_domain.yml b/roles/caddy/tasks/register_acme_domain.yml new file mode 100644 index 0000000..5a6f8e3 --- /dev/null +++ b/roles/caddy/tasks/register_acme_domain.yml @@ -0,0 +1,19 @@ +- name: call acme-dns-client + expect: + command: "./acme-dns-client register -d {{ domain | quote }} -s {{ acme_dns_server | 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 diff --git a/roles/caddy/tasks/setup_acme_client.yml b/roles/caddy/tasks/setup_acme_client.yml new file mode 100644 index 0000000..46453ec --- /dev/null +++ b/roles/caddy/tasks/setup_acme_client.yml @@ -0,0 +1,88 @@ +- 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/caddy/tasks/setup_lego.yml b/roles/caddy/tasks/setup_lego.yml new file mode 100644 index 0000000..ad52960 --- /dev/null +++ b/roles/caddy/tasks/setup_lego.yml @@ -0,0 +1,158 @@ +- 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/caddy.j2 b/roles/caddy/templates/caddy.j2 new file mode 100644 index 0000000..6ea0add --- /dev/null +++ b/roles/caddy/templates/caddy.j2 @@ -0,0 +1 @@ +{{ caddy_cfg | to_nice_json(indent=2) }} diff --git a/roles/caddy/templates/init.j2 b/roles/caddy/templates/init.j2 new file mode 100644 index 0000000..8ecc368 --- /dev/null +++ b/roles/caddy/templates/init.j2 @@ -0,0 +1,23 @@ +#!/sbin/openrc-run + +: ${caddy_opts:="--config {{ caddy_conf_file | quote }}"} + +name="$SVCNAME" +directory="{{ caddy_conf_dir }}" +command=/usr/sbin/caddy +command_args="run --environ $caddy_opts" +command_user="{{ caddy_user ~ ':' ~ caddy_group }}" +pidfile="/var/run/$SVCNAME.pid" +command_background=true +extra_started_commands="reload" + +depend() { + need net localmount + after firewall +} + +reload() { + ebegin "Reloading $SVCNAME" + su ${command_user%:*} -s /bin/sh -c "$command reload $caddy_opts" + eend $? +} diff --git a/roles/caddy/vars/reverse_proxy.yml b/roles/caddy/vars/reverse_proxy.yml new file mode 100644 index 0000000..0c11d0a --- /dev/null +++ b/roles/caddy/vars/reverse_proxy.yml @@ -0,0 +1,27 @@ +caddy_reverse_proxy_config: + apps: + http: + servers: + rproxy: + listen: + - "tcp4/:443" + - "tcp6/:443" + automatic_https: + disable: "{{ caddy_use_lego }}" + tls_connection_policies: + - match: + sni: + - "{{ host_fqdn }}" + default_sni: "{{ host_fqdn }}" + routes: + - match: + - host: + - "{{ host_fqdn }}" + handle: + - handler: subroute + routes: + - handle: + - handler: reverse_proxy + upstreams: + - dial: "127.0.0.1:{{ reverse_proxy_port }}" + terminal: true diff --git a/roles/caddy/vars/tls_caddy.yml b/roles/caddy/vars/tls_caddy.yml new file mode 100644 index 0000000..ff1cda9 --- /dev/null +++ b/roles/caddy/vars/tls_caddy.yml @@ -0,0 +1,33 @@ +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/caddy/vars/tls_lego.yml b/roles/caddy/vars/tls_lego.yml new file mode 100644 index 0000000..2a92368 --- /dev/null +++ b/roles/caddy/vars/tls_lego.yml @@ -0,0 +1,8 @@ +caddy_unmanaged_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 }}" + tags: "{{ caddy_domains }}" diff --git a/roles/coredns/tasks/info.yml b/roles/coredns/tasks/info.yml index 6633b95..b013448 100644 --- a/roles/coredns/tasks/info.yml +++ b/roles/coredns/tasks/info.yml @@ -11,3 +11,9 @@ memory: 128 swap: 64 disk: 0.3 + + role_firewall_config: + filter: + input: + - { protocol: tcp, dst_port: [53, 443, 853], action: accept } + - { protocol: udp, dst_port: [53, 443, 853], action: accept } diff --git a/roles/iptables/defaults/main.yml b/roles/iptables/defaults/main.yml index 8caa2a1..123db06 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" +iptables_file: "{{ (iptables_dir, 'rules-save') | path_join }}" iptables_mappings: state: { module: 'state', param: 'state', upper: yes, join: ',' } @@ -20,3 +20,24 @@ iptables_mappings: set_mss: { param: 'set-mss' } to_source: { param: 'to-source' } + + +firewall_default_config: + filter: + default_policy: + input: drop + forward: drop + output: accept + input: + - { state: ['established', 'related'], action: accept } + - { state: invalid, action: drop } + - { protocol: icmp, icmp_type: 8, action: accept } + - { in_intf: lo, action: accept } + - { not_in_intf: lo, src_addr: '127.0.0.0/8', action: drop } + + +firewall_ssh_config: + filter: + input: + - "{{ { 'protocol': 'tcp', 'dst_port': 22, 'src_addr': admin_net | d(int_net | d('0.0.0.0/0')), 'action': 'accept' } }}" + - "{{ { 'protocol': 'tcp', 'dst_port': 22, 'src_addr': hostvars['ansible']['ansible_host'], 'action': 'accept' } if hostvars['ansible'] is defined else None }}" diff --git a/roles/iptables/tasks/main.yml b/roles/iptables/tasks/main.yml index 0547b8e..aa37003 100644 --- a/roles/iptables/tasks/main.yml +++ b/roles/iptables/tasks/main.yml @@ -1,7 +1,10 @@ - block: - name: set firewall_cfg set_fact: - firewall_cfg: "{{ firewall_default_config | d({}) | combine(firewall | d({}), recursive=true) }}" + 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 | d({}), recursive=true, list_merge='append') }}" - name: install iptables @@ -44,4 +47,4 @@ enabled: yes state: started - when: firewall is mapping + 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/iptables/templates/iptables.j2 b/roles/iptables/templates/iptables.j2 index b3ac261..afad9c2 100644 --- a/roles/iptables/templates/iptables.j2 +++ b/roles/iptables/templates/iptables.j2 @@ -49,7 +49,9 @@ {% if section.key != 'default_policy' -%} {% if section.value | type_debug == 'list' -%} {% for rule in section.value -%} - {{ iptables_rule(section.key, rule) }} + {% if rule is mapping and rule != None -%} + {{ iptables_rule(section.key, rule) }} + {% endif -%} {% endfor -%} {% elif section.value is mapping -%} {{ iptables_rule(section.key, section.value) }} diff --git a/roles/vault/defaults/main.yml b/roles/vault/defaults/main.yml index 9090715..bafbc6c 100644 --- a/roles/vault/defaults/main.yml +++ b/roles/vault/defaults/main.yml @@ -14,8 +14,8 @@ vault_default_config: rocket_port: "{{ vault_port }}" websocket_port: "{{ vault_websocket_port }}" - org_attachment_limit: "{{ ((hardware.disk | d(10) | float) * 1024 * 1024 / 30) | int | abs }}" - user_attachment_limit: "{{ ((hardware.disk | d(10) | float) * 1024 * 1024 / 90) | int | abs }}" + org_attachment_limit: "{{ ((host_hardware.disk | d(10) | float) * 1024 * 1024 / 30) | int | abs }}" + user_attachment_limit: "{{ ((host_hardware.disk | d(10) | float) * 1024 * 1024 / 90) | int | abs }}" database_max_conns: 4 websocket_enabled: yes diff --git a/roles/vault/tasks/main.yml b/roles/vault/tasks/main.yml index b386669..5721586 100644 --- a/roles/vault/tasks/main.yml +++ b/roles/vault/tasks/main.yml @@ -7,7 +7,9 @@ - name: set vault_cfg set_fact: - vault_cfg: "{{ vault_default_config | d({}) | combine(vault_mail_config | d({}), recursive=true) | combine(vault_config | d({}), recursive=true) }}" + vault_cfg: "{{ vault_default_config | d({}) | + combine(vault_mail_config | d({}), recursive=true) | + combine(vault_config | d({}), recursive=true) }}" - name: install curl @@ -30,7 +32,7 @@ - name: create data directory file: - path: "{{ vault_dir }}/data" + path: "{{ (vault_dir, 'data') | path_join }}" state: directory mode: 0750 owner: "{{ vault_user }}" @@ -53,7 +55,7 @@ - name: run docker-image-extract command: - cmd: "{{ vault_extract_dir }}/docker-image-extract vaultwarden/server:alpine" + cmd: "{{ (vault_extract_dir, 'docker-image-extract') | path_join }} vaultwarden/server:alpine" chdir: "{{ vault_extract_dir }}" register: result changed_when: no @@ -62,7 +64,7 @@ - name: check if output directory exists stat: - path: "{{ vault_extract_dir }}/output" + path: "{{ (vault_extract_dir, 'output') | path_join }}" register: result @@ -74,8 +76,8 @@ - name: move vaultwarden to vault dir copy: - src: "{{ vault_extract_dir ~ '/output/vaultwarden' }}" - dest: "{{ vault_dir ~ '/vaultwarden' }}" + src: "{{ (vault_extract_dir, 'output', 'valutwarden') | path_join }}" + dest: "{{ (vault_dir, 'valutwarden') | path_join }}" force: yes remote_src: yes owner: "{{ vault_user }}" @@ -85,14 +87,14 @@ - name: remove output directory file: - path: "{{ vault_extract_dir }}/output" + path: "{{ (vault_extract_dir, 'output') | path_join }}" state: absent changed_when: no - name: ensure vaultwarden has executable bit set file: - path: "{{ vault_dir }}/vaultwarden" + path: "{{ (vault_dir, 'valutwarden') | path_join }}" mode: "+x" @@ -114,7 +116,7 @@ - name: template .env file template: src: env.j2 - dest: "{{ vault_dir }}/.env" + dest: "{{ (vault_dir, '.env') | path_join }}" force: yes mode: 0400 owner: "{{ vault_user }}" @@ -160,8 +162,8 @@ - name: add directories to backup plan include_role: name: backup + tasks_from: add.yml vars: - function: add backup_items: - "{{ vault_dir }}" diff --git a/roles/vault/templates/init.j2 b/roles/vault/templates/init.j2 index 08eabeb..7e523c8 100644 --- a/roles/vault/templates/init.j2 +++ b/roles/vault/templates/init.j2 @@ -2,7 +2,7 @@ name="$SVCNAME" directory="{{ vault_dir }}" -command="{{ vault_dir }}/vaultwarden" +command="{{ (vault_dir, 'vaultwarden') | path_join }}" command_user="{{ vault_user ~ ':' ~ vault_group }}" pidfile="/var/run/$SVCNAME.pid" {% if vault_supervised | d(false) == true -%} @@ -14,6 +14,6 @@ command_background=true depend() { need net use dns - before nginx + before nginx caddy after postgresql mariadb } diff --git a/tasks/get_host_arch.yml b/tasks/get_host_arch.yml new file mode 100644 index 0000000..342ad89 --- /dev/null +++ b/tasks/get_host_arch.yml @@ -0,0 +1,7 @@ +- name: determine host architecture + set_fact: + host_architecture: "{{ [ansible_architecture] | map('extract', { + 'aarch64': 'arm64', + 'x86_64': 'amd64', + 'i386': '386' + }) | first }}"