Fix fresh-deploy blockers and clean up architecture
- Seed postfix-accounts.cf before mailserver start to satisfy Dovecot's requirement for at least one account on first boot - Add failed_when: false to mail user/alias list tasks (files don't exist on first run) - Add forgejo_runner_version (was undefined); default to 12 - Create /srv/forgejo/data/gitea/conf before deploying app.ini - Decouple goaccess sync from restic: new enable_goaccess_sync flag with its own goaccess_sync_* variables - Move Docker installation to bootstrap exclusively; rename docker.yml to networks.yml (runs docker_network role only) - Add radicale_password to vault template and setup.sh - Fix goaccess sync tasks gated on enable_goaccess_sync - Add upstream bug comment to authorized_key deprecation warning - Update CLAUDE.md and README.md throughout Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
75891c3271
commit
b38cd94fc8
23 changed files with 400 additions and 307 deletions
|
|
@ -52,6 +52,11 @@ www.{{ site }} {
|
|||
|
||||
{% endfor %}
|
||||
{% if enable_mail | default(false) %}
|
||||
http://{{ mail_hostname }} {
|
||||
root * /var/www/acme
|
||||
file_server
|
||||
}
|
||||
|
||||
{{ webmail_domain }} {
|
||||
import access_log
|
||||
reverse_proxy rainloop:{{ rainloop_port }}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ services:
|
|||
- /srv/caddy/config:/config
|
||||
- /srv/caddy/sites:/srv/sites:ro
|
||||
- /srv/goaccess/reports:/srv/goaccess/reports:ro
|
||||
- /var/www/acme:/var/www/acme:ro
|
||||
environment:
|
||||
{% if enable_goaccess | default(true) %}
|
||||
GOACCESS_USER: "{{ goaccess_user }}"
|
||||
|
|
|
|||
56
roles/dns/tasks/extra_mail_domain.yml
Normal file
56
roles/dns/tasks/extra_mail_domain.yml
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
- name: "{{ extra_domain }} A record"
|
||||
hetzner.hcloud.zone_rrset:
|
||||
zone: "{{ extra_domain }}"
|
||||
name: "@"
|
||||
type: A
|
||||
ttl: 300
|
||||
records:
|
||||
- value: "{{ server_ip }}"
|
||||
api_token: "{{ hcloud_token }}"
|
||||
state: present
|
||||
|
||||
- name: "{{ extra_domain }} MX record"
|
||||
hetzner.hcloud.zone_rrset:
|
||||
zone: "{{ extra_domain }}"
|
||||
name: "@"
|
||||
type: MX
|
||||
ttl: 300
|
||||
records:
|
||||
- value: "10 {{ mail_hostname }}."
|
||||
api_token: "{{ hcloud_token }}"
|
||||
state: present
|
||||
|
||||
- name: "{{ extra_domain }} SPF record"
|
||||
hetzner.hcloud.zone_rrset:
|
||||
zone: "{{ extra_domain }}"
|
||||
name: "@"
|
||||
type: TXT
|
||||
ttl: 300
|
||||
records:
|
||||
- value: "{{ 'v=spf1 mx -all' | hetzner.hcloud.txt_record }}"
|
||||
api_token: "{{ hcloud_token }}"
|
||||
state: present
|
||||
|
||||
- name: "{{ extra_domain }} DMARC record"
|
||||
hetzner.hcloud.zone_rrset:
|
||||
zone: "{{ extra_domain }}"
|
||||
name: _dmarc
|
||||
type: TXT
|
||||
ttl: 300
|
||||
records:
|
||||
- value: "{{ ('v=DMARC1; p=none; rua=mailto:dmarc@' + extra_domain) | hetzner.hcloud.txt_record }}"
|
||||
api_token: "{{ hcloud_token }}"
|
||||
state: present
|
||||
|
||||
- name: "{{ extra_domain }} DKIM record"
|
||||
hetzner.hcloud.zone_rrset:
|
||||
zone: "{{ extra_domain }}"
|
||||
name: mail._domainkey
|
||||
type: TXT
|
||||
ttl: 300
|
||||
records:
|
||||
- value: "{{ dkim_keys[extra_domain] | hetzner.hcloud.txt_record }}"
|
||||
api_token: "{{ hcloud_token }}"
|
||||
state: present
|
||||
when: dkim_keys is defined and extra_domain in dkim_keys
|
||||
|
|
@ -22,4 +22,39 @@
|
|||
loop: "{{ dns_zones | subelements('records') }}"
|
||||
loop_control:
|
||||
label: "{{ item.0.zone }} {{ item.1.name }} {{ item.1.type }}"
|
||||
when: item.1.when | default(true) | bool
|
||||
tags: dns
|
||||
|
||||
- name: Ensure extra mail domain zones exist
|
||||
hetzner.hcloud.zone:
|
||||
name: "{{ item }}"
|
||||
mode: primary
|
||||
api_token: "{{ hcloud_token }}"
|
||||
state: present
|
||||
loop: "{{ mail_domains | difference([domain]) }}"
|
||||
when: enable_mail
|
||||
tags: dns
|
||||
|
||||
- name: Configure extra mail domain DNS records
|
||||
ansible.builtin.include_tasks: extra_mail_domain.yml
|
||||
vars:
|
||||
extra_domain: "{{ item }}"
|
||||
loop: "{{ mail_domains | difference([domain]) }}"
|
||||
when: enable_mail
|
||||
tags: dns
|
||||
|
||||
- name: Manage DKIM records
|
||||
hetzner.hcloud.zone_rrset:
|
||||
zone: "{{ item.key }}"
|
||||
name: mail._domainkey
|
||||
type: TXT
|
||||
ttl: 300
|
||||
records:
|
||||
- value: "{{ item.value | hetzner.hcloud.txt_record }}"
|
||||
api_token: "{{ hcloud_token }}"
|
||||
state: present
|
||||
loop: "{{ dkim_keys | default({}) | dict2items }}"
|
||||
loop_control:
|
||||
label: "{{ item.key }} mail._domainkey TXT"
|
||||
when: enable_mail | default(false) and item.value | length > 0
|
||||
tags: dns
|
||||
|
|
|
|||
|
|
@ -22,5 +22,6 @@ forgejo_mailer_enabled: false
|
|||
# forgejo_smtp_password: defined in vault.yml
|
||||
|
||||
# Actions runner
|
||||
forgejo_runner_version: "12"
|
||||
forgejo_runner_name: default-runner
|
||||
forgejo_runner_labels: "docker:docker://node:20-bookworm,ubuntu-latest:docker://ubuntu:latest,ubuntu-22.04:docker://ubuntu:22.04"
|
||||
|
|
|
|||
|
|
@ -13,11 +13,14 @@
|
|||
loop:
|
||||
- /srv/forgejo
|
||||
|
||||
- name: Create Forgejo data directory
|
||||
- name: Create Forgejo data directories
|
||||
ansible.builtin.file:
|
||||
path: /srv/forgejo/data
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
loop:
|
||||
- /srv/forgejo/data
|
||||
- /srv/forgejo/data/gitea/conf
|
||||
|
||||
# stat+chown: avoids UID/GID lookup warnings for container-internal UIDs not present on host
|
||||
- name: Stat Forgejo data directory
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@
|
|||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
when: enable_goaccess_sync | default(false)
|
||||
|
||||
- name: Deploy sync systemd service
|
||||
ansible.builtin.template:
|
||||
|
|
@ -68,6 +69,7 @@
|
|||
group: root
|
||||
mode: "0644"
|
||||
notify: Reload systemd
|
||||
when: enable_goaccess_sync | default(false)
|
||||
|
||||
- name: Deploy sync systemd timer
|
||||
ansible.builtin.template:
|
||||
|
|
@ -77,15 +79,20 @@
|
|||
group: root
|
||||
mode: "0644"
|
||||
notify: Reload systemd
|
||||
when: enable_goaccess_sync | default(false)
|
||||
|
||||
- name: Flush handlers to reload systemd
|
||||
ansible.builtin.meta: flush_handlers
|
||||
|
||||
- name: Enable and start GoAccess timers
|
||||
- name: Enable and start GoAccess report timer
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
name: goaccess-report.timer
|
||||
enabled: true
|
||||
state: started
|
||||
loop:
|
||||
- goaccess-report.timer
|
||||
- goaccess-sync.timer
|
||||
|
||||
- name: Enable and start GoAccess sync timer
|
||||
ansible.builtin.systemd:
|
||||
name: goaccess-sync.timer
|
||||
enabled: true
|
||||
state: started
|
||||
when: enable_goaccess_sync | default(false)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@
|
|||
set -euo pipefail
|
||||
|
||||
rsync -az --delete \
|
||||
-e "ssh -i {{ restic_ssh_key }} -p {{ restic_ssh_port }} -o StrictHostKeyChecking=no -o BatchMode=yes" \
|
||||
-e "ssh -i {{ goaccess_sync_ssh_key }} -p {{ goaccess_sync_ssh_port }} -o StrictHostKeyChecking=no -o BatchMode=yes" \
|
||||
/srv/goaccess/reports/ \
|
||||
{{ restic_user }}@{{ restic_host }}:analytics/
|
||||
{{ goaccess_sync_user }}@{{ goaccess_sync_host }}:{{ goaccess_sync_remote_path }}/
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
# read-only docker exec always reports changed; changed_when: false suppresses spurious output
|
||||
# failed_when: false — postfix-virtual.cf may not exist on first run
|
||||
- name: List existing mail aliases
|
||||
command: docker exec mailserver setup alias list
|
||||
register: mail_alias_list
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
tags:
|
||||
- users
|
||||
|
||||
|
|
|
|||
|
|
@ -92,15 +92,32 @@
|
|||
name: certbot
|
||||
state: present
|
||||
|
||||
- name: Check if mail TLS certificate already exists
|
||||
ansible.builtin.stat:
|
||||
path: /etc/letsencrypt/live/{{ mail_hostname }}/fullchain.pem
|
||||
register: mail_cert
|
||||
|
||||
- name: Stop Caddy to free port 80 for certbot
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /srv/caddy
|
||||
state: stopped
|
||||
when: not mail_cert.stat.exists
|
||||
|
||||
- name: Obtain a Let's Encrypt certificate for {{ mail_hostname }}
|
||||
command: >
|
||||
certbot certonly --standalone
|
||||
-d {{ mail_hostname }}
|
||||
--non-interactive --agree-tos -m postmaster@{{ domain }}
|
||||
args:
|
||||
creates: /etc/letsencrypt/live/{{ mail_hostname }}/fullchain.pem
|
||||
when: not mail_cert.stat.exists
|
||||
tags: config
|
||||
|
||||
- name: Restart Caddy after certbot
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /srv/caddy
|
||||
state: present
|
||||
build: never
|
||||
when: not mail_cert.stat.exists
|
||||
|
||||
- name: Deploy mail compose file
|
||||
template:
|
||||
src: compose.yml.j2
|
||||
|
|
@ -126,6 +143,17 @@
|
|||
notify: Restart mailserver
|
||||
tags: config
|
||||
|
||||
- name: Seed mail accounts into postfix-accounts.cf before first start
|
||||
ansible.builtin.shell: |
|
||||
grep -qF "{{ item.address }}" /srv/mail/config/postfix-accounts.cf 2>/dev/null && exit 0
|
||||
hash=$(openssl passwd -6 {{ item.password | quote }})
|
||||
printf '%s|{SHA512-CRYPT}%s\n' "{{ item.address }}" "${hash}" >> /srv/mail/config/postfix-accounts.cf
|
||||
loop: "{{ mail_users }}"
|
||||
no_log: true
|
||||
args:
|
||||
executable: /bin/bash
|
||||
tags: users
|
||||
|
||||
- name: Start mailserver
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /srv/mail
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
# read-only docker exec always reports changed; changed_when: false suppresses spurious output
|
||||
# failed_when: false — postfix-accounts.cf may not exist on first run (seeded separately)
|
||||
- name: Check if mail user exists
|
||||
command: docker exec mailserver setup email list
|
||||
register: mail_user_list
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
tags:
|
||||
- users
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
hcloud_server_type: cx22
|
||||
cloud_provider: hetzner
|
||||
hcloud_server_type: cx23
|
||||
hcloud_image: ubuntu-24.04
|
||||
hcloud_location: fsn1
|
||||
|
|
|
|||
|
|
@ -30,11 +30,18 @@
|
|||
|
||||
- name: Update inventory with new IP
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ inventory_dir }}/hosts.yml"
|
||||
path: "{{ lookup('env', 'LINDERHOF_DIR') }}/hosts.yml"
|
||||
regexp: '^\s+ansible_host:'
|
||||
line: " ansible_host: {{ server_ip }}"
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Update config with new IP
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ lookup('env', 'LINDERHOF_DIR') }}/group_vars/all/config.yml"
|
||||
regexp: '^server_ip:'
|
||||
line: "server_ip: {{ server_ip }}"
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Print server IP
|
||||
ansible.builtin.debug:
|
||||
msg: "Server '{{ server_name }}' provisioned at {{ server_ip }}"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue