Commit fc5b1b67a2

fc5b1b67a29ea0785b3f74c8ddc03ff80f5c9e90

parent: 7a6343d02d

Verified · cmc ci/build: success ci/test: skipped

cmc <hello@cleberg.net> · 2026-09-28 04:31 UTC

wiki: Architecture overview names diagrams; titles drop their numbers; gaps filed

A link to a non-page file resolves to the page route and 404s (#283).
A title starting "5. " renders as an ordered list numbered from 1; the
sidebar already carries the numbers. The review's unfiled gaps are now
#273-#282; Known gaps and the controls matrix cite them.

Ref #272, #283

Layout: unified · split

.gitbay/wiki/Architecture/00-Overview.org +6 −6
@@ -38,13 +38,13 @@ the =ReadOnly= count from the =ReadOnly: true= literals in
38 38
39| # | Document | Diagram | 39| # | Document | Diagram |
40|---+----------------------------------------------------+-------------------------------------------------| 40|---+----------------------------------------------------+-------------------------------------------------|
41| 1 | [[file:01-System-Context.org][System context]] | [[file:diagrams/01-context.svg][01-context.svg]] | 41| 1 | [[file:01-System-Context.org][System context]] | =01-context.svg= |
42| 2 | [[file:02-Components.org][Components]] | [[file:diagrams/02-components.svg][02-components.svg]] | 42| 2 | [[file:02-Components.org][Components]] | =02-components.svg= |
43| 3 | [[file:03-Deployment.org][Deployment and network]] | [[file:diagrams/03-deployment.svg][03-deployment.svg]] | 43| 3 | [[file:03-Deployment.org][Deployment and network]] | =03-deployment.svg= |
44| 4 | [[file:04-Trust-Boundaries.org][Trust boundaries and data flows]] | [[file:diagrams/04-trust-boundaries.svg][04-trust-boundaries.svg]], [[file:diagrams/06-push-flow.svg][06-push-flow.svg]] | 44| 4 | [[file:04-Trust-Boundaries.org][Trust boundaries and data flows]] | =04-trust-boundaries.svg=, =06-push-flow.svg= |
45| 5 | [[file:05-Identity-and-Access.org][Identity and access]] | [[file:diagrams/05-authorization.svg][05-authorization.svg]] | 45| 5 | [[file:05-Identity-and-Access.org][Identity and access]] | =05-authorization.svg= |
46| 6 | [[file:06-Data-and-Cryptography.org][Data and cryptography]] | | 46| 6 | [[file:06-Data-and-Cryptography.org][Data and cryptography]] | |
47| 7 | [[file:07-CI-and-Supply-Chain.org][CI and supply chain]] | [[file:diagrams/07-ci-flow.svg][07-ci-flow.svg]] | 47| 7 | [[file:07-CI-and-Supply-Chain.org][CI and supply chain]] | =07-ci-flow.svg= |
48| 8 | [[file:08-Operations.org][Operations]] | | 48| 8 | [[file:08-Operations.org][Operations]] | |
49| 9 | [[file:09-Controls.org][Controls matrix]] | | 49| 9 | [[file:09-Controls.org][Controls matrix]] | |
50| 10 | [[file:10-Known-Gaps.org][Known gaps]] | | 50| 10 | [[file:10-Known-Gaps.org][Known gaps]] | |
.gitbay/wiki/Architecture/01-System-Context.org +1 −1
@@ -1,4 +1,4 @@
1#+title: 1. System context 1#+title: System context
2 2
3[[file:diagrams/01-context.svg]] 3[[file:diagrams/01-context.svg]]
4 4
.gitbay/wiki/Architecture/02-Components.org +1 −1
@@ -1,4 +1,4 @@
1#+title: 2. Components 1#+title: Components
2 2
3[[file:diagrams/02-components.svg]] 3[[file:diagrams/02-components.svg]]
4 4
.gitbay/wiki/Architecture/03-Deployment.org +1 −1
@@ -1,4 +1,4 @@
1#+title: 3. Deployment and network 1#+title: Deployment and network
2 2
3[[file:diagrams/03-deployment.svg]] 3[[file:diagrams/03-deployment.svg]]
4 4
.gitbay/wiki/Architecture/04-Trust-Boundaries.org +1 −1
@@ -1,4 +1,4 @@
1#+title: 4. Trust boundaries and data flows 1#+title: Trust boundaries and data flows
2 2
3[[file:diagrams/04-trust-boundaries.svg]] 3[[file:diagrams/04-trust-boundaries.svg]]
4 4
.gitbay/wiki/Architecture/05-Identity-and-Access.org +1 −1
@@ -1,4 +1,4 @@
1#+title: 5. Identity and access 1#+title: Identity and access
2 2
3[[file:diagrams/05-authorization.svg]] 3[[file:diagrams/05-authorization.svg]]
4 4
.gitbay/wiki/Architecture/06-Data-and-Cryptography.org +1 −1
@@ -1,4 +1,4 @@
1#+title: 6. Data and cryptography 1#+title: Data and cryptography
2 2
3* Data inventory 3* Data inventory
4 4
.gitbay/wiki/Architecture/07-CI-and-Supply-Chain.org +1 −1
@@ -1,4 +1,4 @@
1#+title: 7. CI and supply chain 1#+title: CI and supply chain
2 2
3[[file:diagrams/07-ci-flow.svg]] 3[[file:diagrams/07-ci-flow.svg]]
4 4
.gitbay/wiki/Architecture/08-Operations.org +1 −1
@@ -1,4 +1,4 @@
1#+title: 8. Operations 1#+title: Operations
2 2
3* Logging 3* Logging
4 4
.gitbay/wiki/Architecture/09-Controls.org +9 −9
@@ -1,4 +1,4 @@
1#+title: 9. Controls matrix 1#+title: Controls matrix
2 2
3One row per control an auditor typically asks about. *Status*: =in 3One row per control an auditor typically asks about. *Status*: =in
4place= (implemented and cited), =partial= (implemented with a stated 4place= (implemented and cited), =partial= (implemented with a stated
@@ -23,8 +23,8 @@ chapter names of OWASP ASVS 4.0 where one fits.
23| Brute-force limit on SSH auth | in place | 10 failures a minute per IP (=internal/sshd/ratelimit.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=) | 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=) | 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 | 26| Session lifetime | partial | 7 days absolute, no idle timeout (#276) |
27| Credential expiry | partial | API tokens optional; SSH and deploy keys none | 27| Credential expiry | partial | API tokens optional; SSH and deploy keys none (#277) |
28| Revocation takes effect immediately | gap | removed SSH key keeps open connections (#256) | 28| Revocation takes effect immediately | gap | removed SSH key keeps open connections (#256) |
29| Delegation bounded by the delegating credential | gap | expiring tokens can mint lasting credentials (#257) | 29| Delegation bounded by the delegating credential | gap | expiring tokens can mint lasting credentials (#257) |
30 30
@@ -56,9 +56,9 @@ chapter names of OWASP ASVS 4.0 where one fits.
56| Control | Status | Evidence | 56| Control | Status | Evidence |
57|---------------------------------------------+----------+------------------------------------------------------------------| 57|---------------------------------------------+----------+------------------------------------------------------------------|
58| TLS for all authenticated HTTP | in place | ACME or certificate files; HSTS | 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 ([[file:06-Data-and-Cryptography.org][6]]) | 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 | 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 | 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=) | 62| Data retention configurable | in place | =[retention]= (=internal/config/config.go=) |
63| User data export | in place | =account export= | 63| User data export | in place | =account export= |
64 64
@@ -68,16 +68,16 @@ chapter names of OWASP ASVS 4.0 where one fits.
68|---------------------------------------------+----------+------------------------------------------------------------------| 68|---------------------------------------------+----------+------------------------------------------------------------------|
69| Security-relevant writes audited | in place | every successful mutating command (=control.go=) | 69| Security-relevant writes audited | in place | every successful mutating command (=control.go=) |
70| Authentication failures audited | in place | =auth.failed=, =auth.throttled= | 70| Authentication failures audited | in place | =auth.failed=, =auth.throttled= |
71| Denied attempts audited | gap | refused commands are not recorded | 71| Denied attempts audited | gap | refused commands are not recorded (#275) |
72| Audit log tamper resistance | gap | same database, writable by the daemon user | 72| Audit log tamper resistance | gap | same database, writable by the daemon user (#275) |
73 73
74** Communications and integrations (V9, V10, V12) 74** Communications and integrations (V9, V10, V12)
75 75
76| Control | Status | Evidence | 76| Control | Status | Evidence |
77|---------------------------------------------+----------+------------------------------------------------------------------| 77|---------------------------------------------+----------+------------------------------------------------------------------|
78| SSRF protection on user-supplied URLs | partial | webhooks at save and connect; mirrors at save only | 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 | 79| Webhook payload integrity | in place | HMAC-SHA256 header |
80| SMTP credentials protected in transit | partial | STARTTLS opportunistic; Go refuses PLAIN auth without TLS to a remote host | 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 | 81| Upload size limits | in place | per-owner storage quota at push (=internal/sshd/sshd.go=); API body 1 MiB |
82 82
83** CI and build isolation 83** CI and build isolation
.gitbay/wiki/Architecture/10-Known-Gaps.org +11 −18
@@ -1,4 +1,4 @@
1#+title: 10. Known gaps 1#+title: Known gaps
2 2
3Open weaknesses. Issues on krz/gitbay are public; this page gives the 3Open weaknesses. Issues on krz/gitbay are public; this page gives the
4title and the consequence, not a reproduction. The current list is the 4title and the consequence, not a reproduction. The current list is the
@@ -18,29 +18,22 @@ what the 2026-09-27 review found; remove a row when its issue closes.
18| #260 | CI network | Builds share the runner's source address; no egress policy | medium | 18| #260 | CI network | Builds share the runner's source address; no egress policy | medium |
19| #261 | Various | Migration foreign-key check after commit; three web writes bypass dispatch; documentation drift | medium | 19| #261 | Various | Migration foreign-key check after commit; three web writes bypass dispatch; documentation drift | medium |
20| #262 | Availability | No limit on concurrent git pack generation | high | 20| #262 | Availability | No limit on concurrent git pack generation | high |
21| #273 | Data at rest | CI secrets, webhook secrets and mirror tokens are stored in clear in SQLite | high |
22| #274 | Backups | The local backup archive is not encrypted | medium |
23| #275 | Audit | Refused writes are not audited; the audit table is writable by the daemon user | medium |
24| #276 | Sessions | Web sessions last 7 days with no idle timeout | low |
25| #277 | Credentials | SSH and deploy keys never expire | low |
26| #278 | Login links | =web login= over SSH skips the login-link rate limit | low |
27| #279 | SSRF | Mirror URLs are checked when saved, not when git connects | medium |
28| #280 | Mail | STARTTLS only when the relay offers it | medium |
29| #281 | TLS | No explicit minimum TLS version | low |
30| #282 | Hook socket | Anything that can open =hook.sock= can act as any user | medium |
21 31
22Decisions already taken on these: #256 closes a removed key's 32Decisions already taken on these: #256 closes a removed key's
23connections, running commands included; #257 refuses credential 33connections, running commands included; #257 refuses credential
24creation from expiring tokens, records which token created each 34creation from expiring tokens, records which token created each
25credential, and makes =read= the default scope. 35credential, and makes =read= the default scope.
26 36
27* Not yet filed
28
29Found during the 2026-09-27 review.
30
31| Area | Gap | Where |
32|------------------+---------------------------------------------------------------------------------------+---------------------------------------------|
33| Data at rest | CI secrets, webhook secrets and mirror tokens are stored in clear in SQLite | =build_secrets=, =webhooks=, =mirrors= |
34| Backups | The local backup archive is not encrypted | =cmd/gitbayd/backup.go= |
35| Audit | Refused commands are not audited; the audit table is writable by the daemon user | =internal/control/control.go= |
36| Sessions | Web sessions have a 7-day absolute lifetime and no idle timeout | =internal/httpd/accounts.go= |
37| Credentials | SSH and deploy keys never expire | =ssh_keys= |
38| Login links | =web login= over SSH is not counted against the 5-per-hour login-link limit | =internal/control/web.go= |
39| SSRF | Mirror URLs are checked when saved but not when git connects, so a DNS change can redirect a mirror to a private address | =internal/control/mirrorcmd.go= |
40| Mail | STARTTLS is used only when the relay offers it | =internal/mail/mail.go= |
41| TLS | Go defaults; no explicit minimum version | =cmd/gitbayd/main.go= |
42| Hook socket | Any process that can open =hook.sock= can claim any user id; it relies on the data directory's permissions | =internal/hookd/hookd.go= |
43
44* Questions an auditor will ask that have no answer yet 37* Questions an auditor will ask that have no answer yet
45 38
46| Question | Status | 39| Question | Status |