.gitbay/wiki/Architecture/09-Controls.org

bd49b87fce895e9f0a7588152548fb6e1821ac7d
gitbay/.gitbay/wiki/Architecture/09-Controls.org rendered · source · history · blame · raw

104 lines · 10433 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                            | in place | 12 hours idle, 7 days absolute (=internal/store/sessions.go=)            |
 27| Credential expiry                           | in place | optional =--ttl= on API tokens, SSH and deploy keys; checked at auth and per exec |
 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                                 | in place | =MergeGates=; =ci/*= statuses written only by the build subsystem; required contexts |
 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                   | in place | AES-256-GCM, key file outside the database and the main backups (=internal/seal=) |
 60| Secrets kept out of argv, logs and output   | in place | =ReadsStdin=, pruned audit argv, write-only secret commands      |
 61| Local backups encrypted                     | in place | age to =[backup] age_recipients= (=cmd/gitbayd/backup.go=); offsite copy by restic |
 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                     | in place | refused mutating commands and pushes, ten a minute per actor, 600 in all (=internal/control/auditrefusal.go=) |
 72| Audit log tamper resistance                 | partial  | hash chain checked by =gitbayd admin audit verify=; every row the daemon writes copied to its journal; the table is writable by the daemon user, and removing the newest rows (or reusing their ids) shows only by comparing verify's last id and hash with the journal |
 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 and sync, git pinned to the checked address (=internal/mirror/mirror.go=); =repo import --from= has no address check (#298) |
 79| Webhook payload integrity                   | in place | HMAC-SHA256 header                                               |
 80| SMTP credentials protected in transit       | in place | STARTTLS required for non-local relays, implicit TLS optional (=internal/mail/mail.go=) |
 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                | in place | rootless podman, cgroup limits; untrusted builds get a disposable home (=cmd/gitbay-runner/main.go=) |
 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             | partial  | host: loopback closed, public 22/80/443 only (=gitbay-runner-egress.nft=); internet outbound open by decision (#260) |
 92| Build results reused only across equal trust | in place | =SuccessBuildForTree=, =SuccessBuildFor= (=internal/store/builds.go=) |
 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    | in place | global, per-principal, bounded queue across SSH, HTTP and git:// (=internal/packlimit=); not in system SSH mode |
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 |