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 |
| 83 | front of the daemon. A request from one of them is attributed, for API |
87 | front 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 | |
| 68 | The TLS configuration uses Go's defaults; no minimum version or cipher |
68 | TLS 1.2 is the minimum, set in code (=serverTLS= in |
| 69 | list 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 @@ |
| |
1 | package main |
| |
2 | |
| |
3 | import "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. |
| |
7 | func 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 @@ |
| |
1 | package main |
| |
2 | |
| |
3 | import ( |
| |
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). |
| |
10 | func 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 | } |