Security
Security Standards
Aligned to ISO/IEC 27001:2022. This document defines the operational controls in use. A full ISMS gap assessment should be conducted before 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, personal data, financial records | Encrypted at rest and in transit; access logged; never in version control |
| Internal | Architecture docs, infrastructure configs, internal comms | Access limited to authorised personnel; not published |
| Public | Marketing content, 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
- All production systems require MFA
- 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
- Supabase:
anonkey for client-side;service_rolekey only in server-side/CI contexts, never in frontend bundles - Woodpecker secrets injected at pipeline runtime; never logged
Access Review
Quarterly review of:
- Supabase user roles
- 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, Supabase service keys) 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 |
| Supabase service role key | Server-side writes | Annual or on breach |
| Woodpecker agent shared secret | Agent ↔ server auth | On agent re-enrolment or breach |
| Cloudflare API tokens | DNS, R2 | Annual |
| SSH private keys | Server access | On device loss or personnel change |
Network Security (ISO 27001 A.8.20–A.8.22)
Perimeter
- All public-facing services sit behind Cloudflare (DDoS mitigation, WAF, TLS termination)
- 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: 80/443 (Cloudflare only via CF IP ranges), 22 (SSH, key-auth only), Tailscale interface
- 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) — never to application containers
Vulnerability Management (ISO 27001 A.8.8)
- Dependency updates reviewed monthly via
npm audit/pip 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 retained via Docker log driver; minimum 14-day retention
- Supabase audit logs enabled for auth events
- Woodpecker pipeline logs retained for 90 days
- 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 data-privacy.md)
- 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 |
|---|---|---|---|
| Supabase databases | Daily (Supabase managed) | 7 days PITR | Supabase cloud |
| Gitea repositories | Weekly | 30 days | Off-site |
| Server configuration | On change (git) | Indefinite | Gitea |
| Docker volumes (stateful) | Daily | 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 |
|---|---|---|
| Supabase | Database, auth, storage | SOC 2 Type II; GDPR compliant; data in ap-southeast-1 |
| Cloudflare | CDN, DNS, WAF | SOC 2 Type II |
| Tailscale | VPN overlay | SOC 2 Type II; WireGuard-based |
| Gitea (self-hosted) | Source control, registry | Self-managed; security is owner’s responsibility |
| n8n (self-hosted) | Workflow automation | Self-managed; no external data sharing |