From f527e6ebd0985159fb06aaf286e2736814e7c336 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Sun, 11 Jan 2026 21:43:23 +0000 Subject: [PATCH] docs: fix Kubernetes DaemonSet deployment guide Fixes #1091 - addresses all three documentation issues reported: 1. Binary path: Changed from /usr/local/bin/pulse-agent (which doesn't exist in the main image) to /opt/pulse/bin/pulse-agent-linux-amd64 2. PULSE_AGENT_ID: Added to example and documented why it's required for DaemonSets (prevents token conflicts when all pods share one API token) 3. Resource visibility flags: Added PULSE_KUBE_INCLUDE_ALL_PODS and PULSE_KUBE_INCLUDE_ALL_DEPLOYMENTS to example, with explanation of the default behavior (show only problematic resources) Also added tolerations, resource requests/limits, and ARM64 note. --- docs/KUBERNETES.md | 56 ++++++- frontend-modern/src/components/Login.tsx | 111 ++++++++++++-- .../src/components/Settings/Settings.tsx | 3 + frontend-modern/src/types/config.ts | 15 ++ go.mod | 7 + go.sum | 26 ++++ internal/api/audit_handlers.go | 137 +++++++++++++++++ internal/api/router.go | 74 +++++++++ internal/config/persistence.go | 143 ++++++++++++++++++ pkg/server/server.go | 33 ++++ 10 files changed, 589 insertions(+), 16 deletions(-) diff --git a/docs/KUBERNETES.md b/docs/KUBERNETES.md index 3e1ac3a68c..29dd1546f6 100644 --- a/docs/KUBERNETES.md +++ b/docs/KUBERNETES.md @@ -89,7 +89,7 @@ spec: containers: - name: pulse-agent image: rcourtman/pulse:latest - command: ["/usr/local/bin/pulse-agent"] + command: ["/opt/pulse/bin/pulse-agent-linux-amd64"] args: - --enable-kubernetes env: @@ -97,18 +97,70 @@ spec: value: "http://pulse-server.pulse.svc.cluster.local:7655" - name: PULSE_TOKEN value: "YOUR_API_TOKEN_HERE" + - name: PULSE_AGENT_ID + value: "my-k8s-cluster" - name: PULSE_ENABLE_HOST value: "false" + - name: PULSE_KUBE_INCLUDE_ALL_PODS + value: "true" + - name: PULSE_KUBE_INCLUDE_ALL_DEPLOYMENTS + value: "true" securityContext: readOnlyRootFilesystem: true allowPrivilegeEscalation: false - resources: {} + resources: + requests: + cpu: 50m + memory: 128Mi + limits: + memory: 512Mi + tolerations: + - operator: Exists ``` +> **Note for ARM64 clusters**: Replace `pulse-agent-linux-amd64` with `pulse-agent-linux-arm64`. + Use a token scoped for the agent: - `kubernetes:report` for Kubernetes reporting - `host-agent:report` if you enable host metrics +#### Important DaemonSet Configuration + +**PULSE_AGENT_ID (Required for DaemonSets)** + +When running as a DaemonSet, all pods share the same API token but need a unified identity. Without `PULSE_AGENT_ID`, each pod auto-generates a unique ID (e.g., `mac-xxxxx`), causing token conflicts: + +``` +API token is already in use by agent "mac-aa5496fed726". Each Kubernetes agent must use a unique API token. +``` + +Set `PULSE_AGENT_ID` to a shared cluster name so all pods report as one logical agent: + +```yaml +- name: PULSE_AGENT_ID + value: "my-k8s-cluster" +``` + +**Resource Visibility Flags** + +By default, Pulse only shows resources with problems (unhealthy pods, failing deployments). To see all resources: + +| Environment Variable | Description | Default | +|---------------------|-------------|---------| +| `PULSE_KUBE_INCLUDE_ALL_PODS` | Show all non-succeeded pods, not just problematic ones | `false` | +| `PULSE_KUBE_INCLUDE_ALL_DEPLOYMENTS` | Show all deployments, not just those with issues | `false` | + +For most monitoring use cases, set both to `true`: + +```yaml +- name: PULSE_KUBE_INCLUDE_ALL_PODS + value: "true" +- name: PULSE_KUBE_INCLUDE_ALL_DEPLOYMENTS + value: "true" +``` + +See [UNIFIED_AGENT.md](UNIFIED_AGENT.md) for all available configuration options. + #### Add Host Metrics (Optional) If you want node CPU/memory/disk metrics, add privileged mode plus host mounts: diff --git a/frontend-modern/src/components/Login.tsx b/frontend-modern/src/components/Login.tsx index 6e1870e60e..83f2391cb3 100644 --- a/frontend-modern/src/components/Login.tsx +++ b/frontend-modern/src/components/Login.tsx @@ -1,7 +1,9 @@ -import { Component, createSignal, Show, onMount, lazy, Suspense } from 'solid-js'; +import { Component, createSignal, Show, For, onMount, lazy, Suspense } from 'solid-js'; import { logger } from '@/utils/logger'; import { apiClient, apiFetchJSON } from '@/utils/apiClient'; import { STORAGE_KEYS } from '@/utils/localStorage'; +import Globe from 'lucide-solid/icons/globe'; +import Key from 'lucide-solid/icons/key'; const SetupWizard = lazy(() => import('./SetupWizard').then((m) => ({ default: m.SetupWizard })), @@ -13,7 +15,7 @@ interface LoginProps { securityStatus?: SecurityStatus; // Full security status from App.tsx to avoid redundant API call } -import type { SecurityStatus } from '@/types/config'; +import type { SecurityStatus, SSOProviderInfo } from '@/types/config'; export const Login: Component = (props) => { const [username, setUsername] = createSignal(''); @@ -30,8 +32,10 @@ export const Login: Component = (props) => { const [oidcMessage, setOidcMessage] = createSignal(''); const supportsOIDC = () => Boolean(authStatus()?.oidcEnabled); + const ssoProviders = () => authStatus()?.ssoProviders || []; + const hasMultipleProviders = () => ssoProviders().length > 0; - const resolveOidcError = (reason?: string | null) => { + const resolveSSOError = (reason?: string | null) => { switch (reason) { case 'email_restricted': return 'Your account email is not permitted to access Pulse.'; @@ -51,6 +55,12 @@ export const Login: Component = (props) => { return 'The identity provider is issuing HS256 tokens. Configure it to sign ID tokens with RS256 (see your IdP\'s OIDC settings).'; case 'invalid_nonce': return 'Security validation failed (nonce mismatch). Please try again.'; + case 'saml_validation_failed': + return 'SAML assertion validation failed. Please try again or contact an administrator.'; + case 'provider_not_found': + return 'SSO provider not found or not configured.'; + case 'provider_not_initialized': + return 'SSO provider is not properly configured. Contact your administrator.'; default: return 'Single sign-on failed. Please try again or contact an administrator.'; } @@ -73,18 +83,35 @@ export const Login: Component = (props) => { } const params = new URLSearchParams(window.location.search); + + // Handle OIDC callback const oidcStatus = params.get('oidc'); if (oidcStatus === 'error') { const reason = params.get('oidc_error'); - setOidcError(resolveOidcError(reason)); + setOidcError(resolveSSOError(reason)); setError(''); } else if (oidcStatus === 'success') { setOidcMessage('Signed in successfully. Loading Pulse…'); setError(''); } - if (oidcStatus) { + + // Handle SAML callback + const samlStatus = params.get('saml'); + if (samlStatus === 'error') { + const reason = params.get('saml_error'); + setOidcError(resolveSSOError(reason)); + setError(''); + } else if (samlStatus === 'success') { + setOidcMessage('Signed in successfully. Loading Pulse…'); + setError(''); + } + + // Clean up URL parameters + if (oidcStatus || samlStatus) { params.delete('oidc'); params.delete('oidc_error'); + params.delete('saml'); + params.delete('saml_error'); const newQuery = params.toString(); const newUrl = `${window.location.pathname}${newQuery ? `?${newQuery}` : ''}`; window.history.replaceState({}, document.title, newUrl); @@ -304,6 +331,7 @@ export const Login: Component = (props) => { oidcError, oidcMessage, showLocalLogin: shouldShowLocalLogin(), + ssoProviders: ssoProviders(), }} /> } @@ -344,6 +372,7 @@ const LoginForm: Component<{ oidcError: () => string; oidcMessage: () => string; showLocalLogin: boolean; + ssoProviders: SSOProviderInfo[]; }> = (props) => { const { username, @@ -361,6 +390,7 @@ const LoginForm: Component<{ oidcError, oidcMessage, showLocalLogin, + ssoProviders, } = props; // Check if we're on the demo server @@ -417,7 +447,67 @@ const LoginForm: Component<{ class="mt-8 space-y-6 bg-white/80 dark:bg-gray-800/80 backdrop-blur-lg rounded-lg p-8 shadow-xl animate-slide-up" onSubmit={handleSubmit} > - + {/* Multi-Provider SSO Section */} + 0}> +
+ + {(provider) => ( + + )} + + +
+ {oidcError()} +
+
+ +
+ {oidcMessage()} +
+
+ +
+ + + or + + +
+

+ Use your admin credentials to sign in below. +

+
+
+
+ {/* Legacy OIDC fallback (when no multi-provider SSO but legacy OIDC is enabled) */} +