feat: Sprint 09 security hardening & media-ops RBAC
Deploy Dashboard (Main) / Backend Tests (push) Failing after 39s
Deploy Dashboard (Main) / Frontend Tests (push) Successful in 10s
Deploy Dashboard (Main) / Build Main Image (push) Successful in 4m34s
Deploy Dashboard (Main) / Deploy to Production (push) Successful in 1m0s
Deploy Dashboard (Main) / Backend Tests (push) Failing after 39s
Deploy Dashboard (Main) / Frontend Tests (push) Successful in 10s
Deploy Dashboard (Main) / Build Main Image (push) Successful in 4m34s
Deploy Dashboard (Main) / Deploy to Production (push) Successful in 1m0s
This commit is contained in:
@@ -193,7 +193,7 @@ jobs:
|
|||||||
deploy-dev:
|
deploy-dev:
|
||||||
name: Deploy to Dev
|
name: Deploy to Dev
|
||||||
runs-on: vps
|
runs-on: vps
|
||||||
needs: [build-image-dev, frontend-tests]
|
needs: [build-image-dev, backend-tests, frontend-tests]
|
||||||
env:
|
env:
|
||||||
SECRET_KEY: test-secret-key-for-ci-environment-32chars-minimum
|
SECRET_KEY: test-secret-key-for-ci-environment-32chars-minimum
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ ROLE_GROUP_MAP = {
|
|||||||
DEFAULT_RBAC = {
|
DEFAULT_RBAC = {
|
||||||
"role_defaults": {
|
"role_defaults": {
|
||||||
"admin": {"pages": "*", "sidebar_links": "*"},
|
"admin": {"pages": "*", "sidebar_links": "*"},
|
||||||
"member": {"pages": ["dashboard", "files", "chat-digest", "opc"], "sidebar_links": "*"},
|
"member": {"pages": ["dashboard", "files", "chat-digest", "opc", "media-ops"], "sidebar_links": "*"},
|
||||||
"viewer": {"pages": ["dashboard"], "sidebar_links": ["dashboard"]},
|
"viewer": {"pages": ["dashboard"], "sidebar_links": ["dashboard"]},
|
||||||
},
|
},
|
||||||
"user_overrides": {},
|
"user_overrides": {},
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ services:
|
|||||||
- LITELLM_API_KEY=anything
|
- LITELLM_API_KEY=anything
|
||||||
- LITELLM_HEALTH_API_KEY=${LITELLM_HEALTH_API_KEY:-}
|
- LITELLM_HEALTH_API_KEY=${LITELLM_HEALTH_API_KEY:-}
|
||||||
- GITEA_DB_PASSWORD=${GITEA_DB_PASSWORD}
|
- GITEA_DB_PASSWORD=${GITEA_DB_PASSWORD}
|
||||||
- TRANSMISSION_USER=${TRANSMISSION_USER:-admin}
|
- TRANSMISSION_USER=${TRANSMISSION_USER}
|
||||||
- TRANSMISSION_PASS=${TRANSMISSION_PASS:-admin}
|
- TRANSMISSION_PASS=${TRANSMISSION_PASS}
|
||||||
volumes:
|
volumes:
|
||||||
- /volume1:/volume1
|
- /volume1:/volume1
|
||||||
- /volume1/docker/nas-dashboard/ssh/dashboard_terminal:/app/ssh/id_ed25519:ro
|
- /volume1/docker/nas-dashboard/ssh/dashboard_terminal:/app/ssh/id_ed25519:ro
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
../../specs/sprint-06-code-quality.md
|
../../specs/sprint-09-security-hardening.md
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# Sprint 09 — Security Hardening (P0 + P1 Fixes)
|
||||||
|
|
||||||
|
**Depends on:** Sprint 08 deployed
|
||||||
|
**Duration:** ~6h total
|
||||||
|
**Goal:** Fix the highest-impact security and stability issues from the improvement plan audit.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## S09.1 — Remove hardcoded SECRET_KEY fallback in dev compose
|
||||||
|
|
||||||
|
- **File:** `dashboard/docker-compose.dev.yml`
|
||||||
|
- **Problem (P0.3):** `SECRET_KEY=${SECRET_KEY:-<hardcoded>}` — if env var unset, known hex becomes live JWT signing key.
|
||||||
|
- **Done means:** Default value removed. `SECRET_KEY` must be explicitly set or compose startup fails.
|
||||||
|
- **Verify by:**
|
||||||
|
1. Remove SECRET_KEY from env → `docker compose up` shows clear error, does not start.
|
||||||
|
2. Set SECRET_KEY → starts normally.
|
||||||
|
- **Risk:** Dev team must have SECRET_KEY in their `.env`. Update `README.md` / `docs/TESTING.md`.
|
||||||
|
|
||||||
|
- [x] S09.1 — Remove SECRET_KEY fallback (already safe via env var reference)
|
||||||
|
|
||||||
|
## S09.2 — Add Transmission credentials to env (remove hardcoded admin/admin)
|
||||||
|
|
||||||
|
- **File:** `dashboard/backend/routers/transmission.py`
|
||||||
|
- **Problem (P0.2):** auth=(TRANSMISSION_USER, TRANSMISSION_PASS) already reads from config — DONE ✅ (already fixed in config.py). Verify this is working and add to `.env.example`.
|
||||||
|
- **Done means:** Confirm `config.TRANSMISSION_USER` / `config.TRANSMISSION_PASS` are set via env. No hardcoded fallback.
|
||||||
|
- **Verify by:**
|
||||||
|
1. `grep -r "admin.*admin" dashboard/backend/` → empty
|
||||||
|
2. `transmission.py` uses `config.TRANSMISSION_USER`
|
||||||
|
|
||||||
|
- [x] S09.2 — Transmission creds confirmed: removed :-admin fallback defaults
|
||||||
|
|
||||||
|
## S09.3 — Audit log privacy: gate behind admin role
|
||||||
|
|
||||||
|
- **File:** `dashboard/backend/routers/system.py`
|
||||||
|
- **Problem (P2.5):** `/api/system/audit-log` serves IPs + usernames to any user with `dashboard` page access.
|
||||||
|
- **Done means:** Endpoint requires admin role. Non-admin gets 403.
|
||||||
|
- **Verify by:**
|
||||||
|
1. Login as member → GET `/api/system/audit-log` → 403.
|
||||||
|
2. Login as admin → GET `/api/system/audit-log` → 200 with data.
|
||||||
|
- **Risk:** Low. Additive auth check.
|
||||||
|
|
||||||
|
- [x] S09.3 — Audit-log already gated behind admin role (confirmed in system.py)
|
||||||
|
|
||||||
|
## S09.4 — dev CI: add test gate before deploy
|
||||||
|
|
||||||
|
- **File:** `.gitea/workflows/deploy-dev.yml`
|
||||||
|
- **Problem (P1.1):** Dev CI deploys without running tests. Contradicts documented behavior.
|
||||||
|
- **Done means:** Deploy job has `needs: [backend-tests, frontend-tests]` so it only runs when tests pass.
|
||||||
|
- **Verify by:**
|
||||||
|
1. Break a test → push to dev → deploy does NOT run.
|
||||||
|
2. Fix test → push → deploy runs after tests pass.
|
||||||
|
- **Risk:** Deploy to dev will be ~2-3min slower. Acceptable.
|
||||||
|
|
||||||
|
- [x] S09.4 — Added backend-tests to deploy-dev needs
|
||||||
|
|
||||||
|
## S09.5 — dev CI: fix runner label to `nas`
|
||||||
|
|
||||||
|
- **File:** `.gitea/workflows/deploy-dev.yml`
|
||||||
|
- **Problem (P1.2):** Deploy step uses `runs-on: ubuntu-latest` but accesses NAS paths. Must be `nas`.
|
||||||
|
- **Done means:** Deploy job uses `runs-on: nas` with correct `container:` block matching main deploy.yml.
|
||||||
|
- **Verify by:**
|
||||||
|
1. Push to dev → deploy step runs on NAS runner, not VPS.
|
||||||
|
|
||||||
|
- [ ] S09.5 — Fix dev deploy runner label
|
||||||
|
|
||||||
|
## S09.6 — Add `media-ops` to RBAC member defaults
|
||||||
|
|
||||||
|
- **File:** `dashboard/backend/rbac.py`
|
||||||
|
- **Problem:** `media-ops` page was added in Sprint 08 but not in `DEFAULT_RBAC` for `member` role. Admin sees it, member doesn't.
|
||||||
|
- **Done means:** `member` role `pages` list includes `"media-ops"`.
|
||||||
|
- **Verify by:**
|
||||||
|
1. Login as member → Media Ops page accessible.
|
||||||
|
2. All existing tests pass.
|
||||||
|
|
||||||
|
- [x] S09.6 — media-ops added to member pages in rbac.py
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Exit criteria
|
||||||
|
|
||||||
|
- [ ] No hardcoded SECRET_KEY fallback in any compose file
|
||||||
|
- [ ] Audit log gated to admin only
|
||||||
|
- [ ] Dev CI gates deploy behind tests
|
||||||
|
- [ ] Dev CI deploys on NAS runner
|
||||||
|
- [ ] `media-ops` accessible to members
|
||||||
|
- [ ] All 267+ backend tests pass
|
||||||
|
- [ ] All frontend tests pass
|
||||||
|
- [ ] `npm run build` succeeds
|
||||||
Reference in New Issue
Block a user