.gitbay/wiki/Architecture/09-Controls.org
104 lines · 9908 bytes
1#+title: Controls matrix
2
3One row per control an auditor typically asks about. *Status*: =in
4place= (implemented and cited), =partial= (implemented with a stated
5limit), =gap= (not implemented; see [[file:10-Known-Gaps.org][10]]). Categories follow the
6chapter names of OWASP ASVS 4.0 where one fits.
7
8** Architecture (V1)
9
10| Control | Status | Evidence |
11|---------------------------------------------+----------+------------------------------------------------------------------|
12| One authorization path for every surface | partial | all surfaces call =control.Dispatch= (=internal/control/control.go=); three web toggles write the store directly (#261) |
13| No server-side signing key | in place | =internal/sig= verifies only |
14| Least functionality by default | in place | API, web accounts, git://, push and registration default off (=internal/config/config.go=) |
15| No git library; git runs as a subprocess with built argv | in place | =internal/gitutil= |
16
17** Authentication (V2) and session management (V3)
18
19| Control | Status | Evidence |
20|---------------------------------------------+----------+------------------------------------------------------------------|
21| No passwords anywhere | in place | SSH keys, emailed single-use links, bearer tokens |
22| Credentials stored as hashes | in place | SHA-256 of 256-bit random values (=internal/store/sessions.go=) |
23| Brute-force limit on SSH auth | in place | 10 failures a minute per IP (=internal/sshd/ratelimit.go=) |
24| Account enumeration resistance at login | in place | uniform response (=internal/control/loginlink.go=) |
25| Session cookie flags | in place | HttpOnly, SameSite=Lax, Secure with TLS (=internal/httpd/accounts.go=) |
26| Session lifetime | partial | 7 days absolute, no idle timeout (#276) |
27| Credential expiry | partial | API tokens optional; SSH and deploy keys none (#277) |
28| Revocation takes effect immediately | in place | removing a key or disabling an account closes its connections; every exec re-reads its key (=internal/sshd/sshd.go=) |
29| Delegation bounded by the delegating credential | partial | expiring tokens refused on =MintsCredential= commands; credentials record their creating token (=internal/control/control.go=); a web session can still mint credentials that outlive it (#297) |
30
31** Access control (V4)
32
33| Control | Status | Evidence |
34|---------------------------------------------+----------+------------------------------------------------------------------|
35| Deny by default on private data | in place | =CanRead= requires owner, public or grant (=internal/policy/access.go=) |
36| Private resources indistinguishable from missing | in place | =resolveRepo= (=internal/control/repo.go=), =runGit=, smart HTTP |
37| Credential scopes narrow account rights | in place | key and token scopes (=control.go=, =policy/access.go=) |
38| Server-side write protections | in place | pre-receive =CheckPush=, signed commits (=internal/hookd/hookd.go=) |
39| Merge gates | partial | =MergeGates=; any writer can post a =ci/*= status (#258) |
40| Admin functions isolated | in place | =admin= noun gated in =Dispatch=; =audit= admin-only |
41| CSRF protection | in place | SameSite=Lax plus =checkOrigin= (=accounts.go=) |
42| Typed confirmation for destructive web actions | in place | =internal/httpd/confirm.go= |
43
44** Input handling and output encoding (V5)
45
46| Control | Status | Evidence |
47|---------------------------------------------+----------+------------------------------------------------------------------|
48| User markup sanitised | in place | =ugcHTML= with bluemonday (=internal/httpd/web.go=) |
49| No script execution in pages | in place | CSP =script-src 'none'= (=internal/httpd/routes.go=) |
50| Control characters stripped at the terminal | in place | =termSafe= (=internal/control/term.go=) |
51| No shell in command execution | in place | =protocol.Tokenize= for SSH argv; git and podman with argv slices |
52| Parsers fuzzed | partial | five fuzz targets run briefly by =deploy/audit.sh= |
53
54** Cryptography (V6) and data protection (V8)
55
56| Control | Status | Evidence |
57|---------------------------------------------+----------+------------------------------------------------------------------|
58| TLS for all authenticated HTTP | in place | ACME or certificate files; HSTS |
59| Secrets encrypted at rest | gap | CI secrets, webhook secrets, mirror tokens stored in clear (#273) |
60| Secrets kept out of argv, logs and output | in place | =ReadsStdin=, pruned audit argv, write-only secret commands |
61| Local backups encrypted | gap | tar.gz in clear; offsite copy encrypted by restic (#274) |
62| Data retention configurable | in place | =[retention]= (=internal/config/config.go=) |
63| User data export | in place | =account export= |
64
65** Logging (V7)
66
67| Control | Status | Evidence |
68|---------------------------------------------+----------+------------------------------------------------------------------|
69| Security-relevant writes audited | in place | every successful mutating command (=control.go=) |
70| Authentication failures audited | in place | =auth.failed=, =auth.throttled= |
71| Denied attempts audited | gap | refused commands are not recorded (#275) |
72| Audit log tamper resistance | gap | same database, writable by the daemon user (#275) |
73
74** Communications and integrations (V9, V10, V12)
75
76| Control | Status | Evidence |
77|---------------------------------------------+----------+------------------------------------------------------------------|
78| SSRF protection on user-supplied URLs | partial | webhooks at save and connect; mirrors at save only (#279) |
79| Webhook payload integrity | in place | HMAC-SHA256 header |
80| SMTP credentials protected in transit | partial | STARTTLS opportunistic (#280); Go refuses PLAIN auth without TLS to a remote host |
81| Upload size limits | in place | per-owner storage quota at push (=internal/sshd/sshd.go=); API body 1 MiB |
82
83** CI and build isolation
84
85| Control | Status | Evidence |
86|---------------------------------------------+----------+------------------------------------------------------------------|
87| Untrusted code runs isolated | partial | rootless podman, cgroup limits; shared build home per repository (#255) |
88| No secrets for untrusted builds | in place | =internal/control/build.go= |
89| Runner limited to attached repositories | in place | =runnerMayBuild= (=build.go=) |
90| Build images fixed by the operator | in place | =--pull=never= |
91| Build network egress restricted | gap | #260 |
92| Build results reused only across equal trust | gap | tree reuse ignores trust and image (#258) |
93
94** Availability and operations
95
96| Control | Status | Evidence |
97|---------------------------------------------+----------+------------------------------------------------------------------|
98| Rate limits on API and writes | in place | [[file:05-Identity-and-Access.org][5. Rate limits]] |
99| Concurrency limit on git pack generation | gap | #262 |
100| Service hardening | in place | systemd sandboxing ([[file:03-Deployment.org][3]]) |
101| Backups offsite and append-only | in place | restic with append-only credentials (documented) |
102| Restore tested | gap | #259 |
103| Migrations validated before commit | gap | foreign-key check runs after commit (#261) |
104| Signed, reviewed changes to production | in place | signed commits, =require-mr=, ff-only merges, clean-tree deploys |