Fix host agent registration verification issues (#746)
- Change default server listen addresses to empty string (listen on all interfaces including IPv6) - Add short hostname matching fallback in host lookup API to handle FQDN vs short name mismatches - Implement retry loop (30s) in both Windows and Linux/macOS installers for registration verification - Fix lint errors: remove unnecessary fmt.Sprintf and nil checks before len() This resolves the 'Installer could not yet confirm host registration with Pulse' warning by addressing timing issues, hostname matching, and network connectivity.
This commit is contained in:
parent
efad01b717
commit
07d8381858
7 changed files with 71 additions and 20 deletions
|
|
@ -455,11 +455,24 @@ if (-not $NoService) {
|
|||
if ($status -eq 'Running') {
|
||||
PulseSuccess "Service started successfully!"
|
||||
|
||||
PulseInfo "Waiting 10 seconds to validate agent reporting..."
|
||||
Start-Sleep -Seconds 10
|
||||
PulseInfo "Waiting for agent to register with Pulse (up to 30s)..."
|
||||
|
||||
$maxRetries = 15
|
||||
$retryDelay = 2
|
||||
$lookupHost = $null
|
||||
|
||||
for ($i = 1; $i -le $maxRetries; $i++) {
|
||||
Start-Sleep -Seconds $retryDelay
|
||||
$hostname = $env:COMPUTERNAME
|
||||
$lookupHost = Test-AgentRegistration -PulseUrl $PulseUrl -Hostname $hostname -Token $Token
|
||||
|
||||
if ($lookupHost) {
|
||||
break
|
||||
}
|
||||
Write-Host "." -NoNewline
|
||||
}
|
||||
Write-Host "" # Newline after dots
|
||||
|
||||
$hostname = $env:COMPUTERNAME
|
||||
$lookupHost = Test-AgentRegistration -PulseUrl $PulseUrl -Hostname $hostname -Token $Token
|
||||
if ($lookupHost) {
|
||||
PulseSuccess "Agent successfully registered with Pulse (host '$hostname')."
|
||||
if ($lookupHost.status) {
|
||||
|
|
|
|||
|
|
@ -482,7 +482,7 @@ func CheckAuth(cfg *config.Config, w http.ResponseWriter, r *http.Request) bool
|
|||
w.Write([]byte(fmt.Sprintf(`{"error":"Invalid credentials","attempts":%d,"remaining":%d,"maxAttempts":%d}`,
|
||||
attempts, remaining, maxFailedAttempts)))
|
||||
} else {
|
||||
w.Write([]byte(fmt.Sprintf(`{"error":"Invalid credentials","locked":true,"message":"Account locked for 15 minutes"}`)))
|
||||
w.Write([]byte(`{"error":"Invalid credentials","locked":true,"message":"Account locked for 15 minutes"}`))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ func (h *DockerAgentHandlers) HandleReport(w http.ResponseWriter, r *http.Reques
|
|||
"id": cmd.ID,
|
||||
"type": cmd.Type,
|
||||
}
|
||||
if payload != nil && len(payload) > 0 {
|
||||
if len(payload) > 0 {
|
||||
commandResponse["payload"] = payload
|
||||
}
|
||||
response["commands"] = []map[string]any{commandResponse}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ func (h *HostAgentHandlers) HandleLookup(w http.ResponseWriter, r *http.Request)
|
|||
}
|
||||
|
||||
if !found && hostname != "" {
|
||||
// First pass: exact match (case-insensitive)
|
||||
for _, candidate := range state.Hosts {
|
||||
if strings.EqualFold(candidate.Hostname, hostname) || strings.EqualFold(candidate.DisplayName, hostname) {
|
||||
host = candidate
|
||||
|
|
@ -119,6 +120,26 @@ func (h *HostAgentHandlers) HandleLookup(w http.ResponseWriter, r *http.Request)
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: short hostname match (if exact match failed)
|
||||
if !found {
|
||||
// Helper to get short hostname (before first dot)
|
||||
getShortName := func(h string) string {
|
||||
if idx := strings.Index(h, "."); idx != -1 {
|
||||
return h[:idx]
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
shortLookup := getShortName(hostname)
|
||||
for _, candidate := range state.Hosts {
|
||||
if strings.EqualFold(getShortName(candidate.Hostname), shortLookup) {
|
||||
host = candidate
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ func (h *NotificationHandlers) GetWebhooks(w http.ResponseWriter, r *http.Reques
|
|||
}
|
||||
|
||||
// Mask headers - only show keys, not values
|
||||
if webhook.Headers != nil && len(webhook.Headers) > 0 {
|
||||
if len(webhook.Headers) > 0 {
|
||||
maskedHeaders := make(map[string]string)
|
||||
for key := range webhook.Headers {
|
||||
maskedHeaders[key] = "***REDACTED***"
|
||||
|
|
@ -165,7 +165,7 @@ func (h *NotificationHandlers) GetWebhooks(w http.ResponseWriter, r *http.Reques
|
|||
}
|
||||
|
||||
// Mask custom fields - only show keys, not values
|
||||
if webhook.CustomFields != nil && len(webhook.CustomFields) > 0 {
|
||||
if len(webhook.CustomFields) > 0 {
|
||||
maskedFields := make(map[string]string)
|
||||
for key := range webhook.CustomFields {
|
||||
maskedFields[key] = "***REDACTED***"
|
||||
|
|
|
|||
|
|
@ -71,9 +71,9 @@ func IsPasswordHashed(password string) bool {
|
|||
// NOTE: The envconfig tags are legacy and not used - configuration is loaded from encrypted JSON files
|
||||
type Config struct {
|
||||
// Server settings
|
||||
BackendHost string `envconfig:"BACKEND_HOST" default:"0.0.0.0"`
|
||||
BackendHost string `envconfig:"BACKEND_HOST" default:""`
|
||||
BackendPort int `envconfig:"BACKEND_PORT" default:"3000"`
|
||||
FrontendHost string `envconfig:"FRONTEND_HOST" default:"0.0.0.0"`
|
||||
FrontendHost string `envconfig:"FRONTEND_HOST" default:""`
|
||||
FrontendPort int `envconfig:"FRONTEND_PORT" default:"7655"`
|
||||
ConfigPath string `envconfig:"CONFIG_PATH" default:"/etc/pulse"`
|
||||
DataPath string `envconfig:"DATA_PATH" default:"/var/lib/pulse"`
|
||||
|
|
@ -523,9 +523,9 @@ func Load() (*Config, error) {
|
|||
|
||||
// Initialize config with defaults
|
||||
cfg := &Config{
|
||||
BackendHost: "0.0.0.0",
|
||||
BackendHost: "",
|
||||
BackendPort: 3000,
|
||||
FrontendHost: "0.0.0.0",
|
||||
FrontendHost: "",
|
||||
FrontendPort: 7655,
|
||||
ConfigPath: dataDir,
|
||||
DataPath: dataDir,
|
||||
|
|
|
|||
|
|
@ -1087,14 +1087,31 @@ if [[ "$SERVICE_MODE" != "manual" && "$SERVICE_RUNNING" == true ]]; then
|
|||
if [[ -z "$PULSE_TOKEN" ]]; then
|
||||
log_info "Registration check skipped (no API token available for lookup)."
|
||||
elif command -v curl &> /dev/null; then
|
||||
log_info "Verifying agent registration with Pulse server..."
|
||||
LOOKUP_RESPONSE=$(curl -fsSL \
|
||||
-H "Authorization: Bearer $PULSE_TOKEN" \
|
||||
--get \
|
||||
--data-urlencode "hostname=$HOSTNAME" \
|
||||
"$PULSE_URL/api/agents/host/lookup" 2>/dev/null || true)
|
||||
log_info "Verifying agent registration with Pulse server (up to 30s)..."
|
||||
|
||||
MAX_RETRIES=15
|
||||
RETRY_DELAY=2
|
||||
LOOKUP_SUCCESS=false
|
||||
|
||||
for ((i=1; i<=MAX_RETRIES; i++)); do
|
||||
LOOKUP_RESPONSE=$(curl -fsSL \
|
||||
-H "Authorization: Bearer $PULSE_TOKEN" \
|
||||
--get \
|
||||
--data-urlencode "hostname=$HOSTNAME" \
|
||||
"$PULSE_URL/api/agents/host/lookup" 2>/dev/null || true)
|
||||
|
||||
if [[ "$LOOKUP_RESPONSE" == *'"success":true'* ]]; then
|
||||
if [[ "$LOOKUP_RESPONSE" == *'"success":true'* ]]; then
|
||||
LOOKUP_SUCCESS=true
|
||||
break
|
||||
fi
|
||||
|
||||
# Print a dot without newline to show progress
|
||||
printf "."
|
||||
sleep $RETRY_DELAY
|
||||
done
|
||||
echo "" # Newline after dots
|
||||
|
||||
if [[ "$LOOKUP_SUCCESS" == true ]]; then
|
||||
host_status=$(printf '%s' "$LOOKUP_RESPONSE" | sed -n 's/.*"status":"\([^"]*\)".*/\1/p')
|
||||
last_seen=$(printf '%s' "$LOOKUP_RESPONSE" | sed -n 's/.*"lastSeen":"\([^"]*\)".*/\1/p')
|
||||
log_success "Agent successfully registered with Pulse server!"
|
||||
|
|
@ -1102,7 +1119,7 @@ if [[ "$SERVICE_MODE" != "manual" && "$SERVICE_RUNNING" == true ]]; then
|
|||
log_info "Pulse reports status: $host_status (last seen $last_seen)"
|
||||
fi
|
||||
else
|
||||
log_warn "Agent lookup did not confirm registration yet (response: ${LOOKUP_RESPONSE:-no data})."
|
||||
log_warn "Agent lookup did not confirm registration after 30s (response: ${LOOKUP_RESPONSE:-no data})."
|
||||
log_info "Service is running; metrics should appear shortly."
|
||||
fi
|
||||
else
|
||||
|
|
|
|||
Loading…
Reference in a new issue