From 427ff8aa1dff9bbaaed69e7c2c68e654acdaec68 Mon Sep 17 00:00:00 2001 From: Samuel James Date: Thu, 3 Sep 2026 14:42:31 +0000 Subject: [PATCH] life-church: add thinkcenter-rustdesk (thinkcenter-only, error-checked) --- .../playbooks/thinkcenter-rustdesk.yml | 200 ++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 life-church/playbooks/thinkcenter-rustdesk.yml diff --git a/life-church/playbooks/thinkcenter-rustdesk.yml b/life-church/playbooks/thinkcenter-rustdesk.yml new file mode 100644 index 0000000..5c5caf5 --- /dev/null +++ b/life-church/playbooks/thinkcenter-rustdesk.yml @@ -0,0 +1,200 @@ +--- +# ============================================================================ +# life-church/playbooks/thinkcenter-rustdesk.yml +# Install the RustDesk remote-desktop client (1.4.9) on thinkcenter and point +# it at the self-hosted RustDesk server (racknerd2, via NetBird mesh). +# thinkcenter ONLY. Idempotent. become password from Infisical /lc-hosts. +# Server config (id/relay/key) pulled from Infisical /rustdesk at runtime. +# +# - Downloads the official .deb (Flutter build) from GitHub with SHA256 verify. +# - Installs via apt (resolves deps automatically). +# - Writes ~/.config/rustdesk/RustDesk2.toml with the custom rendezvous +# (ID) server, relay server, and public key so the client auto-registers to +# the self-hosted server instead of the public RustDesk network. +# +# UNDO: +# sudo apt remove --purge rustdesk +# rm -rf ~/.config/rustdesk +# ============================================================================ + +# --- Play 1 (localhost): fetch creds + rustdesk server config from Infisical - +- name: "thinkcenter RustDesk — resolve secrets from Infisical" + hosts: localhost + gather_facts: false + tasks: + - name: "[resolve] Infisical creds from env" + ansible.builtin.set_fact: + infisical_client_id: "{{ lookup('env','INFISICAL_CLIENT_ID') }}" + infisical_client_secret: "{{ lookup('env','INFISICAL_CLIENT_SECRET') }}" + infisical_project_id: "{{ lookup('env','INFISICAL_PROJECT_ID') }}" + infisical_url: "{{ lookup('env','INFISICAL_API_URL') | default('http://192.168.122.41:8080', true) }}" + infisical_env_slug: "{{ lookup('env','INFISICAL_ENV_SLUG') | default('prod', true) }}" + + - name: "[resolve] Assert Infisical creds present" + ansible.builtin.assert: + that: + - infisical_client_id | length > 0 + - infisical_client_secret | length > 0 + - infisical_project_id | length > 0 + fail_msg: "Infisical creds missing from Semaphore environment 'infisical-dev'." + + - name: "[infisical] Log in" + infisical.vault.login: + url: "{{ infisical_url }}" + auth_method: universal_auth + universal_auth_client_id: "{{ infisical_client_id }}" + universal_auth_client_secret: "{{ infisical_client_secret }}" + register: infisical_login + no_log: true + + - name: "[infisical] Read /lc-hosts (sudo pass + user)" + infisical.vault.read_secrets: + login_data: "{{ infisical_login.login_data }}" + project_id: "{{ infisical_project_id }}" + env_slug: "{{ infisical_env_slug }}" + path: "/lc-hosts" + as_dict: true + register: lc_secrets + no_log: true + + - name: "[infisical] Read /rustdesk (server config)" + infisical.vault.read_secrets: + login_data: "{{ infisical_login.login_data }}" + project_id: "{{ infisical_project_id }}" + env_slug: "{{ infisical_env_slug }}" + path: "/rustdesk" + as_dict: true + register: rd_secrets + no_log: true + + - name: "[set] Stash sudo pass, user, and rustdesk server config" + ansible.builtin.set_fact: + thinkcenter_become_pass: "{{ lc_secrets.secrets['THINKCENTER_SUDO_PASS'] }}" + thinkcenter_login_user: "{{ lc_secrets.secrets['THINKCENTER_USER'] }}" + rd_id_server: "{{ rd_secrets.secrets['RUSTDESK_ID_SERVER'] }}" + rd_relay_server: "{{ rd_secrets.secrets['RUSTDESK_RELAY_SERVER'] }}" + rd_api_server: "{{ rd_secrets.secrets['RUSTDESK_API_SERVER'] | default('', true) }}" + rd_key: "{{ rd_secrets.secrets['RUSTDESK_KEY'] }}" + no_log: true + +# --- Play 2: RustDesk on thinkcenter ONLY ------------------------------------ +- name: "Install + configure RustDesk client on thinkcenter" + hosts: thinkcenter + gather_facts: true + become: true + vars: + ansible_become_pass: "{{ hostvars['localhost'].thinkcenter_become_pass }}" + rd_user: "{{ hostvars['localhost'].thinkcenter_login_user }}" + rd_version: "1.4.9" + rd_deb_url: "https://github.com/rustdesk/rustdesk/releases/download/1.4.9/rustdesk-1.4.9-x86_64.deb" + rd_deb_sha256: "7244ba47c40e804172044bfbe659467c54ce46554c98e78c8c0406f1d612fda3" + rd_id_server: "{{ hostvars['localhost'].rd_id_server }}" + rd_relay_server: "{{ hostvars['localhost'].rd_relay_server }}" + rd_api_server: "{{ hostvars['localhost'].rd_api_server }}" + rd_key: "{{ hostvars['localhost'].rd_key }}" + tmp_dir: /tmp/rustdesk-install + tasks: + - name: "[safety] Confirm we are on thinkcenter" + ansible.builtin.assert: + that: inventory_hostname == 'thinkcenter' + fail_msg: "Refusing to run — this play is scoped to thinkcenter only." + + - name: "[safety] Assert rd_user resolved + exists" + block: + - name: "[safety] Non-empty user" + ansible.builtin.assert: + that: rd_user | length > 0 + fail_msg: "rd_user empty — THINKCENTER_USER not read from Infisical." + - name: "[facts] getent user" + ansible.builtin.getent: + database: passwd + key: "{{ rd_user }}" + - name: "[facts] Assert user exists" + ansible.builtin.assert: + that: rd_user in ansible_facts.getent_passwd + fail_msg: "User '{{ rd_user }}' does not exist on host." + + - name: "[facts] Resolve user home" + ansible.builtin.set_fact: + rd_home: "{{ ansible_facts.getent_passwd[rd_user][4] }}" + + - name: "[dl] Create temp dir" + ansible.builtin.file: + path: "{{ tmp_dir }}" + state: directory + mode: "0755" + + - name: "[dl] Download RustDesk {{ rd_version }} .deb with SHA256 verify (retry)" + ansible.builtin.get_url: + url: "{{ rd_deb_url }}" + dest: "{{ tmp_dir }}/rustdesk.deb" + checksum: "sha256:{{ rd_deb_sha256 }}" + mode: "0644" + register: rd_dl + until: rd_dl is succeeded + retries: 3 + delay: 15 + + - name: "[apt] Install RustDesk .deb (apt resolves dependencies; retry on lock)" + ansible.builtin.apt: + deb: "{{ tmp_dir }}/rustdesk.deb" + state: present + register: rd_install + until: rd_install is succeeded + retries: 3 + delay: 15 + + - name: "[verify] rustdesk binary present" + ansible.builtin.command: which rustdesk + register: rd_which + changed_when: false + failed_when: false + + - name: "[verify] Assert RustDesk installed" + ansible.builtin.assert: + that: rd_which.rc == 0 + fail_msg: "RustDesk binary not found after install." + success_msg: "RustDesk installed at {{ rd_which.stdout | trim }}." + + # ---- Point the client at the self-hosted server (per-user config) ---- + - name: "[config] Ensure rustdesk config dir for {{ rd_user }}" + ansible.builtin.file: + path: "{{ rd_home }}/.config/rustdesk" + state: directory + owner: "{{ rd_user }}" + group: "{{ rd_user }}" + mode: "0700" + + - name: "[config] Write RustDesk2.toml (custom ID/relay server + key)" + ansible.builtin.copy: + dest: "{{ rd_home }}/.config/rustdesk/RustDesk2.toml" + owner: "{{ rd_user }}" + group: "{{ rd_user }}" + mode: "0600" + content: | + rendezvous_server = '{{ rd_id_server }}' + nat_type = 1 + serial = 0 + + [options] + custom-rendezvous-server = '{{ rd_id_server }}' + relay-server = '{{ rd_relay_server }}' + {% if rd_api_server | length > 0 %}api-server = '{{ rd_api_server }}' + {% endif %}key = '{{ rd_key }}' + register: rd_config + no_log: true + + - name: "[cleanup] Remove temp dir" + ansible.builtin.file: + path: "{{ tmp_dir }}" + state: absent + + - name: "[report] RustDesk summary" + ansible.builtin.debug: + msg: + - "===== thinkcenter RustDesk =====" + - "packages changed this run : {{ rd_install.changed }}" + - "rustdesk path : {{ rd_which.stdout | default('n/a') | trim }}" + - "config written for : {{ rd_user }} ({{ rd_home }}/.config/rustdesk/RustDesk2.toml)" + - "ID/relay server : self-hosted (from Infisical /rustdesk)" + - "NOTE: restart RustDesk (or the rustdesk service) for the custom server to take effect. Client shows the server key is applied under Settings -> Network."