--- - name: Validate emergency access client inputs ansible.builtin.assert: that: - emergency_vps_host_key is match('^ssh-(ed25519|rsa|ecdsa-[^ ]+) [A-Za-z0-9+/=]+( [A-Za-z0-9@._:-]+)?$') - emergency_bot_control_public_key | length > 0 fail_msg: Set EMERGENCY_VPS_HOST_KEY and provision the emergency-bot control key first. no_log: true - name: Install reverse SSH client ansible.builtin.apt: name: - openssh-client - sudo state: present update_cache: true - name: Create emergency access service accounts ansible.builtin.user: name: "{{ item }}" system: true # SSHD must start the forced command under a valid shell; authorized_keys # still prevents this account from receiving an arbitrary command or PTY. shell: "{{ '/bin/sh' if item == emergency_control_user else '/usr/sbin/nologin' }}" create_home: true loop: - "{{ emergency_reverse_tunnel_user }}" - "{{ emergency_control_user }}" - name: Create emergency access configuration directory ansible.builtin.file: path: "{{ emergency_access_dir }}" state: directory owner: root group: root mode: "0755" - name: Create reverse SSH state directory ansible.builtin.file: path: "{{ emergency_tunnel_state_dir }}" state: directory owner: "{{ emergency_reverse_tunnel_user }}" group: "{{ emergency_reverse_tunnel_user }}" mode: "0700" - name: Generate dedicated reverse SSH key ansible.builtin.command: cmd: "ssh-keygen -q -t ed25519 -N '' -f {{ emergency_reverse_key_path }}" creates: "{{ emergency_reverse_key_path }}" become_user: "{{ emergency_reverse_tunnel_user }}" no_log: true - name: Read reverse SSH public key ansible.builtin.slurp: src: "{{ emergency_reverse_key_path }}.pub" register: emergency_reverse_key no_log: true - name: Store reverse SSH public key for VPS configuration ansible.builtin.set_fact: emergency_reverse_public_key: "{{ emergency_reverse_key.content | b64decode | trim }}" no_log: true - name: Pin ru-vps SSH host key for the tunnel user ansible.builtin.copy: dest: "{{ emergency_tunnel_state_dir }}/known_hosts" content: "ru-vps-emergency {{ emergency_vps_host_key }}\n" owner: "{{ emergency_reverse_tunnel_user }}" group: "{{ emergency_reverse_tunnel_user }}" mode: "0600" no_log: true - name: Install reverse SSH systemd unit ansible.builtin.template: src: emergency-reverse-ssh.service.j2 dest: /etc/systemd/system/emergency-reverse-ssh.service owner: root group: root mode: "0644" notify: Reload systemd - name: Install emergency tunnel expiry unit ansible.builtin.template: src: emergency-reverse-ssh-expire.service.j2 dest: /etc/systemd/system/emergency-reverse-ssh-expire.service owner: root group: root mode: "0644" notify: Reload systemd - name: Install emergency tunnel expiry timer ansible.builtin.template: src: emergency-reverse-ssh.timer.j2 dest: /etc/systemd/system/emergency-reverse-ssh.timer owner: root group: root mode: "0644" notify: Reload systemd - name: Install restricted emergency control helper ansible.builtin.template: src: emergency-control.j2 dest: /usr/local/libexec/emergency-control owner: root group: root mode: "0755" - name: Install emergency control sudo policy ansible.builtin.template: src: emergency-control.sudoers.j2 dest: /etc/sudoers.d/emergency-control owner: root group: root mode: "0440" validate: visudo -cf %s - name: Authorize emergency-bot control key with forced command ansible.posix.authorized_key: user: "{{ emergency_control_user }}" key: "command=\"/usr/local/libexec/emergency-control\",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty {{ emergency_bot_control_public_key }}" state: present exclusive: true no_log: true - name: Reload systemd before managing emergency units ansible.builtin.meta: flush_handlers - name: Keep emergency tunnel disabled at boot ansible.builtin.systemd_service: name: "{{ item }}" enabled: false loop: - emergency-reverse-ssh.service - emergency-reverse-ssh.timer