From b401a956d59ab4793bf3a7ad3a90b66e494e026d Mon Sep 17 00:00:00 2001 From: Sergey Antropoff Date: Wed, 1 Jul 2026 12:55:52 +0300 Subject: [PATCH] feat: complete uninstall cleanup on server and local output Remove binary, config, masq, system user, ufw rules, and apt packages from VPS; delete output// by default and rebuild global index.html via localhost play. Co-authored-by: Cursor --- Makefile | 2 +- README.md | 8 ++- playbook-uninstall.yml | 13 ++++ roles/hysteria2/defaults/main.yml | 3 - roles/hysteria2/defaults/uninstall.yml | 15 +++++ roles/hysteria2/tasks/uninstall.yml | 87 ++++++++++++++++++++++---- 6 files changed, 108 insertions(+), 20 deletions(-) create mode 100644 roles/hysteria2/defaults/uninstall.yml diff --git a/Makefile b/Makefile index c9827e1..d834e09 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ help: ## Показать справку @echo " make install" @echo " make install LIMIT=vps-de" @echo " make update LIMIT=vps-nl" - @echo " make uninstall LIMIT=vps-de EXTRA_VARS='hysteria2_uninstall_remove_local_output=true'" + @echo " make uninstall LIMIT=vps-de" @echo "" init: ## Создать inventory, group_vars и .vault_pass из примеров diff --git a/README.md b/README.md index 62ff23b..8500704 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ make install # → output/index.html откроется в браузере | `make install` | Установка Salamander + экспорт URL/QR/HTML | | `make update` | Обновить бинарник, конфиг, перевыпустить экспорт | | `make export` | Только экспорт (URL, QR, HTML) | -| `make uninstall` | Удалить Hysteria2 с VPS | +| `make uninstall` | Полное удаление Hysteria2 с VPS + `output//` + пересборка `output/index.html` | | `make vault-encrypt` | Зашифровать vault | | `make vault-edit` | Редактировать vault | @@ -70,7 +70,8 @@ make install # → output/index.html откроется в браузере make install LIMIT=vps-de make update LIMIT=vps-nl make export -make uninstall LIMIT=vps-de EXTRA_VARS='hysteria2_uninstall_remove_local_output=true' +make uninstall LIMIT=vps-de +make uninstall LIMIT=vps-de EXTRA_VARS='hysteria2_uninstall_remove_local_output=false' make install EXTRA_VARS='hysteria2_open_browser=false' make update EXTRA_VARS='hysteria2_wait_for_acme=false' ``` @@ -304,7 +305,8 @@ ASCII QR — `hysteria share --qr` → `user.qr.txt`. | `hysteria2_listen_port` | group | Порт (443) | | `hysteria2_generate_qr_png` | group | PNG QR через `qrencode` | | `hysteria2_open_browser` | group | Открыть `output/index.html` после экспорта | -| `hysteria2_uninstall_remove_local_output` | extra-vars | Удалить `output//` при uninstall | +| `hysteria2_uninstall_remove_local_output` | group | Удалить `output//` при uninstall (по умолчанию `true`) | +| `hysteria2_uninstall_rebuild_global_index` | group | Пересобрать `output/index.html` после uninstall (по умолчанию `true`) | | `vault_hysteria2_obfs_passwords` | vault | obfs-пароли по серверам | --- diff --git a/playbook-uninstall.yml b/playbook-uninstall.yml index ad4de3d..51a39d2 100644 --- a/playbook-uninstall.yml +++ b/playbook-uninstall.yml @@ -5,3 +5,16 @@ roles: - role: hysteria2 tags: [uninstall] + +- name: Rebuild global output index after uninstall + hosts: localhost + connection: local + gather_facts: true + become: false + tags: [uninstall] + tasks: + - name: Regenerate output/index.html from remaining servers + ansible.builtin.include_role: + name: hysteria2 + tasks_from: export_global.yml + when: hysteria2_uninstall_rebuild_global_index | default(true) | bool diff --git a/roles/hysteria2/defaults/main.yml b/roles/hysteria2/defaults/main.yml index 440df68..6784305 100644 --- a/roles/hysteria2/defaults/main.yml +++ b/roles/hysteria2/defaults/main.yml @@ -43,6 +43,3 @@ hysteria2_qr_png_error_correction: M hysteria2_wait_for_acme: true hysteria2_open_browser: true -# --- uninstall --- -hysteria2_uninstall_remove_config: true -hysteria2_uninstall_remove_local_output: false diff --git a/roles/hysteria2/defaults/uninstall.yml b/roles/hysteria2/defaults/uninstall.yml new file mode 100644 index 0000000..71af7d5 --- /dev/null +++ b/roles/hysteria2/defaults/uninstall.yml @@ -0,0 +1,15 @@ +--- +# Системный пользователь Hysteria (создаётся install_server.sh) +hysteria2_system_user: hysteria + +# --- uninstall (Salamander: без masq, порты 443/tcp+udp) --- +hysteria2_uninstall_remove_config: true +hysteria2_uninstall_remove_masq: false +hysteria2_uninstall_remove_system_user: true +hysteria2_uninstall_remove_packages: true +hysteria2_uninstall_remove_firewall_rules: true +hysteria2_uninstall_remove_local_output: true +hysteria2_uninstall_rebuild_global_index: true +hysteria2_uninstall_ufw_rules: + - "{{ hysteria2_listen_port }}/tcp" + - "{{ hysteria2_listen_port }}/udp" diff --git a/roles/hysteria2/tasks/uninstall.yml b/roles/hysteria2/tasks/uninstall.yml index aad4b04..f43e229 100644 --- a/roles/hysteria2/tasks/uninstall.yml +++ b/roles/hysteria2/tasks/uninstall.yml @@ -6,31 +6,79 @@ state: stopped failed_when: false -- name: Remove Hysteria2 via official script - ansible.builtin.shell: - cmd: bash <(curl -fsSL https://get.hy2.sh/) --remove - executable: /bin/bash +- name: Sync official Hysteria2 install script on control node + ansible.builtin.import_tasks: sync_install_script.yml + +- name: Copy Hysteria2 install script to server + ansible.builtin.copy: + src: "{{ hysteria2_install_script_name }}" + dest: "{{ hysteria2_install_script_remote_path }}" + mode: "0755" + +- name: Remove Hysteria2 binary and systemd units via official script + ansible.builtin.command: + cmd: "{{ hysteria2_install_script_remote_path }} --remove" register: _hysteria2_remove + changed_when: _hysteria2_remove.rc == 0 failed_when: false -- name: Remove Hysteria2 configuration directory +- name: Remove Hysteria2 configuration and ACME data ansible.builtin.file: path: "{{ hysteria2_config_path | dirname }}" state: absent when: hysteria2_uninstall_remove_config | bool +- name: Remove Hysteria system user and home directory + ansible.builtin.user: + name: "{{ hysteria2_system_user }}" + state: absent + remove: true + when: hysteria2_uninstall_remove_system_user | bool + failed_when: false + +- name: Check if ufw is available and active + ansible.builtin.command: ufw status + register: _hysteria2_ufw_status + changed_when: false + failed_when: false + when: hysteria2_uninstall_remove_firewall_rules | bool + +- name: Remove firewall rules added during install + ansible.builtin.command: "ufw delete allow {{ item }}" + loop: "{{ hysteria2_uninstall_ufw_rules }}" + register: _hysteria2_ufw_delete + changed_when: >- + _hysteria2_ufw_delete.rc == 0 + and 'Could not delete' not in (_hysteria2_ufw_delete.stdout | default('')) + and 'Could not find' not in (_hysteria2_ufw_delete.stderr | default('')) + failed_when: false + when: + - hysteria2_uninstall_remove_firewall_rules | bool + - "'active' in (_hysteria2_ufw_status.stdout | default(''))" + +- name: Remove packages installed for Hysteria2 + ansible.builtin.apt: + name: "{{ _hysteria2_apt_packages }}" + state: absent + purge: true + autoremove: true + vars: + _hysteria2_apt_packages: >- + {{ + ['curl', 'micro'] + + (['qrencode'] if hysteria2_generate_qr_png | bool else []) + }} + when: hysteria2_uninstall_remove_packages | bool + +- name: Remove copied install script from server + ansible.builtin.file: + path: "{{ hysteria2_install_script_remote_path }}" + state: absent + - name: Reload systemd after uninstall ansible.builtin.systemd: daemon_reload: true -- name: Show uninstall result - ansible.builtin.debug: - msg: >- - Hysteria2 (Salamander) удалён с {{ inventory_hostname }}. - {% if not hysteria2_uninstall_remove_local_output | bool %} - Локальные URL/QR в {{ hysteria2_output_dir }}/{{ hysteria2_output_name }}/ сохранены. - {% endif %} - - name: Remove local exported client files ansible.builtin.file: path: "{{ hysteria2_output_dir }}/{{ hysteria2_output_name }}" @@ -38,3 +86,16 @@ delegate_to: localhost become: false when: hysteria2_uninstall_remove_local_output | bool + +- name: Show uninstall result + ansible.builtin.debug: + msg: >- + Hysteria2 (Salamander) полностью удалён с {{ inventory_hostname }}. + {% if hysteria2_uninstall_remove_local_output | bool %} + Локальные URL/QR в {{ hysteria2_output_dir }}/{{ hysteria2_output_name }}/ удалены. + {% if hysteria2_uninstall_rebuild_global_index | bool %} + Глобальный {{ hysteria2_output_dir }}/index.html пересобран. + {% endif %} + {% else %} + Локальные URL/QR в {{ hysteria2_output_dir }}/{{ hysteria2_output_name }}/ сохранены. + {% endif %}