diff --git a/internal/api/unified_agent_download_test.go b/internal/api/unified_agent_download_test.go index 46bcd46a9..d5f2a0003 100644 --- a/internal/api/unified_agent_download_test.go +++ b/internal/api/unified_agent_download_test.go @@ -56,3 +56,32 @@ func TestHandleDownloadUnifiedAgentSetsChecksumAndInvalidatesOnChange(t *testing t.Fatalf("unexpected response body after update") } } + +func TestHandleDownloadUnifiedAgentAllowsVersionCacheKeyQuery(t *testing.T) { + binDir := setupTempPulseBin(t) + filePath := filepath.Join(binDir, "pulse-agent-linux-amd64") + + payload := []byte("agent-binary-v1") + if err := os.WriteFile(filePath, payload, 0o755); err != nil { + t.Fatalf("failed to write test binary: %v", err) + } + + req := httptest.NewRequest(http.MethodGet, "/download/pulse-agent?arch=linux-amd64&serverVersion=v5.1.27-rc.1", nil) + rr := httptest.NewRecorder() + + router := &Router{checksumCache: make(map[string]checksumCacheEntry)} + router.handleDownloadUnifiedAgent(rr, req) + + if rr.Code != http.StatusOK { + t.Fatalf("expected 200 OK, got %d", rr.Code) + } + + expected := fmt.Sprintf("%x", sha256.Sum256(payload)) + if got := rr.Header().Get("X-Checksum-Sha256"); got != expected { + t.Fatalf("unexpected checksum header: got %q want %q", got, expected) + } + + if strings.TrimSpace(rr.Body.String()) != string(payload) { + t.Fatalf("unexpected response body") + } +} diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 376e69383..d0031a8ce 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -234,7 +234,27 @@ $Url = $Url.TrimEnd('/') # Determine architecture $Arch = if ([Environment]::Is64BitOperatingSystem) { "amd64" } else { "386" } $ArchParam = "windows-$Arch" +$ServerVersion = "" + +try { + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13 + $versionClient = New-Object System.Net.WebClient + $versionClient.Headers.Add("User-Agent", "PulseInstaller/1.0") + $versionInfo = ($versionClient.DownloadString("$Url/api/version") | ConvertFrom-Json) + if ($versionInfo -and $versionInfo.version) { + $ServerVersion = [string]$versionInfo.version + Write-Host "Pulse server version: $ServerVersion" -ForegroundColor Cyan + } +} catch { +} finally { + if ($versionClient) { $versionClient.Dispose() } +} + $DownloadUrl = "$Url/download/pulse-agent?arch=$ArchParam" +if (-not [string]::IsNullOrWhiteSpace($ServerVersion)) { + $escapedServerVersion = [Uri]::EscapeDataString($ServerVersion) + $DownloadUrl = "$DownloadUrl&serverVersion=$escapedServerVersion" +} Write-Host "Downloading agent from $DownloadUrl..." -ForegroundColor Cyan if (-not (Test-Path $InstallDir)) { @@ -278,6 +298,17 @@ try { if ($webClient) { $webClient.Dispose() } } +$DownloadedVersion = "" +try { + $DownloadedVersion = ((& $TempPath --version 2>$null) | Select-Object -First 1).Trim() +} catch { + $DownloadedVersion = "" +} + +if (-not [string]::IsNullOrWhiteSpace($ServerVersion) -and -not [string]::IsNullOrWhiteSpace($DownloadedVersion) -and $DownloadedVersion -ne $ServerVersion) { + Write-Host "Warning: downloaded agent version ($DownloadedVersion) does not match Pulse server version ($ServerVersion). Check that Pulse is upgraded and that any reverse proxy is not serving a stale cached binary." -ForegroundColor Yellow +} + # --- Binary Verification --- Write-Host "Verifying downloaded binary..." -ForegroundColor Cyan diff --git a/scripts/install.sh b/scripts/install.sh index a8064cfd7..e94218831 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1025,9 +1025,6 @@ esac # Construct arch param in format expected by download endpoint (e.g., linux-amd64) ARCH_PARAM="${OS}-${ARCH}" -DOWNLOAD_URL="${PULSE_URL}/download/${BINARY_NAME}?arch=${ARCH_PARAM}" -log_info "Downloading agent from ${DOWNLOAD_URL}..." - # Create temp file and register for cleanup TMP_BIN=$(mktemp) TMP_FILES+=("$TMP_BIN") @@ -1037,6 +1034,20 @@ CURL_ARGS=(-fsSL --connect-timeout 30 --max-time 300) if [[ "$INSECURE" == "true" ]]; then CURL_ARGS+=(-k); fi if [[ -n "$CURL_CA_BUNDLE" ]]; then CURL_ARGS+=(--cacert "$CURL_CA_BUNDLE"); fi +SERVER_VERSION="" +if server_version_json="$(curl "${CURL_ARGS[@]}" "${PULSE_URL}/api/version" 2>/dev/null)"; then + SERVER_VERSION="$(printf '%s' "$server_version_json" | sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)" +fi + +DOWNLOAD_QUERY="arch=${ARCH_PARAM}" +if [[ -n "$SERVER_VERSION" ]]; then + DOWNLOAD_QUERY="${DOWNLOAD_QUERY}&serverVersion=${SERVER_VERSION}" + log_info "Pulse server version: ${SERVER_VERSION}" +fi + +DOWNLOAD_URL="${PULSE_URL}/download/${BINARY_NAME}?${DOWNLOAD_QUERY}" +log_info "Downloading agent from ${DOWNLOAD_URL}..." + if ! curl "${CURL_ARGS[@]}" -o "$TMP_BIN" "$DOWNLOAD_URL"; then fail "Download failed. Check URL and connectivity." fi @@ -1060,6 +1071,11 @@ elif [[ "$OS" == "darwin" ]]; then fi chmod +x "$TMP_BIN" +NEW_VERSION=$("$TMP_BIN" --version 2>/dev/null | head -1 || echo "unknown") + +if [[ -n "$SERVER_VERSION" && -n "$NEW_VERSION" && "$NEW_VERSION" != "unknown" && "$NEW_VERSION" != "$SERVER_VERSION" ]]; then + log_warn "Downloaded agent version (${NEW_VERSION}) does not match Pulse server version (${SERVER_VERSION}). Check that Pulse is upgraded and that any reverse proxy is not serving a stale cached binary." +fi # --- Upgrade Detection --- # Check if pulse-agent is already installed and handle upgrade gracefully @@ -1075,8 +1091,7 @@ fi if [[ -x "$UPGRADE_CHECK_BIN" ]]; then EXISTING_VERSION=$("$UPGRADE_CHECK_BIN" --version 2>/dev/null | head -1 || echo "unknown") - NEW_VERSION=$("$TMP_BIN" --version 2>/dev/null | head -1 || echo "unknown") - + if [[ -n "$EXISTING_VERSION" && "$EXISTING_VERSION" != "unknown" ]]; then UPGRADE_MODE=true log_info "Existing installation detected: $EXISTING_VERSION"