Commit 273e9b95f8

273e9b95f8b87ee2be442c6bff6cbffbfd09cccf

parent: ab95b83440

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-29 02:15 UTC

wiki: web mints and grants need a sign-in from the last 15 minutes

Closes #297

Layout: unified · split

.gitbay/wiki/Architecture/05-Identity-and-Access.org +12 −1
@@ -18,7 +18,7 @@
18| SSH user key | user's public key | fingerprint and public blob | =full=, =git=, or =runner= | optional =--ttl=, refused at auth | =keys remove= (own keys); closes its connections | 18| SSH user key | user's public key | fingerprint and public blob | =full=, =git=, or =runner= | optional =--ttl=, refused at auth | =keys remove= (own keys); closes its connections |
19| Deploy key | public key | same table, scope =deploy:<repo>:ro/rw= | one repository, read or read-write | optional =--ttl=, refused at auth | =repo deploy-key remove= (repo admin); closes its connections | 19| Deploy key | public key | same table, scope =deploy:<repo>:ro/rw= | one repository, read or read-write | optional =--ttl=, refused at auth | =repo deploy-key remove= (repo admin); closes its connections |
20| API token | =gb_= + 32 random bytes hex | SHA-256 hash | =read= (default) or =full=; with an expiry, no credential-minting command | optional =--ttl= | =token revoke [--created]= | 20| API token | =gb_= + 32 random bytes hex | SHA-256 hash | =read= (default) or =full=; with an expiry, no credential-minting command | optional =--ttl= | =token revoke [--created]= |
21| Web session | 32 random bytes hex, cookie =gitbay_session= | SHA-256 hash | full account | 12 h idle, 7 days absolute | logout, =web sessions revoke= | 21| Web session | 32 random bytes hex, cookie =gitbay_session= | SHA-256 hash | full account; credential-minting and access-granting commands only within 15 minutes of sign-in | 12 h idle, 7 days absolute | logout, =web sessions revoke= |
22| Login link | 32 random bytes hex in a URL | SHA-256 hash, single use | creates a web session | 15 min (mail), 5 min (SSH) | consumed on use | 22| Login link | 32 random bytes hex in a URL | SHA-256 hash, single use | creates a web session | 15 min (mail), 5 min (SSH) | consumed on use |
23| Email verification | 32 random bytes hex | SHA-256 hash, single use | verifies one address for one account | 24 h | consumed on use | 23| Email verification | 32 random bytes hex | SHA-256 hash, single use | verifies one address for one account | 24 h | consumed on use |
24| Invite | random code | SHA-256 hash, single use | one registration for one email | as issued | consumed on use | 24| Invite | random code | SHA-256 hash, single use | one registration for one email | as issued | consumed on use |
@@ -118,6 +118,17 @@ button and the displayed status (=internal/control/mr.go=):
118- Destructive web actions (key, email and PGP removal, release, snippet, 118- Destructive web actions (key, email and PGP removal, release, snippet,
119 team and label deletion, user disable and demote) require the target's 119 team and label deletion, user disable and demote) require the target's
120 name typed into the form (=internal/httpd/confirm.go=). 120 name typed into the form (=internal/httpd/confirm.go=).
121- Commands that create a credential (SSH, deploy and runner keys, API
122 tokens, email verification, login links, PGP keys, device tokens) or
123 grant access (repository and organization roles, teams, transfers,
124 admin promote and enable, webhooks, secrets, mirrors) are refused
125 from a browser session that signed in more than 15 minutes ago
126 (=control.ReauthWindow=, =Command.NeedsRecentSignIn=). The sign-in
127 time is =web_sessions.created_at=, which idle renewal does not move;
128 a request with no sign-in time is refused. SSH, API tokens and host
129 commands are unaffected. The refusal is audited; the form shows it
130 with a sign-in link, and the login returns to the page through the
131 server-set =gitbay_next= cookie (=internal/httpd/flash.go=).
121 132
122* Rate limits 133* Rate limits
123 134
.gitbay/wiki/Architecture/09-Controls.org +1 −1
@@ -26,7 +26,7 @@ chapter names of OWASP ASVS 4.0 where one fits.
26| Session lifetime | in place | 12 hours idle, 7 days absolute (=internal/store/sessions.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 | 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=); LFS transfer tokens are refused with their key (=internal/httpd/lfs.go=) | 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=); LFS transfer tokens are refused with their key (=internal/httpd/lfs.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) | 29| Delegation bounded by the delegating credential | in place | expiring tokens refused on =MintsCredential= commands; credentials record their creating token; a browser session runs credential-minting and access-granting commands only within 15 minutes of signing in, and the refusal is audited (=internal/control/control.go=) |
30 30
31** Access control (V4) 31** Access control (V4)
32 32
.gitbay/wiki/Architecture/10-Known-Gaps.org −2
@@ -13,8 +13,6 @@ what the 2026-09-27 review found; remove a row when its issue closes.
13| #259 | Recovery | No restore has been exercised; the drill is written (Admin wiki) and not yet run | high | 13| #259 | Recovery | No restore has been exercised; the drill is written (Admin wiki) and not yet run | high |
14| #260 | CI network | Builds share the runner's source address; no egress policy | medium | 14| #260 | CI network | Builds share the runner's source address; no egress policy | medium |
15| #261 | Various | Migration foreign-key check after commit; three web writes bypass dispatch; documentation drift | medium | 15| #261 | Various | Migration foreign-key check after commit; three web writes bypass dispatch; documentation drift | medium |
16| #297 | Credentials | A browser session can mint tokens and keys that outlive it | low |
17| #301 | SSRF | =repo import-issues --api-base= fetches without an address check or pin | medium |
18 16
19* Not filed 17* Not filed
20 18
.gitbay/wiki/Threat-Model.org +3 −2
@@ -61,8 +61,9 @@ matrix and the open gaps are in the [[file:Architecture/00-Overview.org][Archite
61 rights narrowed by its credential's scope, decided in one place, so a 61 rights narrowed by its credential's scope, decided in one place, so a
62 bearer token is worth exactly its scope and no more, and a token or 62 bearer token is worth exactly its scope and no more, and a token or
63 SSH key with an expiry cannot create a credential that outlives it. 63 SSH key with an expiry cannot create a credential that outlives it.
64 Browser sessions are not covered yet (#297). Git transport never runs 64 A browser session can create one, or grant access, only within 15
65 over the API. 65 minutes of signing in (=control.ReauthWindow=, #297). Git transport
66 never runs over the API.
66- *Anonymous surfaces* — HTTPS clone of public repos, =git://= where 67- *Anonymous surfaces* — HTTPS clone of public repos, =git://= where
67 enabled, the read-only web UI — carry no credentials and expose only 68 enabled, the read-only web UI — carry no credentials and expose only
68 public data. HTTP push is refused via a pkt-line =ERR=, never a 401. 69 public data. HTTP push is refused via a pkt-line =ERR=, never a 401.
.gitbay/wiki/Users.org +6
@@ -701,6 +701,12 @@ creation, expiry and last use, and =gitbay web sessions revoke <id>=
701or =--all= ends them from the terminal, which is where a lost laptop is 701or =--all= ends them from the terminal, which is where a lost laptop is
702handled. 702handled.
703 703
704Actions that create a credential or grant access (adding a key, token
705or email, org and repository roles, transfers) ask you to sign in again
706when your web sign-in is older than 15 minutes. The form shows a "Sign
707in again" link and the login returns to the page. Idle renewal does not
708extend this window.
709
704=web theme set light= or =dark= fixes the web UI's colour scheme for 710=web theme set light= or =dark= fixes the web UI's colour scheme for
705your account; =system=, the default, follows the browser's own 711your account; =system=, the default, follows the browser's own
706preference. =web theme show= prints it. The account page has the same 712preference. =web theme show= prints it. The account page has the same
CHANGELOG.org +6
@@ -27,6 +27,12 @@ too; rewrite it before upgrading.
27 access — keys, PGP keys, tokens, org membership, and the admin 27 access — keys, PGP keys, tokens, org membership, and the admin
28 promote/enable actions — and the form it tried shows a sign-in link 28 promote/enable actions — and the form it tried shows a sign-in link
29 that returns there (#297). 29 that returns there (#297).
30- A browser session creates credentials and grants access — keys, PGP
31 keys, tokens, verified addresses, org and repository roles, transfers,
32 webhooks, secrets, mirrors, and the admin promote/enable actions —
33 only within 15 minutes of signing in. An older session gets the form
34 back with a "Sign in again" link, and the login returns to it. SSH and
35 API tokens are unaffected (#297).
30- The builds page's status badge section gives an org-mode snippet 36- The builds page's status badge section gives an org-mode snippet
31 beside the Markdown one, for a README.org (#299). 37 beside the Markdown one, for a README.org (#299).
32- API tokens on the settings page: create with a scope and optional 38- API tokens on the settings page: create with a scope and optional