This commit is contained in:
David Schroeder
2026-07-25 22:26:10 -05:00
parent 55babe1ba2
commit eb88282cb1
3 changed files with 105 additions and 12 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
action="${1:-}" action="${1:-}"
FOLDER='/opt/idssys/ta-proxmenu' FOLDER='/opt/idssys/ta-proxmenu'
VERS='2026.7.25-55' VERS='2026.7.25-57'
noupdate=' ' noupdate=' '
+89 -9
View File
@@ -117,7 +117,7 @@ if not isinstance(agent, dict) or not str(agent.get("id", "")).strip():
} }
TAPM_PULSE_AGENT_TOKEN_REQUEST_JSON() { 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() { TAPM_PULSE_ONLINE_NODES_FROM_JSON() {
@@ -205,6 +205,58 @@ TAPM_PULSE_PROMPT_UNTIL_VALID() {
done 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() { TAPM_PULSE_FAIL() {
echo -e "\n${idsCL[LightRed]}$1${idsCL[Default]}" echo -e "\n${idsCL[LightRed]}$1${idsCL[Default]}"
return 1 return 1
@@ -223,7 +275,7 @@ TAPM_PULSE_CONFIRM_CREDENTIALS_SAVED() {
echo echo
echo -e "${idsCL[LightYellow]}============================================================================${idsCL[Default]}" echo -e "${idsCL[LightYellow]}============================================================================${idsCL[Default]}"
echo -e "${idsCL[LightYellow]} IMPORTANT — SAVE THESE PULSE CREDENTIALS NOW${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 "${idsCL[LightYellow]}============================================================================${idsCL[Default]}"
echo -e " Pulse URL: ${idsCL[LightCyan]}${pulse_url}${idsCL[Default]}" echo -e " Pulse URL: ${idsCL[LightCyan]}${pulse_url}${idsCL[Default]}"
echo -e " Username: ${idsCL[White]}${admin_username}${idsCL[Default]}" echo -e " Username: ${idsCL[White]}${admin_username}${idsCL[Default]}"
@@ -393,11 +445,16 @@ TAPM_PULSE_CONFIGURE_SECURITY() {
local username_variable="$4" local username_variable="$4"
local password_variable="$5" local password_variable="$5"
local token_variable="$6" local token_variable="$6"
local requested_password="${7:-}"
local bootstrap_output bootstrap_token generated_username generated_password generated_api_token local bootstrap_output bootstrap_token generated_username generated_password generated_api_token
local request_file curl_config response security_ready='no' attempt local request_file curl_config response security_ready='no' attempt
generated_username='admin' 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 generated_api_token="$(openssl rand -hex 32)" || return 1
request_file="${temp_dir}/pulse-quick-setup.json" request_file="${temp_dir}/pulse-quick-setup.json"
curl_config="${temp_dir}/pulse-quick-setup.curl" curl_config="${temp_dir}/pulse-quick-setup.curl"
@@ -786,12 +843,19 @@ TAPM_PULSE_INSTALL_AGENT_LOCAL() {
done done
fi fi
if (( status == 0 )); then 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" \ bash "$installer_file" \
--url "$pulse_url" \ --url "$pulse_url" \
--token-file "$token_file" \ --token-file "$token_file" \
--hostname "$node" \ --hostname "$node" \
--enable-proxmox \ --enable-host \
--proxmox-type pve \ --disable-proxmox \
--non-interactive \ --non-interactive \
--insecure || status=$? --insecure || status=$?
fi fi
@@ -871,12 +935,20 @@ for artifact in \
fi fi
done 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" \ bash "$installer_file" \
--url "$pulse_url" \ --url "$pulse_url" \
--token-file "$token_file" \ --token-file "$token_file" \
--hostname "$node" \ --hostname "$node" \
--enable-proxmox \ --enable-host \
--proxmox-type pve \ --disable-proxmox \
--non-interactive \ --non-interactive \
--insecure --insecure
systemctl is-active --quiet pulse-agent 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 arch archive_name base_url installer archive signature installer_signature
local memory disk cores cpulimit swap onboot firewall unprivileged nameserver startup 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 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 cluster_status='Registration failed'
local agent_status='Skipped because cluster registration failed' local agent_status='Skipped because cluster registration failed'
local container_created=0 local container_created=0
@@ -1064,6 +1137,11 @@ TAPM_DEPLOY_PULSE_LXC() {
TAPM_ISO_NFS_SELECT_STORAGE root_storage \ TAPM_ISO_NFS_SELECT_STORAGE root_storage \
"Pulse root filesystem storage" "$default_root_storage" || return 1 "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="$( default_root_storage="$(
pvesm status --content vztmpl 2>/dev/null | pvesm status --content vztmpl 2>/dev/null |
awk 'NR > 1 && $3 == "active" { print $1; exit }' awk 'NR > 1 && $3 == "active" { print $1; exit }'
@@ -1097,6 +1175,7 @@ TAPM_DEPLOY_PULSE_LXC() {
echo " Firewall: enabled" echo " Firewall: enabled"
echo " Automatic update: ${auto_updates}" echo " Automatic update: ${auto_updates}"
echo " Proxmox HA: ${add_ha}" echo " Proxmox HA: ${add_ha}"
echo " Admin password: ${admin_password_mode}"
echo echo
read -r -p " Create this Pulse container (type yes to continue)? " choice read -r -p " Create this Pulse container (type yes to continue)? " choice
[[ "$choice" =~ ^[Yy][Ee][Ss]$ ]] || { [[ "$choice" =~ ^[Yy][Ee][Ss]$ ]] || {
@@ -1249,7 +1328,8 @@ TAPM_DEPLOY_PULSE_LXC() {
echo -e "\n${idsCL[LightCyan]}Configuring Pulse administrator security...${idsCL[Default]}" echo -e "\n${idsCL[LightCyan]}Configuring Pulse administrator security...${idsCL[Default]}"
if ! TAPM_PULSE_CONFIGURE_SECURITY \ if ! TAPM_PULSE_CONFIGURE_SECURITY \
"$ctid" "$pulse_url" "$temp_dir" \ "$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 \ pct exec "$ctid" -- rm -f \
/tmp/install.sh "/tmp/${archive_name}" "/tmp/${archive_name}.sshsig" || true /tmp/install.sh "/tmp/${archive_name}" "/tmp/${archive_name}.sshsig" || true
TAPM_CLEAN_TEMP_DIR "$temp_dir" TAPM_CLEAN_TEMP_DIR "$temp_dir"
+15 -2
View File
@@ -49,9 +49,9 @@ assert_success "registered Pulse agent response accepted" \
'{"agent":{"id":"agent-123","hostname":"pve1"}}' '{"agent":{"id":"agent-123","hostname":"pve1"}}'
assert_failure "missing Pulse agent ID rejected" \ assert_failure "missing Pulse agent ID rejected" \
TAPM_PULSE_AGENT_REGISTERED_FROM_RESPONSE '{"agent":{"hostname":"pve1"}}' 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)" \ "$(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_token='0123456789abcdef0123456789abcdef'
setup_host='https://pve1.example.test:8006' 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_failure "Pulse VLAN zero rejected" TAPM_PULSE_VALID_OPTIONAL_VLAN 0
assert_success "blank Pulse VLAN accepted" TAPM_PULSE_VALID_OPTIONAL_VLAN '' 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='[ resources='[
{"type":"lxc","name":"pulse-a","tags":"tapm;pulse"}, {"type":"lxc","name":"pulse-a","tags":"tapm;pulse"},
{"type":"qemu","name":"unrelated"} {"type":"qemu","name":"unrelated"}