Commit ff336e2
fix(security): P2 agent batch (P2-5/7/9/10) (#51)
* fix(security): strip version + uptime from /health response
The unauthenticated /health endpoint returned:
{"status":"ok","version":"1.2.3","uptime":"2h30m15s","timestamp":...}
`version` is populated from compile-time ldflags. A remote scanner
probing /health learns the exact agent build and can correlate against
public CVEs without needing to authenticate. `uptime` reveals whether
the agent has been recently restarted (i.e. whether a patch was
applied). `timestamp` is mostly harmless but adds another datum to a
fingerprint.
Authenticated callers that legitimately need the version still get it
from /api/v1/metadata.
The Handlers struct keeps the `version` and `startTime` fields so the
NewHandlers signature doesn't churn — they're just no longer exposed
via /health.
Test asserts the response body is exactly `{"status":"ok"}` and
contains none of the substrings "1.2.3-leakytest", "uptime", or
"timestamp".
P2-5 from the 2026-05 security audit.
* fix(security): force TLS 1.3 minimum on the agent HTTPS listener
http.Server with no explicit TLSConfig falls through to Go's default,
which is TLS 1.2 floor. Acceptable per RFC, but it leaves the agent
willing to negotiate cipher suites that have been deprecated for years
(CBC modes, RC4 if a downgrade happens, etc.). Both ends of the
agent <-> dashboard channel are software we control; there's no
legitimate reason to support pre-1.3.
Explicit MinVersion: tls.VersionTLS13.
A client that genuinely cannot speak TLS 1.3 (Go ≥ 1.12, OpenSSL ≥
1.1.1, Node ≥ 10.13, basically anything from the last 8 years) will
fail the handshake — which is the correct behavior. The dashboard
binary that talks to this agent is built with the same Go toolchain;
it speaks 1.3 by default.
P2-7 from the 2026-05 security audit.
* fix(security): tighten apt package-name validation; add `--` separator
The /api/v1/packages/install and /packages/remove handlers validated
input as "non-empty AND ≤200 chars" before passing to apt-get. The
package-manager layer (isValidPackageName) already had the strict
Debian-style regex + leading-hyphen rejection, so the practical
exploit window was small — but errors surfaced as generic 500s and
the boundary check was misleadingly weak.
Two changes:
1. Handlers now call isValidPackageName directly. Invalid names return
400 with a clear message instead of falling through to a 500 from
the package-manager layer.
2. apt-get and apt-mark invocations get an explicit "--" between flags
and the package operand:
apt-get install -y -- <name>
apt-get remove -y -- <name>
apt-get install -y --allow-downgrades -- <pkgs...>
apt-mark hold -- <name>
apt-mark unhold -- <name>
Defense-in-depth: a future loosening of isValidPackageName (or a
missed validation site, or a new caller that forgets to call it)
still can't smuggle a "package" name that apt-get would parse as a
flag.
Tests cover the attack class:
--allow-downgrades, --reinstall, -y, --help, .bashrc, name with
spaces / semicolons / $() / newlines / slashes.
P2-9 from the 2026-05 security audit.
* fix(security): validate haproxy.acl.path + haproxy.acl.header at parse time
The audit's P2-10 framing was "drop the dead ACLPath/ACLHeader fields,"
but those labels are documented in
ubuntu-ha-proxy-install/docs/architecture.md and
ubuntu-ha-proxy-install/docs/example-docker-compose.yml as supported
forward-looking features ("Path-based routing", "Custom ACL conditions").
Deleting the BackendConfig fields would break that documented API.
Instead, validate them now so the latent injection class (same shape as
P0-1 BackendName and P0-2 ACLIP) is closed before any future PR wires
them into a generated directive like:
acl req_path path_beg <path>
acl req_hdr_cnt(<header>) gt 0
Two new validators in parser.go:
- validACLPath: leading slash + URL-path-safe charset
(`^/[a-zA-Z0-9/_.\-~%]*$`). Rejects embedded newlines, semicolons,
whitespace, query strings, fragments.
- validACLHeader: RFC 7230 HTTP header token characters
(`^[a-zA-Z0-9!#$%&'*+\-.^_`+"`"+`|~]+$`). Rejects newlines, colons,
slashes, spaces.
extractBackendConfig now rejects the whole backend if either field has
a non-empty malformed value. Empty values (the overwhelmingly common
case) skip validation as today.
Tests cover the legitimate-shape pass list, the injection class
(newline + HAProxy directive payload), and the common-injection-
character reject list.
P2-10 from the 2026-05 security audit.
* fix(security): address P2 agent batch Copilot review
- pm_apt.go: add "--" separator to BuildInstallCommand + InstallUpdates
install paths. Was already on InstallPackage/RemovePackage but the two
bulk-install paths were the actual gap — defense-in-depth against a
caller that skips isValidPackageName.
- handlers.go: remove dead Handlers.version and Handlers.startTime
fields. They were set but never read once the /health response was
stripped to {status: "ok"}. Update HealthResponse doc to drop the
inaccurate "available via /api/v1/metadata" claim — MetadataResponse
exposes HAProxy metadata, not agent version/uptime.
- server.go: rewrite the TLS-1.3-floor comment. The previous text
claimed Go's TLS 1.2 defaults included CBC and RC4; RC4 has been
removed from Go's defaults for years and the default cipher suites
are mostly AEAD. The concrete reason (shrink negotiation surface,
guarantee AEAD + forward secrecy, prevent downgrade) is what
matters.
- docs/: regenerate Swagger schema so the published OpenAPI doc
matches the stripped HealthResponse (was still advertising version,
uptime, timestamp fields).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 3a1cf94 commit ff336e2
12 files changed
Lines changed: 847 additions & 615 deletions
File tree
- gearbox-agent
- docs
- internal
- api
- framework/services/compose
- gears/updates
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
22 | | - | |
23 | 21 | | |
24 | 22 | | |
25 | 23 | | |
26 | | - | |
| 24 | + | |
27 | 25 | | |
28 | 26 | | |
29 | | - | |
30 | | - | |
31 | 27 | | |
32 | 28 | | |
33 | 29 | | |
34 | 30 | | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
35 | 37 | | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
| 38 | + | |
40 | 39 | | |
41 | 40 | | |
42 | 41 | | |
43 | 42 | | |
44 | 43 | | |
45 | | - | |
| 44 | + | |
46 | 45 | | |
47 | 46 | | |
48 | 47 | | |
49 | 48 | | |
50 | 49 | | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
| 50 | + | |
57 | 51 | | |
58 | 52 | | |
59 | 53 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
| |||
69 | 70 | | |
70 | 71 | | |
71 | 72 | | |
72 | | - | |
| 73 | + | |
73 | 74 | | |
74 | 75 | | |
75 | 76 | | |
| |||
157 | 158 | | |
158 | 159 | | |
159 | 160 | | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
160 | 171 | | |
161 | 172 | | |
162 | 173 | | |
| |||
Lines changed: 39 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
46 | 58 | | |
47 | 59 | | |
48 | 60 | | |
| |||
312 | 324 | | |
313 | 325 | | |
314 | 326 | | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
315 | 352 | | |
316 | 353 | | |
317 | 354 | | |
| |||
334 | 371 | | |
335 | 372 | | |
336 | 373 | | |
337 | | - | |
338 | | - | |
| 374 | + | |
| 375 | + | |
339 | 376 | | |
340 | 377 | | |
341 | 378 | | |
| |||
Lines changed: 101 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
757 | 757 | | |
758 | 758 | | |
759 | 759 | | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
| 788 | + | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
| 793 | + | |
| 794 | + | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| 807 | + | |
| 808 | + | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
| 815 | + | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
| 842 | + | |
| 843 | + | |
| 844 | + | |
| 845 | + | |
| 846 | + | |
| 847 | + | |
| 848 | + | |
| 849 | + | |
| 850 | + | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
| 857 | + | |
| 858 | + | |
| 859 | + | |
| 860 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
219 | 219 | | |
220 | 220 | | |
221 | 221 | | |
222 | | - | |
223 | | - | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
224 | 229 | | |
225 | 230 | | |
226 | 231 | | |
| |||
502 | 507 | | |
503 | 508 | | |
504 | 509 | | |
505 | | - | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
506 | 513 | | |
507 | 514 | | |
508 | 515 | | |
| |||
Lines changed: 52 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
0 commit comments