Capture current Ansible control plane state

Commit the accumulated infrastructure work that was living only in the
working tree: monitoring stack, emergency access/bot, gyro allocator,
grimmory, adguard, backup audit and the OpenCode agent definitions.

Also ignore Python bytecode, local archives and Nix/direnv artifacts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTocXkGUUazHdKKd3r9k71
This commit is contained in:
Dmitry
2026-08-26 21:39:28 +03:00
co-authored by Claude Opus 5
parent 4bafa7d09e
commit c676be81ec
126 changed files with 10583 additions and 44 deletions
+280
View File
@@ -0,0 +1,280 @@
---
- name: Harden live Mihomo on ru-vps
hosts: ru-vps
gather_facts: false
vars:
ru_vps_mihomo_harden_confirm: false
mihomo_root: /opt/services/ru-vps/mihomo
mihomo_config_path: "{{ mihomo_root }}/config/config.yaml"
mihomo_compose_path: "{{ mihomo_root }}/docker-compose.yml"
mihomo_backup_dir: /var/backups/ru-vps-mihomo
mihomo_state_dir: /var/lib/ru-vps-mihomo
mihomo_state_path: "{{ mihomo_state_dir }}/auth.json"
mihomo_harden_script_path: /usr/local/sbin/ru-vps-mihomo-harden
mihomo_harden_script_src: "{{ playbook_dir }}/../files/ru-vps-mihomo-harden.py"
mihomo_backup_path: "{{ mihomo_backup_dir }}/config.yaml.{{ lookup('pipe', 'date -u +%Y%m%d%H%M%S') }}"
pre_tasks:
- name: Require explicit Mihomo hardening confirmation
ansible.builtin.assert:
that:
- ru_vps_mihomo_harden_confirm | bool
fail_msg: Run with -e ru_vps_mihomo_harden_confirm=true.
- name: Preflight Mihomo config file exists
ansible.builtin.stat:
path: "{{ mihomo_config_path }}"
register: mihomo_config_stat
- name: Preflight Mihomo compose file exists
ansible.builtin.stat:
path: "{{ mihomo_compose_path }}"
register: mihomo_compose_stat
- name: Preflight Mihomo state file exists
ansible.builtin.stat:
path: "{{ mihomo_state_path }}"
register: mihomo_state_stat
- name: Refuse to run without live Mihomo config and compose files
ansible.builtin.assert:
that:
- mihomo_config_stat.stat.exists
- mihomo_compose_stat.stat.exists
fail_msg: Live Mihomo config or compose file is missing.
- name: Preflight Mihomo container is running
ansible.builtin.command:
argv:
- docker
- inspect
- -f
- '{{ "{{" }}.State.Running{{ "}}" }}'
- mihomo
register: mihomo_container_state
changed_when: false
failed_when: mihomo_container_state.stdout.strip() != 'true'
- name: Preflight PyYAML is available on the target
ansible.builtin.command:
argv:
- python3
- -c
- import yaml
changed_when: false
- name: Preflight current Mihomo config validates
ansible.builtin.command:
argv:
- docker
- compose
- -f
- "{{ mihomo_compose_path }}"
- exec
- -T
- mihomo
- /mihomo
- -t
- -d
- /root/.config/mihomo
changed_when: false
- name: Ensure Mihomo backup directory exists
ansible.builtin.file:
path: "{{ mihomo_backup_dir }}"
state: directory
owner: root
group: root
mode: "0700"
- name: Install root-only Mihomo hardening helper
ansible.builtin.copy:
src: "{{ mihomo_harden_script_src }}"
dest: "{{ mihomo_harden_script_path }}"
owner: root
group: root
mode: "0700"
- name: Create fresh backup of the live Mihomo config after validation
ansible.builtin.copy:
src: "{{ mihomo_config_path }}"
dest: "{{ mihomo_backup_path }}"
remote_src: true
owner: root
group: root
mode: "0600"
tasks:
- block:
- name: Apply Mihomo hardening in place
ansible.builtin.command:
argv:
- "{{ mihomo_harden_script_path }}"
- apply
- --config
- "{{ mihomo_config_path }}"
- --state
- "{{ mihomo_state_path }}"
register: mihomo_harden_apply
changed_when: (mihomo_harden_apply.stdout | from_json).changed
no_log: true
- name: Validate hardened Mihomo config in the running container
ansible.builtin.command:
argv:
- docker
- compose
- -f
- "{{ mihomo_compose_path }}"
- exec
- -T
- mihomo
- /mihomo
- -t
- -d
- /root/.config/mihomo
changed_when: false
- name: Recreate Mihomo container after config hardening
ansible.builtin.command:
argv:
- docker
- compose
- -f
- "{{ mihomo_compose_path }}"
- up
- -d
- --force-recreate
- mihomo
changed_when: true
- name: Wait for hardened Mihomo listeners on loopback
ansible.builtin.wait_for:
host: 127.0.0.1
port: "{{ item }}"
state: started
timeout: 30
loop:
- 7890
- 7891
- name: Run authenticated Mihomo SOCKS probe to Telegram
ansible.builtin.command:
argv:
- "{{ mihomo_harden_script_path }}"
- probe
- --config
- "{{ mihomo_config_path }}"
- --state
- "{{ mihomo_state_path }}"
register: mihomo_harden_probe
changed_when: false
no_log: true
- name: Check anonymous SOCKS access fails
ansible.builtin.command:
argv:
- curl
- --silent
- --show-error
- --connect-timeout
- "5"
- --max-time
- "15"
- --proxy
- socks5h://127.0.0.1:7891
- https://api.telegram.org
- --output
- /dev/null
register: mihomo_anon_probe
changed_when: false
failed_when: false
- name: Remove public Mihomo UFW rules after successful hardening
community.general.ufw:
rule: allow
port: "{{ item.port }}"
proto: "{{ item.proto }}"
delete: true
loop:
- { port: 7890, proto: tcp }
- { port: 7890, proto: udp }
- { port: 7891, proto: tcp }
- { port: 7891, proto: udp }
- name: Verify Mihomo listeners are loopback only
ansible.builtin.command:
argv:
- ss
- -H
- -ltnp
- '( sport = :7890 or sport = :7891 )'
register: mihomo_ss
changed_when: false
- name: Confirm hardened Mihomo is bound to loopback only and anonymous access fails
ansible.builtin.assert:
that:
- mihomo_ss.stdout is search('127\\.0\\.0\\.1:7890')
- mihomo_ss.stdout is search('127\\.0\\.0\\.1:7891')
- mihomo_ss.stdout is not search('0\\.0\\.0\\.0:7890|:::7890|0\\.0\\.0\\.0:7891|:::7891')
- mihomo_anon_probe.rc != 0
fail_msg: Hardened Mihomo must listen on loopback only and reject anonymous SOCKS access.
rescue:
- name: Restore the live Mihomo config from backup
ansible.builtin.copy:
src: "{{ mihomo_backup_path }}"
dest: "{{ mihomo_config_path }}"
remote_src: true
owner: root
group: root
mode: "0640"
- name: Recreate Mihomo container after rollback
ansible.builtin.command:
argv:
- docker
- compose
- -f
- "{{ mihomo_compose_path }}"
- up
- -d
- --force-recreate
- mihomo
changed_when: true
- name: Remove newly created Mihomo credentials state after rollback
ansible.builtin.file:
path: "{{ mihomo_state_path }}"
state: absent
when: not mihomo_state_stat.stat.exists
- name: Wait for restored Mihomo listeners on loopback
ansible.builtin.wait_for:
host: 127.0.0.1
port: "{{ item }}"
state: started
timeout: 30
loop:
- 7890
- 7891
- name: Validate restored Mihomo config in the running container
ansible.builtin.command:
argv:
- docker
- compose
- -f
- "{{ mihomo_compose_path }}"
- exec
- -T
- mihomo
- /mihomo
- -t
- -d
- /root/.config/mihomo
changed_when: false
- name: Fail Mihomo hardening after restoring the backup
ansible.builtin.fail:
msg: Mihomo hardening failed and the live config was restored from backup.