Commit 1ed9fb9399

1ed9fb9399b21da8e6cf45be8389792aac82d5bc

parent: 44e5fb3a83

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-28 07:40 UTC

wiki: key expiry

Closes #277

Layout: unified · split

.gitbay/wiki/API.org +1 −1
@@ -16,7 +16,7 @@ enabled = true
16Tokens are minted wherever the registry is reached: over SSH, on the 16Tokens are minted wherever the registry is reached: over SSH, on the
17API, anywhere. =token create= makes a =read= token unless =--scope full= 17API, anywhere. =token create= makes a =read= token unless =--scope full=
18is given; a read token runs only commands marked read-only. A full-scope 18is given; a read token runs only commands marked read-only. A full-scope
19token can mint another, but a token with a =--ttl= cannot run any 19token can mint another, but a token or SSH key with a =--ttl= cannot run any
20command that creates a credential — =token create=, =keys add=, 20command that creates a credential — =token create=, =keys add=,
21=repo deploy-key add=, =repo runner add=, =web login=, =admin invite=, 21=repo deploy-key add=, =repo runner add=, =web login=, =admin invite=,
22=admin user create=, =email verify=, =admin email verify= — since what 22=admin user create=, =email verify=, =admin email verify= — since what
.gitbay/wiki/Architecture/05-Identity-and-Access.org +2 −2
@@ -15,8 +15,8 @@
15 15
16| Credential | Format and generation | Stored as | Scope | Expiry | Revocation | 16| Credential | Format and generation | Stored as | Scope | Expiry | Revocation |
17|--------------------+-------------------------------------------------+----------------------------------+--------------------------------------------+-------------------------------+-------------------------------------| 17|--------------------+-------------------------------------------------+----------------------------------+--------------------------------------------+-------------------------------+-------------------------------------|
18| SSH user key | user's public key | fingerprint and public blob | =full=, =git=, or =runner= | none | =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 | none | =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 | 7 days, no sliding renewal | logout, =web sessions revoke= | 21| Web session | 32 random bytes hex, cookie =gitbay_session= | SHA-256 hash | full account | 7 days, no sliding renewal | 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 |
.gitbay/wiki/Architecture/09-Controls.org +1 −1
@@ -24,7 +24,7 @@ chapter names of OWASP ASVS 4.0 where one fits.
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 (#276) | 26| Session lifetime | partial | 7 days absolute, no idle timeout (#276) |
27| Credential expiry | partial | API tokens optional; SSH and deploy keys none (#277) | 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=) | 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) | 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 30
.gitbay/wiki/Architecture/10-Known-Gaps.org −1
@@ -20,7 +20,6 @@ what the 2026-09-27 review found; remove a row when its issue closes.
20| #274 | Backups | The local backup archive is not encrypted | medium | 20| #274 | Backups | The local backup archive is not encrypted | medium |
21| #275 | Audit | Refused writes are not audited; the audit table is writable by the daemon user | medium | 21| #275 | Audit | Refused writes are not audited; the audit table is writable by the daemon user | medium |
22| #276 | Sessions | Web sessions last 7 days with no idle timeout | low | 22| #276 | Sessions | Web sessions last 7 days with no idle timeout | low |
23| #277 | Credentials | SSH and deploy keys never expire | low |
24| #278 | Login links | =web login= over SSH skips the login-link rate limit | low | 23| #278 | Login links | =web login= over SSH skips the login-link rate limit | low |
25| #279 | SSRF | Mirror URLs are checked when saved, not when git connects | medium | 24| #279 | SSRF | Mirror URLs are checked when saved, not when git connects | medium |
26| #280 | Mail | STARTTLS only when the relay offers it | medium | 25| #280 | Mail | STARTTLS only when the relay offers it | medium |
.gitbay/wiki/Parity.org +1
@@ -343,6 +343,7 @@ client has no use for one (krz/gitbay#57).
343|-----------------------------+-----+-----+-----| 343|-----------------------------+-----+-----+-----|
344| SSH keys: list, add, remove | yes | yes | yes | 344| SSH keys: list, add, remove | yes | yes | yes |
345| SSH key label | yes | yes | yes | 345| SSH key label | yes | yes | yes |
346| SSH key expiry and last use | yes | no | no |
346| PGP keys: list, add, remove | yes | yes | yes | 347| PGP keys: list, add, remove | yes | yes | yes |
347| email add and verify | yes | yes | yes | 348| email add and verify | yes | yes | yes |
348| email list, remove, primary | yes | yes | yes | 349| email list, remove, primary | yes | yes | yes |
.gitbay/wiki/Users.org +7
@@ -61,6 +61,7 @@ username is always =git= — the key alone determines who you are.
61gitbay auth keys list 61gitbay auth keys list
62gitbay auth keys add --scope git < ~/.ssh/ci_key.pub # key on stdin 62gitbay auth keys add --scope git < ~/.ssh/ci_key.pub # key on stdin
63gitbay auth keys add --label laptop < ~/.ssh/id_ed25519.pub 63gitbay auth keys add --label laptop < ~/.ssh/id_ed25519.pub
64gitbay auth keys add --scope git --ttl 90d < ~/.ssh/ci_key.pub
64gitbay auth keys label SHA256:... "work laptop" 65gitbay auth keys label SHA256:... "work laptop"
65gitbay auth keys remove SHA256:... 66gitbay auth keys remove SHA256:...
66#+end_src 67#+end_src
@@ -69,6 +70,12 @@ A key's label is the comment on its =authorized_keys= line unless
69=--label= gives one; =keys label= renames a key, and with no text 70=--label= gives one; =keys label= renames a key, and with no text
70clears the name. Labels are one line of up to 64 bytes. 71clears the name. Labels are one line of up to 64 bytes.
71 72
73=--ttl 90d= (or any Go duration, =720h=) makes a key stop
74authenticating after that long; =repo deploy-key add= takes the same
75flag. An expiring key cannot create credentials: tokens, keys, login
76links. =keys list= shows when each key was last used and when it
77expires, so a key nobody uses is easy to spot.
78
72Removing a key closes every connection it opened, including the CLI's 79Removing a key closes every connection it opened, including the CLI's
73shared one; removing the key the current command runs on ends that 80shared one; removing the key the current command runs on ends that
74command's connection too. 81command's connection too.
CHANGELOG.org +5
@@ -20,6 +20,11 @@ must add =--scope full=. Existing tokens keep their scope.
20 those too (#257). 20 those too (#257).
21- Removing an SSH key, a deploy key, or disabling an account closes the 21- Removing an SSH key, a deploy key, or disabling an account closes the
22 connections the key opened, a push in flight included (#256). 22 connections the key opened, a push in flight included (#256).
23- =keys add= and =repo deploy-key add= take =--ttl=; an expired key is
24 refused at authentication, and an open connection on it closes within
25 15 seconds. An expiring key cannot create credentials, like an
26 expiring token. =keys list= and =repo deploy-key list= gain =USED= and
27 =EXPIRES= columns, after the label (#277).
23 28
24* v1.36.0 — 2026-09-23 29* v1.36.0 — 2026-09-23
25 30