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:
@@ -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.
|
||||
Reference in New Issue
Block a user