#!/bin/bash # Admin SSH access to THIS host. Manages the install user's authorized_keys — # paste a public key to grant access — and, behind a lockout guard, sshd # password authentication. Everything here is on-demand only: nothing runs # during install or deploy. LibrePortal is the *server* here, so the admin # brings their own public key; we never handle their private key. # # All the privileged work (editing ~/.ssh and /etc/ssh/sshd_config) lives in the # root-owned helper /usr/local/sbin/libreportal-ssh-access (runSshAccess), which # also enforces the lockout guards in the trust boundary. These functions are the # manager-side CLI/WebUI front for it: they shape arguments and print the UX. # The user admins actually log in as (has sudo). Falls back to libreportal. hostSshUser() { echo "${sudo_user_name:-libreportal}" } hostSshHome() { local u; u=$(hostSshUser) [[ "$u" == "root" ]] && { echo "/root"; return; } echo "/home/$u" } hostSshAuthKeysFile() { echo "$(hostSshHome)/.ssh/authorized_keys" } # Refresh the WebUI access snapshot after a change. No-op if generator absent. hostSshRefreshUi() { declare -f webuiGenerateSshAccess >/dev/null 2>&1 && webuiGenerateSshAccess >/dev/null 2>&1 return 0 } hostSshEnsureDir() { runSshAccess ensure-dir } # Count valid authorized public keys. hostSshKeyCount() { runSshAccess key-count 2>/dev/null || echo 0 } # True when sshd currently allows password authentication. hostSshPasswordAuthEnabled() { [[ "$(runSshAccess pw-status 2>/dev/null)" != "off" ]] } # Add a base64-encoded PUBLIC key to the install user's authorized_keys. hostSshKeyAdd() { local key_b64="$1" [[ -z "$key_b64" ]] && { isError "hostSshKeyAdd requires "; return 1; } local out rc out=$(runSshAccess key-add "$key_b64") rc=$? case "$out" in added) isSuccessful "SSH key authorized for $(hostSshUser)" ;; already-authorized) isNotice "That key is already authorized." ;; *) [[ $rc -ne 0 ]] && { isError "Could not add key (not a valid SSH public key?)"; return 1; } ;; esac hostSshRefreshUi } # Remove the authorized key whose fingerprint matches $1. The helper guards # against removing the last key while password auth is off (lockout). hostSshKeyRemove() { local fp="$1" [[ -z "$fp" ]] && { isError "hostSshKeyRemove requires "; return 1; } local out rc out=$(runSshAccess key-remove "$fp" 2>&1) rc=$? if [[ $rc -eq 2 ]]; then isError "Refusing to remove the last key while password login is disabled — you'd be locked out. Re-enable password login first." return 1 fi case "$out" in removed) isSuccessful "Removed SSH key $fp" ;; no-match) isNotice "No key matched fingerprint $fp" ;; *) [[ $rc -ne 0 ]] && { isError "Could not remove key."; return 1; } ;; esac hostSshRefreshUi } # Enable/disable sshd password authentication. Disabling is guarded (helper-side): # there must be at least one authorized key, or you'd lock yourself out. hostSshSetPasswordAuth() { local want="$1" # on|off case "$want" in on|off) ;; *) isError "hostSshSetPasswordAuth requires on|off"; return 1 ;; esac local out rc out=$(runSshAccess pw-set "$want" 2>&1) rc=$? if [[ $rc -eq 2 ]]; then isError "Refusing to disable password login with no authorized keys — add a key first or you'll be locked out." return 1 fi if [[ $rc -ne 0 ]]; then isError "sshd config test failed — restored backup, no change made." return 1 fi isSuccessful "Password login ${want} (sshd reloaded)." hostSshRefreshUi }