Public source validation / validate (push) Failing after 3m8s
249 lines
8.5 KiB
Markdown
249 lines
8.5 KiB
Markdown
# Security threat model
|
|
|
|
## 1. Assets
|
|
|
|
- Unraid host and storage.
|
|
- Docker/container metadata and internal topology.
|
|
- Prometheus metrics and labels.
|
|
- Service URLs and availability data.
|
|
- OIDC identities/roles.
|
|
- Pulse configuration, alerts, incidents and audit.
|
|
- Notification/probe credentials.
|
|
- Database backups.
|
|
- Server access path used by Codex during deployment.
|
|
|
|
## 2. Trust boundaries
|
|
|
|
- Browser to Pulse.
|
|
- Pulse to Authentik.
|
|
- Pulse to PostgreSQL.
|
|
- Pulse to Prometheus.
|
|
- Pulse agent to host/Unraid/Docker.
|
|
- Probe worker to network targets.
|
|
- Notification worker to external channels.
|
|
- Codex workspace to production server.
|
|
|
|
## 3. Primary threats and controls
|
|
|
|
### M0 discovered deployment posture
|
|
|
|
The target host already runs Nginx Proxy Manager, Authentik, Grafana, and multiple Docker/Compose projects. Pulse treats all of them as external protected resources. Existing containers with broad privileges, including any Docker socket access, are not reused as a Pulse pattern. Pulse must be isolated on dedicated resources, integrate with the proxy and OIDC provider additively, and remain server-side for all Unraid/Prometheus access.
|
|
|
|
M0 did not identify a local Prometheus service; this is an explicit datasource uncertainty, not permission to substitute an unreviewed source. Missing or stale telemetry must map to `Unknown`. No hostname, port, network, volume, or path is trusted until the deployment task rechecks ownership and conflicts.
|
|
|
|
### Unrestricted Docker/host control
|
|
|
|
Threat: compromise of web/API leads to host root-equivalent access.
|
|
|
|
Controls:
|
|
- no unrestricted socket in web/API;
|
|
- separate agent/proxy;
|
|
- endpoint allowlist;
|
|
- read-only capability contract;
|
|
- non-root API;
|
|
- network separation;
|
|
- architecture test of compose/mounts;
|
|
- no mutation API in v1.
|
|
|
|
### SSRF from service probes
|
|
|
|
Threat: user config probes metadata, loopback, admin services or redirects.
|
|
|
|
Controls:
|
|
- role restriction;
|
|
- scheme/port allowlist;
|
|
- DNS resolution validation before and after redirect;
|
|
- block metadata/link-local/unspecified by default;
|
|
- configurable LAN allowlist;
|
|
- response size/time limits;
|
|
- no arbitrary methods/body;
|
|
- redacted logging;
|
|
- tests for DNS rebinding/redirect escape.
|
|
|
|
### Query abuse
|
|
|
|
Threat: expensive or injection-like Prometheus queries cause outage or expose labels.
|
|
|
|
Controls:
|
|
- semantic query templates;
|
|
- bounded scope/range/series/points;
|
|
- server-side parameterization/escaping;
|
|
- timeout/concurrency/rate limit;
|
|
- advanced raw query separate permission;
|
|
- audit and query cost metrics.
|
|
|
|
|
|
### Optional hardware capabilities
|
|
|
|
Hardware sensors and GPU telemetry are optional read-only capabilities. The adapter accepts only normalized bounded snapshots from an approved source; absent support is disabled and unsupported support remains inspectable without being treated as a host failure. API/web never gains device, mount, namespace or Docker-socket access for these values.
|
|
### Authentication/authorization bypass
|
|
|
|
Controls:
|
|
- standards-based OIDC validation;
|
|
- issuer/audience/nonce/state/PKCE;
|
|
- secure cookies;
|
|
- server-side RBAC;
|
|
- WebSocket auth/origin/subscription auth;
|
|
- CSRF protection where cookies are used;
|
|
- role matrix tests;
|
|
- break-glass disabled by default.
|
|
|
|
### XSS and dashboard import
|
|
|
|
Controls:
|
|
- no arbitrary HTML/JS widgets;
|
|
- sanitize Markdown;
|
|
- schema validation;
|
|
- safe chart labels/tooltips;
|
|
- CSP;
|
|
- escaped event/upstream text;
|
|
- import size and complexity limits.
|
|
|
|
### Secret leakage
|
|
|
|
Controls:
|
|
- external secret injection;
|
|
- encrypted-at-rest channel/probe references;
|
|
- redaction middleware;
|
|
- no env dumps;
|
|
- evidence policy;
|
|
- secret scan;
|
|
- diagnostic bundle allowlist;
|
|
- backups exclude plaintext or are encrypted/secured.
|
|
|
|
### Supply chain
|
|
|
|
Controls:
|
|
- lockfiles;
|
|
- minimal maintained dependencies;
|
|
- provenance/SBOM where feasible;
|
|
- vulnerability scanning;
|
|
- pinned base images;
|
|
- non-root runtime;
|
|
- update policy;
|
|
- build in CI/clean environment.
|
|
|
|
### Database compromise/data integrity
|
|
|
|
Controls:
|
|
- isolated network;
|
|
- dedicated credentials;
|
|
- TLS when remote;
|
|
- least privilege;
|
|
- migrations/transactions;
|
|
- backup/restore;
|
|
- input validation;
|
|
- audit;
|
|
- no exposed database port unless controlled testing override.
|
|
|
|
### Live/WebSocket abuse
|
|
|
|
Controls:
|
|
- authentication before upgrade;
|
|
- opaque HttpOnly sessions with an eight-hour sliding idle limit and a finite,
|
|
operator-bounded absolute limit; renewal never exposes OIDC tokens to the
|
|
browser, while a revocable session context propagates through HTTP upgrades
|
|
so logout, absolute expiry and request/server cancellation also close an
|
|
already established socket and release its subscriptions;
|
|
- origin policy;
|
|
- message/rate/size limits;
|
|
- max subscriptions/series;
|
|
- idle timeout/heartbeat;
|
|
- bounded send queue and slow-client eviction;
|
|
- no secret data in messages.
|
|
|
|
### Alert/notification abuse
|
|
|
|
Controls:
|
|
- RBAC and audit;
|
|
- versioned rules;
|
|
- safe templates;
|
|
- channel test rate limits;
|
|
- idempotency;
|
|
- recipient allowlist/policy;
|
|
- no secret values in notification body.
|
|
|
|
### Deployment mistakes
|
|
|
|
Controls:
|
|
- discovery and port/network/volume conflict checks;
|
|
- backup touched configs;
|
|
- isolated compose project;
|
|
- offline validation;
|
|
- health/smoke tests;
|
|
- rollback;
|
|
- no prune/delete/unrelated modifications;
|
|
- production evidence.
|
|
|
|
## 4. Security headers
|
|
|
|
At minimum:
|
|
- Content-Security-Policy;
|
|
- frame restrictions;
|
|
- nosniff;
|
|
- strict referrer policy;
|
|
- permissions policy;
|
|
- HSTS when HTTPS deployment is stable;
|
|
- secure/same-site/httpOnly cookies.
|
|
|
|
## 5. Container hardening
|
|
|
|
Where compatible:
|
|
- non-root;
|
|
- read-only root filesystem;
|
|
- tmpfs for temporary paths;
|
|
- drop all capabilities, add only required;
|
|
- no-new-privileges;
|
|
- seccomp/default profile;
|
|
- resource limits;
|
|
- explicit networks;
|
|
- no public database/collector ports;
|
|
- healthchecks;
|
|
- immutable image digest in production record.
|
|
|
|
The agent may need narrow exceptions; document and test each.
|
|
|
|
### Deployment hardening pass (2026-08-04)
|
|
|
|
- Resource limits are set on all six `deploy/compose.yaml` services via the
|
|
non-swarm `cpus`/`mem_limit`/`mem_reservation`/`memswap_limit` keys (the
|
|
project runs plain `docker compose up`, not swarm); sizing rationale is
|
|
inline in that file against `docs/architecture/SYSTEM_ARCHITECTURE.md` §7.
|
|
- `pulse-postgres` now runs `read_only: true` with tmpfs for `/tmp` and
|
|
`/var/run/postgresql`; all six services are now read-only-root. This
|
|
closes the previously undocumented exception; see
|
|
`docs/operations/DEPLOYMENT_UNRAID.md` §6/§8 for the required smoke test.
|
|
- Immutable image digests are enforced by `deploy/verify-image-digests.sh`
|
|
and CI. Every external registry image in `deploy/*.Dockerfile` is pinned to
|
|
a verified digest; only Docker's built-in `scratch` rootfs is exempt because
|
|
it has no registry manifest. See `deploy/IMAGE_DIGESTS.md` for the ledger.
|
|
- `pulse-worker`/`pulse-agent` healthchecks now verify a heartbeat file's
|
|
freshness instead of `kill -0 1`, and self-restart the container on
|
|
staleness (`docker compose up` does not restart on "unhealthy" status
|
|
alone). Contract for the Go runtime:
|
|
`docs/operations/WORKER_AGENT_HEALTHCHECK_CONTRACT.md`.
|
|
- `deploy/nginx.conf` now sends `Strict-Transport-Security` from
|
|
`pulse-web` as defence-in-depth (§4), verified not to conflict with
|
|
TLS terminating at Nginx Proxy Manager per ADR-0010.
|
|
|
|
## 6. Security acceptance
|
|
|
|
Required:
|
|
- threat model review at M0 and M9;
|
|
- SAST/dependency/image/secret scans;
|
|
- auth/RBAC matrix tests;
|
|
- SSRF suite;
|
|
- WebSocket security suite;
|
|
- dashboard import/XSS suite;
|
|
- query limit/validation suite;
|
|
- compose privilege/mount test;
|
|
- backup secret inspection;
|
|
- production exposure scan from permitted network.
|
|
|
|
### M7-02 implementation
|
|
|
|
The probe policy validates scheme, host and port before resolution, rejects loopback/link-local/metadata/multicast/unspecified addresses, and requires explicit CIDR permission for private LAN targets. The safe client repeats resolution at dial time and revalidates redirect destinations. Only bounded GET/HEAD requests with an allowlisted header set are accepted; responses are size/time limited. Policy changes are audit events containing no credentials or full request data.
|
|
### M8-08 notification controls
|
|
|
|
Notification channel persistence accepts only secret references and rejects sensitive configuration keys such as token, password, secret, and authorization. Delivery bodies and subjects are bounded. Sender errors are newline-normalized, length-bounded, and redacted before persistence; bearer credentials are removed as a complete value. Test sends are rate limited and never write delivery records.
|