Security
Security Standards
Aligned to ISO/IEC 27001:2022. This document defines the operational controls in use for the Level147 internal operations dashboard. A full ISMS gap assessment should be conducted before any formal certification pursuit.
Information Security Policy
Level147 is committed to protecting the confidentiality, integrity, and availability of information assets belonging to the business and its clients. All personnel (including contractors and AI-assisted tooling) operate under these controls.
Owner: Principal Consultant
Review: Annual minimum, or after any security incident
Asset Classification
| Class | Examples | Controls |
|---|---|---|
| Confidential | Client credentials, API keys, OIDC client secrets, financial records | Encrypted at rest and in transit; access logged; never in version control |
| Internal | Architecture docs, infrastructure configs, internal comms, dashboard data | Access limited to authorised personnel via Cloudflare Access + Authentik SSO |
| Public | Open-source code, public documentation | No restrictions |
Access Control (ISO 27001 A.5.15–A.5.18)
Principle of Least Privilege
Every user, service account, and agent is granted only the minimum permissions required for its function. Admin access is never used for routine tasks.
Authentication
The dashboard uses a two-layer authentication model:
- Cloudflare Access (network perimeter) — all requests pass through a zero-trust Cloudflare Access policy before reaching the application. Unauthenticated requests are blocked at the network edge.
- NextAuth v5 + Authentik OIDC (application layer) — provides user identity (
session.user) and role claims. Enforced at two points:src/middleware.tsgates all/api/*routes (except/api/auth/*and/api/health) as the framework-level choke point so new routes are protected by default.- Each route handler also calls
requireSession()(src/lib/require-session.ts) for defence-in-depth.
No other identity providers are used. There is no email/password auth, no Google/GitHub login, and no guest access.
- All production systems require MFA (enforced via Authentik)
- SSH key authentication only — password auth disabled on all servers
- SSH keys: Ed25519 minimum; RSA keys ≥ 4096-bit if required for compatibility
- SSH keys rotated on personnel change or suspected compromise
Service Accounts
- Each service has its own credential — no shared passwords between services
- Authentik OIDC client secret: server-side only (NextAuth config), never in frontend bundles
- Woodpecker secrets injected at pipeline runtime; never logged
Access Review
Quarterly review of:
- Authentik users and group memberships
- Gitea organisation members
- Woodpecker admin access
- Portainer access
- Cloudflare account members
Secrets Management (ISO 27001 A.5.17)
Rules
- Never commit secrets. No API keys, passwords, tokens, or private keys in version control — ever.
.envfiles are always gitignored..env.examplewith placeholder values is committed.- CI/CD secrets live in Woodpecker repo or organisation secrets, scoped to the minimum required pipelines.
- Secrets referenced in pipelines use the
from_secret:directive — never inline plaintext. - Long-lived tokens (Gitea PATs, OIDC client secrets) are documented in the secrets inventory below.
Secrets Inventory
Maintain a private off-system record (e.g. Bitwarden) of:
| Secret | Scope | Rotation trigger |
|---|---|---|
Gitea PAT (GITEA_TOKEN) | Registry push/pull | Annual or on personnel change |
| Authentik OIDC client secret | NextAuth provider config | Annual or on breach |
| Woodpecker agent shared secret | Agent ↔ server auth | On agent re-enrolment or breach |
| Cloudflare API tokens | DNS, tunnel | Annual |
| SSH private keys | Server access | On device loss or personnel change |
Network Security (ISO 27001 A.8.20–A.8.22)
Perimeter
- All access to the dashboard is routed through a cloudflared tunnel → Cloudflare Access (zero-trust). There is no direct public ingress to the origin server.
- The dashboard is not public-facing; it is an internal tool accessible only to authorised users.
- Internal services (Woodpecker gRPC port 9000, Portainer, Gitea) are not publicly exposed.
- Internal access via Tailscale overlay VPN only.
Firewall Rules
- Default deny inbound on all hosts
- Explicit allow: 22 (SSH, key-auth only), Tailscale interface; cloudflared outbound tunnel (no inbound 80/443 required)
- Docker-published ports on internal hosts: bound to
127.0.0.1or Tailscale interface, not0.0.0.0
TLS
- Minimum TLS 1.2; TLS 1.3 preferred
- Cloudflare Full (Strict) mode — origin certificates must be valid
- Internal service-to-service traffic on private networks may use HTTP if on Tailscale
Container Security (ISO 27001 A.8.9)
- Container images pinned to explicit semver tags — never
:latestin production - Images pulled from trusted registries only (
gitea.level147.net, official Docker Hub images) - Resource limits set on all containers: CPU quota and memory limits in compose
- Containers run as non-root where the image supports it
- Docker socket mounts (
/var/run/docker.sock) granted only to trusted infra agents (Woodpecker deploy agent, Portainer) — the dashboard app mounts the socket without:robecause it performs write operations (container restart/stop)
Vulnerability Management (ISO 27001 A.8.8)
- Dependency updates reviewed monthly via
npm audit/ equivalent - OS patches applied to all servers within 30 days of release; critical patches within 72 hours
- Docker base images rebuilt on significant upstream releases
- Known CVEs in dependencies escalated: High/Critical → address within sprint; Medium → next planned cycle
Logging and Monitoring (ISO 27001 A.8.15–A.8.16)
- Container logs: Target 14-day retention. Note: Docker log rotation must be explicitly configured (
max-size/max-filein the log driver) to enforce this — confirm it is set in production before relying on this figure. - NextAuth session events logged server-side (sign-in, sign-out, token refresh failures)
- Woodpecker pipeline logs: Target 90-day retention (Woodpecker default; verify current server configuration).
- Cloudflare Analytics reviewed monthly for anomalies
- Failed auth attempts and unusual API call volumes trigger manual investigation
Incident Response (ISO 27001 A.5.24–A.5.28)
Severity Classification
| Severity | Definition | Response time |
|---|---|---|
| P1 — Critical | Active breach, data exfiltration, system unavailable | Immediate |
| P2 — High | Suspected compromise, significant data exposure risk | Within 4 hours |
| P3 — Medium | Vulnerability identified, no active exploit | Within 48 hours |
| P4 — Low | Minor issue, no immediate risk | Next business day |
Response Steps
- Contain — isolate affected system (revoke credentials, block IPs, take service offline if needed)
- Assess — determine scope: what was accessed, what data was exposed, time window
- Notify — if personal data is involved, assess Australian notifiable data breach obligations (see /governance/data-privacy)
- Remediate — patch, rotate credentials, rebuild from clean state if warranted
- Post-incident review — document root cause, update controls, review this standard
Credential Compromise Response
- Immediately revoke/rotate all affected credentials
- Audit logs for usage of compromised credential
- Redeploy affected services with new credentials
- Review how credential was exposed; update secrets management controls
Business Continuity (ISO 27001 A.5.29–A.5.30)
Backup Schedule
| Data | Backup frequency | Retention | Location |
|---|---|---|---|
IPAM data (data/ipam.json) | Target: Daily | Target: 7 days | Off-site |
| Gitea repositories | Target: Weekly | Target: 30 days | Off-site |
| Server configuration | On change (git) | Indefinite | Gitea |
| Docker volumes (stateful) | Target: Daily | Target: 7 days | Off-site |
Recovery Objectives
| Objective | Target |
|---|---|
| Recovery Time Objective (RTO) | 4 hours for production services |
| Recovery Point Objective (RPO) | 24 hours maximum data loss |
Third-Party and Supply Chain (ISO 27001 A.5.19–A.5.22)
Key third-party dependencies and their security posture:
| Provider | Role | Notes |
|---|---|---|
| Cloudflare | CDN, DNS, WAF, zero-trust Access, tunnel | SOC 2 Type II |
| Tailscale | VPN overlay | SOC 2 Type II; WireGuard-based |
| Authentik (self-hosted) | Identity provider (OIDC) | Self-managed; security is owner’s responsibility |
| Gitea (self-hosted) | Source control, container registry | Self-managed; security is owner’s responsibility |
| Woodpecker CI (self-hosted) | CI/CD | Self-managed; no external data sharing |
v0.1.0 · d0d7a20 · 2026-06-26