docs: record DWH auth implementation evidence
This commit is contained in:
@@ -1,55 +1,60 @@
|
||||
# Task 4 report — one-command Docker documentation
|
||||
# Task 4 — Unix-socket DWH verification report
|
||||
|
||||
## Status
|
||||
## Scope
|
||||
|
||||
Implemented. The installation documentation now uses the canonical flow:
|
||||
Implemented the standalone Linux verifier at `tools/dwh-auth/internal/service` and wired the exact command:
|
||||
|
||||
```sh
|
||||
cp .env.example .env
|
||||
cp deploy/secrets/thothii.secrets.example deploy/secrets/thothii.secrets
|
||||
chmod 600 deploy/secrets/thothii.secrets
|
||||
docker compose up --build -d
|
||||
```text
|
||||
dwh-auth serve --registry-root ABSOLUTE_CANONICAL --socket ABSOLUTE_CANONICAL
|
||||
```
|
||||
|
||||
Updated:
|
||||
The service accepts only `GET /verify`. It returns empty `204` responses with `X-DWH-Key-ID` for verified v1 or reserved legacy credentials; credential failures are generic empty `401` responses, and registry/integrity faults are empty `503` responses. Other paths/methods return empty `404`/`405`.
|
||||
|
||||
- `README.md` with root `.env` defaults, one bundle, optional overlay presets, CA limitation,
|
||||
preprocessing, and migration notes.
|
||||
- `docs/installazione-docker-4-contesti.md` rewritten with exact files to create/edit and the
|
||||
four requested contexts (co-located DB/vector, Mac, Windows, and remote DB/Evidence server).
|
||||
- `docs/index.md` link text for the one-command installation.
|
||||
- `deploy/secrets/README.md` bundle syntax, permissions, runtime mount verification, CA handling,
|
||||
and migration guidance.
|
||||
- `scripts/docker-smoke.sh` now creates a disposable mode-0600 bundle and exercises the default
|
||||
Compose services without the legacy `external` profile.
|
||||
- `scripts/test-default-compose.sh` asserts the exact installation command, tracked templates,
|
||||
and absence of the legacy setup in the guide.
|
||||
- `scripts/test-container-deployment.sh` now validates the bundle mount and rejects legacy
|
||||
per-secret references; `.dockerignore` explicitly re-includes only the required vector policy
|
||||
helper so the Docker build context remains safe.
|
||||
- The Mac/Windows/local-vector and remote-server snippets now include required DWH/database and
|
||||
Evidence-root settings. `deploy/env.example` is explicitly deprecated and no longer selects a
|
||||
different Compose overlay.
|
||||
## Security decisions
|
||||
|
||||
The docs explicitly state that a PEM CA chain cannot be put in the strict single-line bundle. A
|
||||
reviewed Compose override/secret-manager mount is required for `THT_SSL_CA`. Direct PostgreSQL
|
||||
workspace examples are marked as advanced and require a separate reviewed runtime password mount;
|
||||
the base bundle mount is the only default mount.
|
||||
- Exactly one `X-API-Key` header, maximum 128 bytes.
|
||||
- Strict `thtdwh_v1.` parsing precedes legacy lookup; non-v1 values alone may use the reserved legacy record.
|
||||
- Registry integrity is checked before every verification request, so unrelated malformed/unsafe records fail closed with `503`.
|
||||
- Logs emit only timestamp, decision code, and (when safely parsed or verified) public key ID; test sentinels prove no key, digest, description, or query value is emitted.
|
||||
- `serve` validates canonical absolute paths, performs startup `Store.Check`, and reports service startup errors as non-secret `integrity failure`.
|
||||
- Socket collisions that are regular files, directories, symlinks, live sockets, or foreign-owned stale sockets are refused. Only an owned stale Unix socket after `ECONNREFUSED` can be reclaimed.
|
||||
- Published sockets are mode `0660`; cancellation calls graceful shutdown and removes only a revalidated same-device/same-inode owned socket. A test seam proves a changed path is retained rather than unlinked.
|
||||
|
||||
## Required supporting security fix
|
||||
|
||||
Commit `943f809` (`fix: reject duplicate legacy DWH records`) tightens the Task 2 registry contract: a synthetically valid active plus revoked legacy pair is now an integrity failure. It is intentionally separate from the Task 4 commit.
|
||||
|
||||
## TDD evidence
|
||||
|
||||
RED was observed for the missing handler, listener/configuration API, CLI wiring, unrelated-registry corruption, active+revoked legacy state, and cleanup replacement race. Each increment was then implemented minimally and rerun GREEN.
|
||||
|
||||
## Verification
|
||||
|
||||
- `sh -n scripts/docker-smoke.sh scripts/test-default-compose.sh` — passed.
|
||||
- `./scripts/test-default-compose.sh` — passed.
|
||||
- `./scripts/test-container-deployment.sh` — passed after migrating its local-vector assertions
|
||||
to the single bundle and checking the `.dockerignore` deployment allowlist.
|
||||
- `git diff --check` — passed.
|
||||
- `./scripts/test-docker-smoke.sh` — passed after updating its static assertion to the default
|
||||
no-profile invocation.
|
||||
- `docker buildx build --file docker/core.Dockerfile --check .` — passed; BuildKit reported no
|
||||
warnings after the `.dockerignore` parent-directory fix.
|
||||
All commands were executed in official `golang:1.26.5`, with only this worktree mounted:
|
||||
|
||||
## Concerns
|
||||
```text
|
||||
gofmt -w cmd internal/command internal/service
|
||||
go test ./internal/service ./internal/command -count=1
|
||||
go test ./... -count=1
|
||||
go test -race ./... -count=1
|
||||
go vet ./...
|
||||
git diff --check
|
||||
```
|
||||
|
||||
The legacy `scripts/vector-rotate-bootstrap-password.sh` maintenance helper still accepts
|
||||
old/new standalone files. Its output is intentionally documented as a transitional interface;
|
||||
the resulting value must be copied into the bundle before restarting local-vector services.
|
||||
All passed. A dependency scan also found no third-party Go dependencies.
|
||||
|
||||
## Scope boundary
|
||||
|
||||
No Nginx, systemd, real Unix socket, real registry, credential, legacy stack, or external service was changed. All test data was synthetic and temporary.
|
||||
|
||||
## Follow-up hardening: runtime read-only registry and socket parent
|
||||
|
||||
The Task 5 storage contract uses `root:dwh-auth` SGID directories (`2750`) and a service account with read-only group access. The original registry reader path was incompatible because shared locks were opened `O_RDWR` and lazily created as `0600`; secure-directory validation also rejected SGID.
|
||||
|
||||
The runtime path now uses `registry.OpenReadOnly`: it opens only preprovisioned root, `active`, `revoked`, and `.writer.lock` paths, and rejects `Add`/`Revoke`. The administrative `Open` path bootstraps the lock through the exclusive writer path. Shared lock acquisition opens the existing `root:dwh-auth 0640` lock `O_RDONLY` with `LOCK_SH`; writer acquisition remains `O_RDWR` with `LOCK_EX`, preserving cross-process snapshot exclusion. Secure directories allow SGID but still reject setuid, sticky, group-write, and world-write bits.
|
||||
|
||||
Task 5 must create `.writer.lock` as `0640 root:dwh-auth` alongside the `2750 root:dwh-auth` registry directories before the service starts.
|
||||
|
||||
The socket parent must be a canonical non-symlink directory owned by the service EUID and not group/world writable. This removes the bind-to-chmod and path-replacement exposure from other principals. The remaining POSIX path race is bounded to trusted processes sharing the service EUID inside that non-contendible parent.
|
||||
|
||||
Additional verification (official `golang:1.26.5`, worktree only): focused securefile/registry/service/command tests, full tests, full race tests, vet, plus ten race repetitions each for cross-store snapshot readers, `OpenReadOnly`, and listener tests: all PASS.
|
||||
|
||||
Reference in New Issue
Block a user