142 lines
9.5 KiB
Markdown
142 lines
9.5 KiB
Markdown
# Packet Station security
|
|||
|
|
|
||
|
|
This document is rewritten for the server build. The old browser build's security document is in the old repo (`C:\Users\spencerm\Coding\PacketStation\docs\SECURITY.md`). The design comes from [BRIEF.md](BRIEF.md) and [PLAN.md](PLAN.md).
|
||
|
|
|
||
|
|
Status: phase 0. Most controls below are designed, not built. Each one names the phase that builds it.
|
||
|
|
|
||
|
|
Contents:
|
||
|
|
|
||
|
|
- Part 1: the 20 old layers and where each one lives now.
|
||
|
|
- Part 2: the new build's controls.
|
||
|
|
- Part 3: the production requirement IDs and the phase that closes each.
|
||
|
|
|
||
|
|
## Part 1: the old layers, moved to the server
|
||
|
|
|
||
|
|
The M: drive build had 20 layers of protection, most of them enforced in the browser. Every layer that still applies moves to the server. The rest are dropped or replaced.
|
||
|
|
|
||
|
|
| # | Old layer | Now | Phase |
|
||
|
|
|---|---|---|---|
|
||
|
|
| 1 | PESCO network and firewall | Kept. The app runs on `docker-1` inside the PESCO network, reached only through Nginx Proxy Manager. It publishes no port. PostgreSQL is on each stack's internal network only. | 0 |
|
||
|
|
| 2 | Windows folder rights on the M: folder | Dropped. Replaced by database roles: the app role cannot change the schema or update or delete the audit; migrations run as a separate role. Files sit on a volume only the containers see. | 0 |
|
||
|
|
| 3 | Employee ID + PIN sign-in | Replaced by AD sign-in with the domain password, plus an optional Packet Station PIN checked on the server, for registered shop devices only. | 1 |
|
||
|
|
| 4 | Department and level screens | A static role-to-screen map in the browser, plus server policy on every request. The browser only decides what to show. | 1, 2 |
|
||
|
|
| 5 | Per-PC access log | Replaced by the server audit. Each row carries the AD account, session, client IP and user agent. No per-PC browser ids. | 1 |
|
||
|
|
| 6 | Filing seals and filed-packet lock | HMAC seals computed on the server with a server key. The filed lock is a server policy rule. | 7 |
|
||
|
|
| 7 | Record integrity check | A worker job (seals, asset hashes, audit hash chain) with an alert. | 9 |
|
||
|
|
| 8 | Self-hosted libraries | Kept: no CDN. The CSP allows nothing external. | 0 |
|
||
|
|
| 9 | Output escaping | React escapes output by default. The PDF export escapes on the server. | 1, 7 |
|
||
|
|
| 10 | Central permission policy | Server policy, checked on every write, including writes that do not come from the screens. | 2 |
|
||
|
|
| 11 | PIN sign-off (electronic signature) | Server-verified signature with the PIN or the domain password, stored with who, when, what, factor and a content hash. | 1 |
|
||
|
|
| 12 | IT edit reasons | Kept. Admin writes need a reason and are marked as IT edits in the packet history and the audit. | 2 |
|
||
|
|
| 13 | Screen lock | Server-enforced sessions. The browser reports idle and background state; the server records it and refuses an idle or locked session. Unlock is a step-up check on the server. | 1 |
|
||
|
|
| 14 | Input and upload validation | On the server: field limits on every write, uploads typed by content, size limits, sanitised names. | 2, 4 |
|
||
|
|
| 15 | Second-admin approval | Superseded. Roles come only from AD groups and the group-to-role map is derived, so there is nothing to grant in the app. | 1 |
|
||
|
|
| 16 | Security alerts and automatic integrity check | Worker jobs and the Notifications tab (email through app-03). | 9 |
|
||
|
|
| 17 | Controlled-copy stamp on data books | Applied to server-built PDFs and signed with the seal key. | 7 |
|
||
|
|
| 18 | CSV formula guard | Applied to every server export. | 7 |
|
||
|
|
| 19 | Update lock and stale-version block | Replaced by a build version endpoint. A screen on an older build shows a banner and reloads. | 0 (endpoint), 10 (banner) |
|
||
|
|
| 20 | VISUAL helper write guard | Dropped. There is no helper. VisualKit and MFilesKit are read-only by design and connect as read-only accounts. | 3 |
|
||
|
|
|
||
|
|
## Part 2: the new build's controls
|
||
|
|
|
||
|
|
### Sessions (phase 1)
|
||
|
|
|
||
|
|
- HttpOnly, SameSite=Strict session cookie. The session row on the server decides.
|
||
|
|
- Password sessions: idle sign-out after 15 minutes, absolute 12 hours. PIN shop sessions: absolute 10 hours, idle 15 minutes. All are settings.
|
||
|
|
- Idle and background lock are reported by the browser and enforced by the server. Unlock is a step-up check with the PIN or the domain password.
|
||
|
|
- Origin check on every state-changing request.
|
||
|
|
- Admin is a password session only. A PIN session cannot open Admin or change Account settings.
|
||
|
|
- Roles are resolved from AD at sign-in, cached on the session, and re-resolved every 15 minutes and nightly. A disabled AD account is always refused.
|
||
|
|
|
||
|
|
### PIN (phase 1)
|
||
|
|
|
||
|
|
- Optional, per user, set by the user in Account settings after a password sign-in. Setting or changing it needs the domain password in the same request.
|
||
|
|
- 6 to 12 digits, hashed with Argon2id on the server. Never visible to Admin. Admin can clear a PIN (audited), not set one.
|
||
|
|
- PIN sign-in uses the employee ID and works only on registered devices. Devices are registered by an Admin or a shop department Supervisor in a password session.
|
||
|
|
- Five wrong PIN attempts in a row, across sign-in, unlock and signature, disable the PIN. Three wrong attempts at unlock or signature end the session. A guessed PIN never touches AD.
|
||
|
|
|
||
|
|
### Rate limits (phase 1)
|
||
|
|
|
||
|
|
- Checked before any bind to AD, so the app can never lock a domain account out.
|
||
|
|
- Defaults: 5 failed password sign-ins per account per 5 minutes, then a 10-minute cooldown. 30 failed attempts per client address per 5 minutes. Successful sign-ins never count.
|
||
|
|
- The domain's own lockout (20 attempts in 5 minutes) stays the last line.
|
||
|
|
- The client address comes from `X-Forwarded-For` only when the request comes from `npm_app_1` itself, re-resolved on change. Other containers on `npm_default` are not trusted.
|
||
|
|
|
||
|
|
### Policy (phase 2)
|
||
|
|
|
||
|
|
- One policy in Python on the server. Every write goes: session, policy, transition, validation, version check, audit row, in one transaction.
|
||
|
|
- Every packet read returns `can: [...]` and `blocked: {...}` so the browser enables controls from the server's answer.
|
||
|
|
- A workflow step taken twice is refused with 409 and the current state.
|
||
|
|
- Details in [POLICY.md](POLICY.md).
|
||
|
|
|
||
|
|
### Audit (phase 1)
|
||
|
|
|
||
|
|
- Append-only `audit_events`, with a hash chain (`prev_hash`, `row_hash`).
|
||
|
|
- The app role may insert and select, never update or delete. A trigger also refuses updates and deletes. Migrations run as a separate role.
|
||
|
|
- `packetstation doctor` checks that an audit UPDATE fails.
|
||
|
|
|
||
|
|
### Seals (phase 7)
|
||
|
|
|
||
|
|
- Filing writes an HMAC seal with the server seal key. A reopen by Admin needs a reason and keeps the old seal in history.
|
||
|
|
- The integrity job checks seals and asset hashes (phase 9).
|
||
|
|
|
||
|
|
### Headers (phase 0)
|
||
|
|
|
||
|
|
- The app sets HSTS, CSP and the other security headers itself. NPM's own HSTS is off.
|
||
|
|
- The CSP allows nothing external. It is tuned again in phase 10.
|
||
|
|
|
||
|
|
### Secrets (phase 0)
|
||
|
|
|
||
|
|
- Docker secrets only, files under `/etc/packetstation/<stack>/secrets`, owner `root:10001`, mode `0440`.
|
||
|
|
- Read through `*_PASSWORD_FILE` settings. MFilesKit's password is exported by the entrypoint from its secret file.
|
||
|
|
- Never in the repo, `.env`, logs or chat. See [DEPLOY.md](DEPLOY.md).
|
||
|
|
|
||
|
|
## Part 3: requirement IDs
|
||
|
|
|
||
|
|
The IDs come from Part 3 of the old SECURITY.md. The status follows the approved plan.
|
||
|
|
|
||
|
|
| ID | Status |
|
||
|
|
|---|---|
|
||
|
|
| H1, H2 | Closed, phase 0 (docker-1, internal DNS, NPM) |
|
||
|
|
| H3 | Closed, phase 0 (NPM TLS, HSTS from the app) |
|
||
|
|
| H4 | Closed, phase 0 (two stacks) |
|
||
|
|
| H5 | IT's (host patching); documented in DEPLOY.md |
|
||
|
|
| I1 | Partly closed, phase 1 (AD sign-in, no SSO); Entra SSO deferred |
|
||
|
|
| I2 | Deferred (MFA with Entra) |
|
||
|
|
| I3 | Closed for "PIN on the server", phase 1; device certificate deferred |
|
||
|
|
| I4 | Closed, phase 1 |
|
||
|
|
| I5 | Superseded (no in-app admin grant); separate IT admin accounts remain IT's |
|
||
|
|
| I6 | Largely closed, phase 9 (groups plus reconcile); quarterly review IT's |
|
||
|
|
| I7 | Closed, phase 1 |
|
||
|
|
| I8 | App part closed, phase 1; kiosk lock policy IT's (Intune or GPO) |
|
||
|
|
| A1, A2, A3 | Closed, phase 2 |
|
||
|
|
| A4 | Closed, phase 0 |
|
||
|
|
| D1, D2, D4 | Deferred (envelope encryption, Key Vault) |
|
||
|
|
| D3 | IT's (host disk encryption on docker-1) |
|
||
|
|
| D5 | Closed, phase 4 (optional ClamAV) |
|
||
|
|
| D6, D7 | Deferred (retention period and customer copy need a decision from Seth and Q.C.) |
|
||
|
|
| M1 | Closed, phase 2 |
|
||
|
|
| M2 | Closed, phase 7 |
|
||
|
|
| M3 | Closed, phase 9 |
|
||
|
|
| M4, M5 | Deferred (logs to stdout now; SIEM shipping is IT's) |
|
||
|
|
| S1 | Closed, phase 0, tuned phase 10 |
|
||
|
|
| S2 | Closed, phase 1 |
|
||
|
|
| S3 | Closed, phase 9 (CI scanning) |
|
||
|
|
| S4 | Deferred to IT (pentest before go-live) |
|
||
|
|
| S5 | Closed, phase 2 |
|
||
|
|
| S6, S7, S8 | Closed, phase 7 |
|
||
|
|
| O1, O2 | Closed, phase 9 (pg_dump timer plus PBS; one rehearsed restore); quarterly restore IT's |
|
||
|
|
| O3, O4 | Closed, phase 10 |
|
||
|
|
| O5 | Superseded (no migration; M: retired read-only) |
|
||
|
|
|
||
|
|
The brief's wording for the identity IDs, which the table above follows:
|
||
|
|
|
||
|
|
- **I4 and I7** (PIN lockout, PIN signatures verified on the server) are closed by this build.
|
||
|
|
- **I8** is closed for the app's part. The kiosk device lock policy in I8 is IT's, via Intune or GPO.
|
||
|
|
- **I6** (joiner, mover, leaver) is largely carried by the AD groups plus the nightly reconcile. The quarterly access review is still IT's.
|
||
|
|
- **I3** is closed for "PIN checked on the server" and open for "Intune-managed, device certificate". Registered devices use a device cookie for now.
|
||
|
|
- **I1** is partly closed by AD sign-in. There is no SSO yet.
|
||
|
|
- **I5** is superseded. No in-app admin grant exists, so second-admin approval has nothing to approve. Separate IT admin accounts with MFA remain IT's.
|
||
|
|
|
||
|
|
Out of scope for this build, with hooks left: writing to VISUAL or M-Files (never), Entra ID sign-in and MFA (I1, I2), envelope encryption with Key Vault (D1, D2), true `.xlsx` exports, scheduled report emails.
|