Commit 928b919242

928b9192421c88e67439ef307a9ce9729f89f37d

parent: 6f553fea02

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

cmc <hello@cleberg.net> · 2026-09-28 08:25 UTC

https: TLS 1.2 minimum, set explicitly

Closes #281

Layout: unified · split

.gitbay/wiki/Admin.org +4
@@ -78,6 +78,10 @@ validation still prints, followed by the contradiction.
78 site_url with a public DNS name. 78 site_url with a public DNS name.
79- =files=: =cert_file= + =key_file=. 79- =files=: =cert_file= + =key_file=.
80- =off=: plain HTTP — development, or behind a TLS-terminating proxy. 80- =off=: plain HTTP — development, or behind a TLS-terminating proxy.
81- The HTTPS listener accepts TLS 1.2 and 1.3 only (=serverTLS= in
82 =cmd/gitbayd/tls.go=), with the default cipher suites of the Go
83 release the binary was built with. =openssl s_client -connect
84 <host>:443 -tls1_1= fails the handshake.
81 85
82=trusted_proxies= lists the addresses or CIDRs of reverse proxies in 86=trusted_proxies= lists the addresses or CIDRs of reverse proxies in
83front of the daemon. A request from one of them is attributed, for API 87front of the daemon. A request from one of them is attributed, for API
.gitbay/wiki/Architecture/06-Data-and-Cryptography.org +3 −3
@@ -56,7 +56,7 @@ secret, webhook secret and mirror token.
56| Channel | Protection | 56| Channel | Protection |
57|--------------------------+--------------------------------------------------------------| 57|--------------------------+--------------------------------------------------------------|
58| SSH | Go =x/crypto/ssh=; ed25519 host key generated on first start | 58| SSH | Go =x/crypto/ssh=; ed25519 host key generated on first start |
59| HTTPS | TLS via ACME or operator certificates; HSTS one year | 59| HTTPS | TLS 1.2 minimum (=cmd/gitbayd/tls.go=), ACME or operator certificates; HSTS one year |
60| HTTP port 80 | ACME challenges and redirect only | 60| HTTP port 80 | ACME challenges and redirect only |
61| git:// | none (public data only; off by default) | 61| git:// | none (public data only; off by default) |
62| Runner ↔ server | SSH | 62| Runner ↔ server | SSH |
@@ -65,8 +65,8 @@ secret, webhook secret and mirror token.
65| Webhooks | TLS when the URL is https; HMAC-SHA256 body signature in =X-Gitbay-Signature-256= (=internal/webhook/webhook.go=) | 65| Webhooks | TLS when the URL is https; HMAC-SHA256 body signature in =X-Gitbay-Signature-256= (=internal/webhook/webhook.go=) |
66| Mirrors | per URL; token passed through =GIT_ASKPASS=, never argv (=internal/mirror/mirror.go=) | 66| Mirrors | per URL; token passed through =GIT_ASKPASS=, never argv (=internal/mirror/mirror.go=) |
67 67
68The TLS configuration uses Go's defaults; no minimum version or cipher 68TLS 1.2 is the minimum, set in code (=serverTLS= in
69list is set in code. 69=cmd/gitbayd/tls.go=); cipher suites are Go's defaults.
70 70
71* Cryptographic primitives 71* Cryptographic primitives
72 72
.gitbay/wiki/Architecture/10-Known-Gaps.org −1
@@ -21,7 +21,6 @@ what the 2026-09-27 review found; remove a row when its issue closes.
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| #279 | SSRF | Mirror URLs are checked when saved, not when git connects | medium | 22| #279 | SSRF | Mirror URLs are checked when saved, not when git connects | medium |
23| #280 | Mail | STARTTLS only when the relay offers it | medium | 23| #280 | Mail | STARTTLS only when the relay offers it | medium |
24| #281 | TLS | No explicit minimum TLS version | low |
25| #282 | Hook socket | Anything that can open =hook.sock= can act as any user | medium | 24| #282 | Hook socket | Anything that can open =hook.sock= can act as any user | medium |
26| #297 | Credentials | A browser session can mint tokens and keys that outlive it | low | 25| #297 | Credentials | A browser session can mint tokens and keys that outlive it | low |
27 26
CHANGELOG.org +2
@@ -33,6 +33,8 @@ must add =--scope full=. Existing tokens keep their scope.
33 hours. =web sessions list= shows when each was last used (#276). 33 hours. =web sessions list= shows when each was last used (#276).
34- =web login= over SSH refuses a sixth link in an hour, the same bound 34- =web login= over SSH refuses a sixth link in an hour, the same bound
35 the login page's mailed links have (#278). 35 the login page's mailed links have (#278).
36- The HTTPS listener refuses TLS below 1.2, in both certificate modes
37 (#281).
36 38
37* v1.36.0 — 2026-09-23 39* v1.36.0 — 2026-09-23
38 40
cmd/gitbayd/main.go +2 −1
@@ -239,6 +239,7 @@ func serveCmd() *cobra.Command {
239 case "off": 239 case "off":
240 errCh <- hs.ListenAndServe() 240 errCh <- hs.ListenAndServe()
241 case "files": 241 case "files":
242 hs.TLSConfig = serverTLS(nil)
242 errCh <- hs.ListenAndServeTLS(cfg.HTTP.CertFile, cfg.HTTP.KeyFile) 243 errCh <- hs.ListenAndServeTLS(cfg.HTTP.CertFile, cfg.HTTP.KeyFile)
243 case "acme": 244 case "acme":
244 host := cfg.SiteHost() 245 host := cfg.SiteHost()
@@ -298,7 +299,7 @@ func serveCmd() *cobra.Command {
298 } 299 }
299 }() 300 }()
300 } 301 }
301 hs.TLSConfig = m.TLSConfig() 302 hs.TLSConfig = serverTLS(m.TLSConfig())
302 errCh <- hs.ListenAndServeTLS("", "") 303 errCh <- hs.ListenAndServeTLS("", "")
303 } 304 }
304 }() 305 }()
cmd/gitbayd/tls.go added +13
@@ -0,0 +1,13 @@
1package main
2
3import "crypto/tls"
4
5// serverTLS sets the HTTPS listener's protocol floor: TLS 1.2 and 1.3,
6// with Go's default cipher suites.
7func serverTLS(c *tls.Config) *tls.Config {
8 if c == nil {
9 c = &tls.Config{}
10 }
11 c.MinVersion = tls.VersionTLS12
12 return c
13}
cmd/gitbayd/tls_test.go added +21
@@ -0,0 +1,21 @@
1package main
2
3import (
4 "crypto/tls"
5 "testing"
6)
7
8// The floor is stated in code rather than inherited from the Go
9// release the binary was built with (#281).
10func TestServerTLSMinimum(t *testing.T) {
11 if got := serverTLS(nil).MinVersion; got != tls.VersionTLS12 {
12 t.Fatalf("files mode: MinVersion %#x, want %#x", got, tls.VersionTLS12)
13 }
14 // autocert's config carries the ALPN protocols TLS-ALPN-01 needs;
15 // setting the floor must keep them.
16 base := &tls.Config{NextProtos: []string{"h2", "http/1.1", "acme-tls/1"}}
17 got := serverTLS(base)
18 if got.MinVersion != tls.VersionTLS12 || len(got.NextProtos) != 3 {
19 t.Fatalf("acme mode: %+v", got)
20 }
21}