.gitbay/wiki/Architecture/04-Trust-Boundaries.org
135 lines · 8959 bytes
11 symbols in this file
1#+title: Trust boundaries and data flows
2
3[[file:diagrams/04-trust-boundaries.svg]]
4
5* Zones
6
7| Zone | Contents | Trust |
8|------+-----------------------------------------------------------------+-----------------------------------------------------------|
9| Z0 | The internet: visitors, clients, webhook and mirror endpoints | none |
10| Z1 | gitbayd process | holds all policy; trusted |
11| Z2 | Local state: SQLite, repositories, LFS, keys, config | trusted; readable by the =gitbay= user |
12| Z3 | git subprocesses and hook processes | run as =gitbay= on data from Z0; their decisions come from Z1 |
13| Z4 | CI runner service | trusted to report honestly; holds secrets for trusted builds |
14| Z5 | CI containers | untrusted code from repositories and forks |
15| Z6 | Operator host access | root; outside every in-application control |
16
17* Boundaries
18
19| ID | Boundary | What crosses | Control at the boundary |
20|-----+------------------------------------+--------------------------------------------------+-----------------------------------------------------------------------------------------|
21| TB1 | Z0 → Z1 SSH | key auth, exec requests, git packs | public-key auth, per-IP failure limit (=internal/sshd/sshd.go=, =ratelimit.go=); unknown keys reach only =register= |
22| TB2 | Z0 → Z1 HTTPS | page requests, form posts, API calls, fetches, LFS | TLS; session cookie or bearer token; =checkOrigin= on posts; CSP and security headers (=internal/httpd/routes.go=); smart HTTP is fetch-only (=smart.go=) |
23| TB3 | identity → data | every command | =Dispatch= gates, then =resolveRepo= with =policy= predicates; unreadable repositories are indistinguishable from missing ones ([[file:05-Identity-and-Access.org][5]]) |
24| TB4 | Z1 → Z3 git | argv, repository path, stdin packs | argv built by code, never a shell; repository path from the database, not the request (=internal/gitutil=) |
25| TB5 | Z3 → Z1 hook socket | ref updates, repository id, user id, key scope, push token, commit objects | the socket is mode 0600 and, on Linux, refuses a peer whose uid is not the daemon's; a request must carry the token sshd minted for its receive-pack (stored hashed in =push_tokens=) and name the same repository, account and scope. The daemon then decides with =policy.CheckPush= and =sig.VerifyCommit= (=internal/hookd/hookd.go=) |
26| TB6 | Z4 ↔ Z1 runner channel | build claims (with secrets for trusted builds), logs, results | runner-scoped SSH key; claims limited to attached repositories; secrets only when the build is trusted (=internal/control/build.go=) |
27| TB7 | Z5 → Z4 container | build steps, workspace, build home | rootless podman, operator-provisioned image, cgroup limits; a trusted build's home is its repository's, an untrusted build's is discarded with it; private ranges and the host's loopback (but DNS) closed; trusted: internet open, host public 22/80/443; untrusted: internet TCP 80/443 and DNS, no host (#260) |
28| TB8 | Z1 → Z0 outbound | webhooks, mirrors, mail, push | address checks on user-supplied URLs; HMAC on webhooks; no redirects ([[file:03-Deployment.org][3]]) |
29| TB9 | user content → browser | Markdown and Org bodies, READMEs, filenames | HTML sanitised (=ugcHTML=, =internal/httpd/web.go=, bluemonday); CSP =script-src 'none'= |
30| TB10| Z6 → everything | host shell | operator SSH on 2222, keys only, fail2ban; append-only offsite backup credentials |
31
32* Flows
33
34Each flow lists its hops in order. Boundary IDs refer to the table
35above.
36
37** A. SSH control command
38
391. Client opens SSH; =authenticate= looks up the key fingerprint and
40 records user id, key id and scope in the connection
41 (=internal/sshd/sshd.go=). TB1.
422. Each exec request: =runExec= reloads the account, touches the key's
43 last-used time, calls =Exec= (=sshd.go=).
443. =Exec= tokenizes the command line (no shell) and routes git
45 transport verbs to =runGit=, everything else to =control.Dispatch=
46 with =Source= set to the key fingerprint (=sshd.go=).
474. =Dispatch= gates and runs the handler; mutating successes are
48 audited (=internal/control/control.go=). TB3.
49
50** B. git push over SSH
51
52[[file:diagrams/06-push-flow.svg]]
53
541. =runGit= resolves the repository, applies the deploy-key or account
55 checks, archive and pull-mirror refusals and the owner's storage
56 quota (=sshd.go=). TB3.
572. =git receive-pack= runs with the hook socket path, repository id,
58 user id and key scope in its environment, and a push token (=sshd.go=). TB4.
593. git runs =pre-receive=, which is =gitbayd hook pre-receive=. It
60 reads the ref updates, computes ancestry in git's quarantine
61 environment and asks the daemon over the socket
62 (=cmd/gitbayd/hook.go=). TB5.
634. The daemon applies =policy.CheckPush= (protected branches,
64 =require-mr=, protected tags, server-owned =refs/merge-requests/*=)
65 and, when the repository requires signed commits, asks for every
66 incoming commit object and verifies each
67 (=internal/hookd/hookd.go=).
685. On refusal the hook exits 1 and git rejects the push atomically.
696. On success git runs =post-receive=; the daemon records events,
70 marks mirrors dirty, queues CI, syncs merge request heads, processes
71 =Closes #N= references and audits force pushes
72 (=hookd.go=).
73
74The server's own merge of a merge request does not pass through the
75hooks: =runMRMerge= updates the ref with a compare-and-swap
76(=internal/control/mr.go=) after =MergeGates=
77(=mr.go=). When signed commits are required only fast-forward
78merges are allowed, so the server never writes an unsigned commit
79(=mr.go=).
80
81** C. Fetch over smart HTTP
82
83=GET info/refs= and =POST git-upload-pack= serve public repositories
84only; a private repository answers 404. =git-receive-pack= over HTTP
85always answers a pkt-line refusal, so there is no password prompt and
86no HTTP write path (=internal/httpd/smart.go=,
87=routes.go=). TB2.
88
89** D. Web read and write
90
911. The session cookie is hashed and looked up (=accounts.go=).
922. Pages dispatch read commands into the registry with =Source=web= and
93 decode the JSON result into the template (=internal/httpd/control.go=).
943. Form posts pass =checkOrigin= (=accounts.go=), dispatch the
95 matching command, and map the exit code to a redirect or an error on
96 the page. The pin, watch and mark-read toggles dispatch =repo pin=,
97 =repo watch=/=mute=/=unwatch= and =notifications read= the same way.
98
99** E. JSON API
100
1011. =apiAuth= hashes the bearer token and looks it up; any failure is a
102 uniform 401 (=internal/httpd/api.go=).
1032. Per-account rate limit, with writes at a tenth of the read budget.
1043. =POST /api/v1/cmd= dispatches any command except git transport;
105 =GET /api/v1/read= refuses anything not marked =ReadOnly=, so a GET
106 cannot write (=apiread.go=).
1074. Exit codes map to HTTP status: 0→200, 2→400, 3→404, 4→403, else 500.
108
109** F. LFS
110
111=git-lfs-authenticate= over SSH applies the same repository checks as
112git transport and returns a one-hour HMAC token scoped to repository,
113operation and the SSH key that asked for it, deploy keys included
114(=internal/sshd/lfs.go=, =internal/lfs/lfs.go=). The HTTP batch, upload
115and download endpoints verify that token and that its key is still
116registered, unexpired and on an enabled account (=store.LiveSSHKeys=),
117and repeat the repository check for the key on each request; public
118repositories allow anonymous download. Objects are verified against
119their SHA-256 id on upload.
120
121** G. Browser login by emailed link
122
1231. =/login= takes a username or email; per-IP and per-account limits
124 (5 per hour) apply, and every non-eligible case returns the same
125 response (=internal/control/loginlink.go=).
1262. A 32-byte token is mailed; only its hash is stored, valid 15 minutes.
1273. =/login?token== consumes it atomically, rechecks the account and
128 sets the session cookie (=accounts.go=).
129
130=ssh git@host web login= mints the same kind of link, valid 5 minutes,
131to an already-authenticated key (=internal/control/web.go=).
132
133** H. CI build
134
135See [[file:07-CI-and-Supply-Chain.org][7. CI and supply chain]].