diff --git a/defaults.inc b/defaults.inc index 9d713e2..a4b2310 100755 --- a/defaults.inc +++ b/defaults.inc @@ -3,7 +3,7 @@ action="${1:-}" FOLDER='/opt/idssys/ta-proxmenu' -VERS='2026.7.25-55' +VERS='2026.7.25-57' noupdate=' ' diff --git a/inc/deploy-pulse-lxc.sh b/inc/deploy-pulse-lxc.sh index fe75d30..8bacd18 100644 --- a/inc/deploy-pulse-lxc.sh +++ b/inc/deploy-pulse-lxc.sh @@ -117,7 +117,7 @@ if not isinstance(agent, dict) or not str(agent.get("id", "")).strip(): } TAPM_PULSE_AGENT_TOKEN_REQUEST_JSON() { - printf '%s\n' '{"type":"pve","enableCommands":false}' + printf '%s\n' '{"type":"host","enableCommands":false}' } TAPM_PULSE_ONLINE_NODES_FROM_JSON() { @@ -205,6 +205,58 @@ TAPM_PULSE_PROMPT_UNTIL_VALID() { done } +TAPM_PULSE_VALID_ADMIN_PASSWORD() { + local password="${1:-}" + + TAPM_PULSE_PASSWORD="$password" python3 -c ' +import os +password = os.environ["TAPM_PULSE_PASSWORD"] +raise SystemExit( + 0 if len(password) >= 12 and len(password.encode("utf-8")) <= 72 else 1 +) +' 2>/dev/null +} + +TAPM_PULSE_SELECT_ADMIN_PASSWORD() { + local output_variable="$1" + local mode_variable="$2" + local selection candidate confirmation + local -a labels=( + "Generate a strong random password (recommended)" + "Enter a custom password" + ) + local -a values=("generated" "custom") + + SELECT_MENU "Pulse administrator password" labels values 0 + selection="$MENU_SELECTION" + if [[ "$selection" == 'generated' ]]; then + printf -v "$output_variable" '%s' '' + printf -v "$mode_variable" '%s' 'Generated' + return 0 + fi + + while true; do + echo + read -r -s -p " Enter custom Pulse admin password (12+ characters): " candidate + echo + if ! TAPM_PULSE_VALID_ADMIN_PASSWORD "$candidate"; then + TAPM_PULSE_FAIL \ + "The password must be at least 12 characters and no more than 72 bytes." + continue + fi + read -r -s -p " Confirm custom Pulse admin password: " confirmation + echo + if [[ "$candidate" != "$confirmation" ]]; then + TAPM_PULSE_FAIL "The passwords did not match. Please try again." + continue + fi + printf -v "$output_variable" '%s' "$candidate" + printf -v "$mode_variable" '%s' 'Custom' + unset candidate confirmation + return 0 + done +} + TAPM_PULSE_FAIL() { echo -e "\n${idsCL[LightRed]}$1${idsCL[Default]}" return 1 @@ -223,7 +275,7 @@ TAPM_PULSE_CONFIRM_CREDENTIALS_SAVED() { echo echo -e "${idsCL[LightYellow]}============================================================================${idsCL[Default]}" echo -e "${idsCL[LightYellow]} IMPORTANT — SAVE THESE PULSE CREDENTIALS NOW${idsCL[Default]}" - echo -e "${idsCL[LightYellow]} They are generated for this installation and will not be displayed again.${idsCL[Default]}" + echo -e "${idsCL[LightYellow]} They will not be displayed again by this installer.${idsCL[Default]}" echo -e "${idsCL[LightYellow]}============================================================================${idsCL[Default]}" echo -e " Pulse URL: ${idsCL[LightCyan]}${pulse_url}${idsCL[Default]}" echo -e " Username: ${idsCL[White]}${admin_username}${idsCL[Default]}" @@ -393,11 +445,16 @@ TAPM_PULSE_CONFIGURE_SECURITY() { local username_variable="$4" local password_variable="$5" local token_variable="$6" + local requested_password="${7:-}" local bootstrap_output bootstrap_token generated_username generated_password generated_api_token local request_file curl_config response security_ready='no' attempt generated_username='admin' - generated_password="Ta!9-$(openssl rand -hex 18)" || return 1 + if [[ -n "$requested_password" ]]; then + generated_password="$requested_password" + else + generated_password="Ta!9-$(openssl rand -hex 18)" || return 1 + fi generated_api_token="$(openssl rand -hex 32)" || return 1 request_file="${temp_dir}/pulse-quick-setup.json" curl_config="${temp_dir}/pulse-quick-setup.curl" @@ -786,12 +843,19 @@ TAPM_PULSE_INSTALL_AGENT_LOCAL() { done fi if (( status == 0 )); then + if ! command -v sensors >/dev/null 2>&1; then + echo " Installing lm-sensors for Pulse host temperature telemetry..." + if ! DEBIAN_FRONTEND=noninteractive apt-get install -y \ + --no-install-recommends lm-sensors; then + echo " lm-sensors could not be installed; Pulse will continue without host temperature telemetry." >&2 + fi + fi bash "$installer_file" \ --url "$pulse_url" \ --token-file "$token_file" \ --hostname "$node" \ - --enable-proxmox \ - --proxmox-type pve \ + --enable-host \ + --disable-proxmox \ --non-interactive \ --insecure || status=$? fi @@ -871,12 +935,20 @@ for artifact in \ fi done +if ! command -v sensors >/dev/null 2>&1; then + echo " Installing lm-sensors for Pulse host temperature telemetry..." + if ! DEBIAN_FRONTEND=noninteractive apt-get install -y \ + --no-install-recommends lm-sensors; then + echo " lm-sensors could not be installed; Pulse will continue without host temperature telemetry." >&2 + fi +fi + bash "$installer_file" \ --url "$pulse_url" \ --token-file "$token_file" \ --hostname "$node" \ - --enable-proxmox \ - --proxmox-type pve \ + --enable-host \ + --disable-proxmox \ --non-interactive \ --insecure systemctl is-active --quiet pulse-agent @@ -948,7 +1020,8 @@ TAPM_DEPLOY_PULSE_LXC() { local arch archive_name base_url installer archive signature installer_signature local memory disk cores cpulimit swap onboot firewall unprivileged nameserver startup local network_config choice add_ha='no' auto_updates='yes' container_ip timezone temp_dir - local default_bridge pulse_url admin_username admin_password primary_api_token + local default_bridge pulse_url admin_username admin_password admin_password_mode + local primary_api_token local cluster_status='Registration failed' local agent_status='Skipped because cluster registration failed' local container_created=0 @@ -1064,6 +1137,11 @@ TAPM_DEPLOY_PULSE_LXC() { TAPM_ISO_NFS_SELECT_STORAGE root_storage \ "Pulse root filesystem storage" "$default_root_storage" || return 1 + admin_password='' + admin_password_mode='Generated' + TAPM_PULSE_SELECT_ADMIN_PASSWORD \ + admin_password admin_password_mode || return 1 + default_root_storage="$( pvesm status --content vztmpl 2>/dev/null | awk 'NR > 1 && $3 == "active" { print $1; exit }' @@ -1097,6 +1175,7 @@ TAPM_DEPLOY_PULSE_LXC() { echo " Firewall: enabled" echo " Automatic update: ${auto_updates}" echo " Proxmox HA: ${add_ha}" + echo " Admin password: ${admin_password_mode}" echo read -r -p " Create this Pulse container (type yes to continue)? " choice [[ "$choice" =~ ^[Yy][Ee][Ss]$ ]] || { @@ -1249,7 +1328,8 @@ TAPM_DEPLOY_PULSE_LXC() { echo -e "\n${idsCL[LightCyan]}Configuring Pulse administrator security...${idsCL[Default]}" if ! TAPM_PULSE_CONFIGURE_SECURITY \ "$ctid" "$pulse_url" "$temp_dir" \ - admin_username admin_password primary_api_token; then + admin_username admin_password primary_api_token \ + "$admin_password"; then pct exec "$ctid" -- rm -f \ /tmp/install.sh "/tmp/${archive_name}" "/tmp/${archive_name}.sshsig" || true TAPM_CLEAN_TEMP_DIR "$temp_dir" diff --git a/tests/test-pulse.sh b/tests/test-pulse.sh index 1b8d691..f524940 100644 --- a/tests/test-pulse.sh +++ b/tests/test-pulse.sh @@ -49,9 +49,9 @@ assert_success "registered Pulse agent response accepted" \ '{"agent":{"id":"agent-123","hostname":"pve1"}}' assert_failure "missing Pulse agent ID rejected" \ TAPM_PULSE_AGENT_REGISTERED_FROM_RESPONSE '{"agent":{"hostname":"pve1"}}' -assert_equal '{"type":"pve","enableCommands":false}' \ +assert_equal '{"type":"host","enableCommands":false}' \ "$(TAPM_PULSE_AGENT_TOKEN_REQUEST_JSON)" \ - "Proxmox-specific agent enrollment requested with commands disabled" + "host telemetry enrollment requested with commands disabled" setup_token='0123456789abcdef0123456789abcdef' setup_host='https://pve1.example.test:8006' @@ -147,6 +147,19 @@ assert_success "valid Pulse VLAN" TAPM_PULSE_VALID_OPTIONAL_VLAN 4094 assert_failure "Pulse VLAN zero rejected" TAPM_PULSE_VALID_OPTIONAL_VLAN 0 assert_success "blank Pulse VLAN accepted" TAPM_PULSE_VALID_OPTIONAL_VLAN '' +assert_success "12-character Pulse admin password accepted" \ + TAPM_PULSE_VALID_ADMIN_PASSWORD '123456789012' +assert_success "72-byte Pulse admin password accepted" \ + TAPM_PULSE_VALID_ADMIN_PASSWORD \ + '123456789012345678901234567890123456789012345678901234567890123456789012' +assert_failure "short Pulse admin password rejected" \ + TAPM_PULSE_VALID_ADMIN_PASSWORD '12345678901' +assert_failure "multibyte Pulse admin password still requires 12 characters" \ + TAPM_PULSE_VALID_ADMIN_PASSWORD 'éééééé' +assert_failure "Pulse admin password above bcrypt limit rejected" \ + TAPM_PULSE_VALID_ADMIN_PASSWORD \ + '1234567890123456789012345678901234567890123456789012345678901234567890123' + resources='[ {"type":"lxc","name":"pulse-a","tags":"tapm;pulse"}, {"type":"qemu","name":"unrelated"}