Document network operations and WireGuard maintenance
This commit is contained in:
@@ -45,6 +45,17 @@ all:
|
||||
backup:
|
||||
compose_args: [--no-deps, --force-recreate]
|
||||
service_reconcile_traefik_restart_targets: [poweradmin]
|
||||
us4:
|
||||
ansible_host: us4.wsvc.info
|
||||
ansible_host_ipv4: 185.201.226.122
|
||||
service_role: wireguard
|
||||
compose_project_dir: /opt/wireguard
|
||||
healthcheck_profile: wireguard
|
||||
wireguard_image: >-
|
||||
lscr.io/linuxserver/wireguard@sha256:ac43e1226878d2611315172d6ea357a95cb326ee73124b91108118efc8666889
|
||||
service_reconcile_services:
|
||||
wireguard:
|
||||
compose_args: [--no-deps, --force-recreate]
|
||||
mailcow:
|
||||
hosts:
|
||||
mx2:
|
||||
@@ -54,11 +65,15 @@ all:
|
||||
powerdns:
|
||||
hosts:
|
||||
hk2:
|
||||
wireguard:
|
||||
hosts:
|
||||
us4:
|
||||
docker_hosts:
|
||||
children:
|
||||
mailcow:
|
||||
vaultwarden:
|
||||
powerdns:
|
||||
wireguard:
|
||||
# Matrix is a dedicated K3s node and intentionally remains outside the
|
||||
# Docker-oriented managed group.
|
||||
k3s_servers:
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
# This owns the non-secret Compose declaration only. WireGuard key material
|
||||
# remains under the server-local /opt/wireguard/config bind mount.
|
||||
- name: Apply the reviewed WireGuard Compose hardening
|
||||
hosts: wireguard
|
||||
become: true
|
||||
gather_facts: false
|
||||
serial: 1
|
||||
vars:
|
||||
wireguard_harden_confirm: false
|
||||
wireguard_compose_candidate: "{{ compose_project_dir }}/compose.yml.candidate"
|
||||
tasks:
|
||||
- name: Require explicit WireGuard hardening confirmation
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- wireguard_harden_confirm | bool
|
||||
fail_msg: >-
|
||||
This operation replaces the non-secret Compose declaration. Run only
|
||||
during the approved maintenance window after a rollback archive exists.
|
||||
|
||||
- name: Verify that a protected pre-change archive exists
|
||||
ansible.builtin.find:
|
||||
paths: "{{ compose_project_dir }}/backups"
|
||||
patterns: 'pre-hardening-*.tar.gz'
|
||||
file_type: file
|
||||
register: wireguard_pre_hardening_archives
|
||||
|
||||
- name: Require a pre-change rollback archive
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- wireguard_pre_hardening_archives.matched | int > 0
|
||||
fail_msg: >-
|
||||
Create a protected pre-hardening archive before replacing compose.yml.
|
||||
|
||||
- name: Render the candidate non-secret Compose declaration
|
||||
ansible.builtin.template:
|
||||
src: ../templates/wireguard-compose.yml.j2
|
||||
dest: "{{ wireguard_compose_candidate }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Validate the candidate Compose declaration
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- docker
|
||||
- compose
|
||||
- --project-directory
|
||||
- "{{ compose_project_dir }}"
|
||||
- --file
|
||||
- "{{ wireguard_compose_candidate }}"
|
||||
- config
|
||||
- --quiet
|
||||
changed_when: false
|
||||
|
||||
- name: Atomically activate the validated Compose declaration
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- mv
|
||||
- "{{ wireguard_compose_candidate }}"
|
||||
- "{{ compose_project_dir }}/compose.yml"
|
||||
changed_when: true
|
||||
@@ -0,0 +1,114 @@
|
||||
---
|
||||
# Deliberately narrow maintenance path for the us4 WireGuard service. The
|
||||
# Compose file and all private key material remain on the server; this playbook
|
||||
# only pulls its declared immutable image and recreates the named service.
|
||||
- name: Update and recreate the approved WireGuard service
|
||||
hosts: wireguard
|
||||
become: true
|
||||
gather_facts: false
|
||||
serial: 1
|
||||
vars:
|
||||
wireguard_maintenance_confirm: false
|
||||
tasks:
|
||||
- name: Require explicit WireGuard maintenance confirmation
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- wireguard_maintenance_confirm | bool
|
||||
fail_msg: >-
|
||||
This operation pulls the image declared in /opt/wireguard/compose.yml
|
||||
and briefly recreates WireGuard. Set wireguard_maintenance_confirm=true
|
||||
only during the approved maintenance window.
|
||||
|
||||
- name: Create the local WireGuard rollback directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ compose_project_dir }}/backups"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
|
||||
- name: Create a timestamp for the rollback archive
|
||||
ansible.builtin.command:
|
||||
argv: [date, +%Y%m%dT%H%M%SZ]
|
||||
changed_when: false
|
||||
register: wireguard_backup_timestamp
|
||||
|
||||
- name: Create a protected local rollback archive of WireGuard configuration
|
||||
ansible.builtin.shell:
|
||||
cmd: >-
|
||||
umask 077 && tar --create --gzip
|
||||
--file={{ compose_project_dir }}/backups/config-{{ wireguard_backup_timestamp.stdout }}.tar.gz
|
||||
--directory={{ compose_project_dir }} config
|
||||
executable: /bin/bash
|
||||
changed_when: true
|
||||
|
||||
- name: Verify the compose declaration uses an immutable image digest
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- docker
|
||||
- compose
|
||||
- --project-directory
|
||||
- "{{ compose_project_dir }}"
|
||||
- config
|
||||
changed_when: false
|
||||
register: wireguard_compose_config
|
||||
|
||||
- name: Reject mutable WireGuard image declarations
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "'@sha256:' in wireguard_compose_config.stdout"
|
||||
fail_msg: >-
|
||||
Pin the WireGuard image to an approved immutable digest before using
|
||||
this maintenance playbook.
|
||||
|
||||
- name: Pull the declared WireGuard image
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- docker
|
||||
- compose
|
||||
- --project-directory
|
||||
- "{{ compose_project_dir }}"
|
||||
- pull
|
||||
- wireguard
|
||||
changed_when: true
|
||||
|
||||
- name: Recreate the WireGuard container
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- docker
|
||||
- compose
|
||||
- --project-directory
|
||||
- "{{ compose_project_dir }}"
|
||||
- up
|
||||
- -d
|
||||
- --no-deps
|
||||
- --force-recreate
|
||||
- wireguard
|
||||
changed_when: true
|
||||
|
||||
- name: Verify WireGuard container state
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- docker
|
||||
- compose
|
||||
- --project-directory
|
||||
- "{{ compose_project_dir }}"
|
||||
- ps
|
||||
- --all
|
||||
changed_when: false
|
||||
register: wireguard_compose_ps
|
||||
|
||||
- name: Display WireGuard container state
|
||||
ansible.builtin.debug:
|
||||
var: wireguard_compose_ps.stdout_lines
|
||||
|
||||
- name: Run the deployed WireGuard health check
|
||||
ansible.builtin.command:
|
||||
argv: [/usr/local/lib/vps-health/run]
|
||||
changed_when: false
|
||||
register: wireguard_health_report
|
||||
failed_when: wireguard_health_report.rc not in [0, 1]
|
||||
|
||||
- name: Display sanitized WireGuard health result
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ wireguard_health_report.stdout | default(wireguard_health_report.stderr, true) }}"
|
||||
@@ -11,3 +11,4 @@ healthcheck_profiles:
|
||||
mailcow: mailcow.sh
|
||||
vaultwarden: vaultwarden.sh
|
||||
pdns: pdns.sh
|
||||
wireguard: wireguard.sh
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
set -uo pipefail
|
||||
exec '{{ healthcheck_install_root }}/{{ healthcheck_profiles[healthcheck_profile] }}' >> '{{ healthcheck_log_dir }}/healthcheck.log' 2>&1
|
||||
set -o pipefail
|
||||
'{{ healthcheck_install_root }}/{{ healthcheck_profiles[healthcheck_profile] }}' 2>&1 | tee -a '{{ healthcheck_log_dir }}/healthcheck.log'
|
||||
exit "${PIPESTATUS[0]}"
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env bash
|
||||
set -uo pipefail
|
||||
source '{{ healthcheck_install_root }}/health-common.sh'
|
||||
|
||||
require_command docker
|
||||
require_command ss
|
||||
check_compose
|
||||
|
||||
container='wireguard'
|
||||
if ! docker inspect "$container" >/dev/null 2>&1; then
|
||||
record critical 'wireguard_container_missing'
|
||||
else
|
||||
running="$(docker inspect "$container" --format '{{ '{{' }}.State.Running{{ '}}' }}' 2>/dev/null)"
|
||||
[[ "$running" == true ]] || record critical 'wireguard_container_not_running'
|
||||
|
||||
interface="$(docker exec "$container" wg show interfaces 2>/dev/null || true)"
|
||||
grep -qw 'wg0' <<<"$interface" || record critical 'wireguard_wg0_missing'
|
||||
|
||||
configured_peer_count="$(docker exec "$container" sh -c 'find /config -mindepth 1 -maxdepth 1 -type d -name "peer_*" | wc -l' 2>/dev/null || true)"
|
||||
runtime_peer_count="$(docker exec "$container" wg show wg0 dump 2>/dev/null | awk 'NR > 1 { count++ } END { print count + 0 }')"
|
||||
[[ "$configured_peer_count" =~ ^[0-9]+$ ]] || record unknown 'wireguard_configured_peer_count_unknown'
|
||||
[[ "$runtime_peer_count" =~ ^[0-9]+$ ]] || record unknown 'wireguard_runtime_peer_count_unknown'
|
||||
if [[ "$configured_peer_count" =~ ^[0-9]+$ && "$runtime_peer_count" =~ ^[0-9]+$ ]]; then
|
||||
[[ "$configured_peer_count" -eq "$runtime_peer_count" ]] || record warning "wireguard_peer_count_mismatch_${configured_peer_count}_${runtime_peer_count}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if ss -H -uln 2>/dev/null | awk '{print $4}' | grep -Eq '(^|:)51820$'; then
|
||||
record ok 'wireguard_udp_51820_listening'
|
||||
else
|
||||
record critical 'wireguard_udp_51820_missing'
|
||||
fi
|
||||
|
||||
emit_result
|
||||
exit "$EXIT_CODE"
|
||||
@@ -0,0 +1,24 @@
|
||||
services:
|
||||
wireguard:
|
||||
image: {{ wireguard_image }}
|
||||
container_name: wireguard
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=Asia/Shanghai
|
||||
- SERVERURL=us4.wsvc.info
|
||||
- SERVERPORT=51820
|
||||
- PEERS=ha,phone,mbp
|
||||
- PEERDNS=1.1.1.1
|
||||
# Intentional: client IPv6 traffic is not tunneled.
|
||||
- ALLOWEDIPS=0.0.0.0/0
|
||||
ports:
|
||||
- 51820:51820/udp
|
||||
volumes:
|
||||
- ./config:/config
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /run:exec
|
||||
restart: unless-stopped
|
||||
Reference in New Issue
Block a user