YourBOX Security Controls
Purpose
Section titled “Purpose”This page consolidates the security posture of YourBOX across source, build, container, runtime and network controls. The application is one workload in the homelab portfolio and inherits the workspace baseline. The page calls out the controls specific to YourBOX and the evidence trail each control produces.
Threat Model Snapshot
Section titled “Threat Model Snapshot”The threats considered in the application design:
- Untrusted user input through registration, login, upload, comment and vote endpoints.
- Untrusted legacy code in
YourBOX/during the strangler migration. The fallback bundle executes 2020 PHP without modern security guarantees. - Compromised CI runner that could exfiltrate the build pipeline.
- Public network reachability of the VPS smoke test, which is the only externally addressable production-like surface.
- Supply chain attacks on container images, PHP packages and CI dependencies.
The threat model excludes a fully compromised homelab platform: a successful breach of OpenBao or the Argo CD control plane is treated as a platform incident with its own response path.
Source Controls
Section titled “Source Controls”| Control | Tool | Stage |
|---|---|---|
| Static analysis | PHPStan level 8 | Lint |
| Style | PHP-CS-Fixer (PSR-12) | Lint |
| Dependency vulnerability | composer audit | Lint |
| Secret detection | gitleaks | Pre commit + CI |
| YAML lint | yamllint | Lint |
| Ansible lint | ansible-lint production | Lint |
| Dockerfile lint | hadolint | Lint |
| License scan | license-checker | SAST |
| Architecture rules | PHPStan custom rules | Lint |
The PHPStan custom rules forbid Symfony imports under Domain/ and
Application/ per ADR-0006. They also forbid require and require_once
inside app/src/.
Build And Image Controls
Section titled “Build And Image Controls”| Control | Tool | Evidence artifact |
|---|---|---|
| Multi stage minimal image | Dockerfile | image digest |
| Vulnerability scan | Trivy (HIGH+CRITICAL) | trivy.json |
| SBOM generation | syft (CycloneDX) | sbom.cdx.json |
| Image signature | cosign | cosign attestation |
| SBOM attestation | cosign attest | cosign attestation |
| Digest pinning | Plumber control | plumber-report.json |
| Source registry allowlist | Plumber control | plumber-report.json |
| Forbidden tag rejection | Plumber control | plumber-report.json |
The CI/CD page describes the exact jobs. Each evidence artifact is kept as a GitLab artifact and attached to the deployment record.
Runtime Controls
Section titled “Runtime Controls”| Control | Where it lives |
|---|---|
| Non root container UID | Dockerfile USER 10001:10001 |
| Read only root filesystem | Helm values containerSecurityContext |
| Drop all Linux capabilities | Helm values capabilities.drop: ["ALL"] |
| Seccomp profile | RuntimeDefault |
| No host network or host PID | Helm values defaults |
| Resource requests and limits | Helm values |
| Liveness and readiness probes | Helm values |
| Pod Disruption Budget | Helm template poddisruptionbudget.yaml |
| Kyverno admission policies | homelab-platform Kyverno cluster policies |
| Sigstore image signature verify | homelab-platform policy-controller |
The Kyverno cluster policies reject images without digests, with a root user, without dropped capabilities, or from a non allowed registry.
Network Policy
Section titled “Network Policy”The NetworkPolicy template described on the Kubernetes GitOps page applies the default deny model. Egress is whitelisted to PostgreSQL, Meilisearch, MinIO, Redis cache, Authentik (OIDC token exchange), the SMTP provider and the messaging bus selected in ADR-0011. Ingress is whitelisted to Traefik and Prometheus.
On the VPS, Traefik enforces a security headers middleware (HSTS, frame deny, content type sniff, referrer policy, permissions policy) and a token bucket rate limit. The VPS firewall keeps the inbound ports to 80 and 443 and a Wireguard or SSH management port restricted to the admin source addresses.
Identity Controls
Section titled “Identity Controls”ADR-0004 sets the OIDC plus form firewall layout. The specific defenses applied on top of the Symfony Security firewall are:
- CSRF tokens on every state changing endpoint.
- Symfony Forms enforce token rotation.
- Session cookies are
Secure,HttpOnly,SameSite=Lax. - Session id rotates on login and on privilege elevation.
- The remember me cookie is the Symfony built in cookie signed with
APP_SECRET. The broken 2020 cookie format is gone. - Rate limit of 5 failed form logins per minute per IP through the Symfony rate limiter.
- Authentik enforces the equivalent rate limit in production.
- Account recovery is delegated to Authentik in production. The 2020 reset flow is deleted.
Secret Controls
Section titled “Secret Controls”ADR-0008 sets the secret management model. The defenses specific to YourBOX:
- The container image carries no secret values. Trivy and image inspection cannot leak credentials.
- The OpenBao policy
kv/data/yourbox/<env>/*is read only and scoped per environment. - The GitLab runner authenticates to OpenBao through JWT and only sees the staging path. The production runner is gated by manual approval.
- The age private key on the VPS lives on a tmpfs and is materialized from Ansible Vault at boot.
- The cosign signing key lives in OpenBao under
kv/yourbox/cosign-keyand is fetched at job time only.
DAST And Dynamic Probes
Section titled “DAST And Dynamic Probes”A ZAP baseline scan runs against the VPS smoke test after the deploy job. The scan targets the public homepage, the login page, the registration page and the catalog. Findings above MEDIUM block the promotion to Azure or to the homelab cluster.
A Nikto scan runs on a weekly schedule against the same surface and surfaces server header and default file findings.
Legacy Surface Hardening
Section titled “Legacy Surface Hardening”The fallback bundle serves the 2020 PHP code unchanged. The defenses applied during the transition:
- The Symfony front controller validates the URL path against the legacy
root directory with
realpath. A path traversal returns 404 instead of leaking files outside the bundle. - The static asset MIME guard refuses to stream a
.env,.env.php,composer.lock,composer.jsonor any non allowlisted file extension. - The Authentik OIDC firewall protects every legacy URL that lives behind
/app/*. The legacy public pages (/,/index.php) inherit the same CSP and security headers from Traefik. - The WAF (ModSecurity through Traefik plugin or Coraza for Kubernetes) is enabled on the public surface and runs the OWASP CRS. The 2020 SQL injection surface is mitigated by the WAF rules until the migration covers the route.
Evidence Trail
Section titled “Evidence Trail”The artifacts produced by each control are:
| Evidence | Source | Retention |
|---|---|---|
trivy.json |
CI scan stage | 90 days as GitLab artifact |
sbom.cdx.json |
CI sign stage | 365 days as GitLab artifact |
| cosign signature | CI sign stage | with the image, immutable |
plumber-report.json |
CI lint stage | 30 days as GitLab artifact |
gitleaks.json |
CI lint stage | 30 days as GitLab artifact |
zap-baseline.json |
CI DAST stage | 90 days as GitLab artifact |
| Audit log records | Application | 90 days hot in Postgres, 365 cold in OpenSearch |
Every deployment Merge Request references the matching pipeline run, so the evidence trail is reachable from the deployment record.
Compliance Surface
Section titled “Compliance Surface”YourBOX does not target a specific compliance framework. The controls above map cleanly to the categories of NIST SSDF, OWASP ASVS L2 and CIS Kubernetes Benchmark Level 1 for the relevant container and admission controls. The mapping is documented as a checklist on the workspace level Security Model page.