Skip to content

✨ v1.1: wire up image-trust verification + post-v1.0 audit cleanup#75

Merged
s-b-e-n-s-o-n merged 7 commits into
mainfrom
dev/v1.1
Jun 2, 2026
Merged

✨ v1.1: wire up image-trust verification + post-v1.0 audit cleanup#75
s-b-e-n-s-o-n merged 7 commits into
mainfrom
dev/v1.1

Conversation

@s-b-e-n-s-o-n
Copy link
Copy Markdown
Contributor

Post-v1.0 whole-app audit: find what's dead, what's incomplete, what's stale, and complete the marquee unfinished feature. Five logical commits.

✨ Image trust: fetch + verify cosign signatures (the "incomplete feature")

request_body.container_create.image_trust compiled its keyed/keyless policy but called the verifier with an empty digest and a nil bundle — so enforce failed every container-create closed and warn logged a permanent bypass. The feature was a no-op. Now wired end to end:

  • New internal/imagefetch — resolves the image to its registry manifest digest, discovers cosign signatures (classic sha256-<digest>.sig tag and OCI 1.1 referrers), and reconstructs a sigstore-go verification bundle from the cosign annotations: keyed (public-key) or keyless (Fulcio cert + Rekor inclusion-promise tlog).
  • 🔒 Signature-transplant defense — every signature's simple-signing payload must bind to the resolved manifest digest, so a valid signature for image B can't authorize creating image A.
  • imagetrust — adds Candidate, VerifyCandidatesWithMode (first match wins, mode-aware), and LoadLiveTrustedRoot (TUF root for keyless, fetched once at startup, fail-closed on error).
  • Depsgo-containerregistry + protobuf-specs promoted to direct (only on the opt-in image_trust path, never the core proxy path).
  • Tests — end-to-end against an in-memory registry: valid signature passes, tampered fails, transplanted signature rejected, unsigned → ErrNoSignatures. govulncheck clean (0 reachable).

🗑️ Dead code removal (Go)

Removed verified-unreachable wrappers and constants surfaced by the audit (filter.Middleware, proxy.New, logging.SetDenied, version.String, unused ui styles, etc.); same-package-only helpers moved to export_test.go to avoid churning call sites. Build / vet / race / lint all green.

🗑️ Dead code + phantom deps (TypeScript)

  • Deleted orphan /cli route (never linked; CliDemo is embedded on the landing page).
  • Removed unused Button variants (secondary/ghost/link/icon).
  • Dropped phantom deps: lucide-react (docs, transitive-only) and postcss-load-config (both workspaces, JSDoc-only).
  • Aligned the cli-demo version frame to the real bf59572 / 2026-05-20 release commit.

📝 Stale docs

CONTRIBUTING coverage 90→96 + govulncheck pre-push row + fuzz-subset wording; SECURITY support-window self-contradiction; README Migration row + verification-guide link → URL; CHANGELOG [Unreleased] entries for #66 and #70.

🔧 CI / Helm hygiene

Helm chart image.tag defaults to appVersion (was mutable latest); probe paths templated from healthPath; folded the redundant extended-fuzz job (fully covered by the monthly fuzz workflow).


Validation: go build / go vet (both tags) / go test -race ./... / golangci-lint / govulncheck all clean; turbo build + biome + knip clean. Full lefthook pre-push pipeline green (goreleaser snapshot, fuzz smoke, zizmor, clean-tree).

Delete production-unreachable convenience wrappers and unused symbols
flagged by golang.org/x/tools/cmd/deadcode; relocate test-only helpers
into _test.go so nothing dead ships in the binary.

- 🗑️ remove unused exported wrappers: filter.Middleware, proxy.New,
  logging.SetDenied, ratelimit.NewLimiter, (*Limiter).Allow,
  config.RolloutMode.AllowsPassThrough, version.String,
  cmd.buildServeHandlerWithRuntime; update call sites to the
  lower-level variants they delegated to
- 🗑️ remove unused ui exports: Arrow/Warn/Info glyphs, Printer.W,
  Printer.Yellow (kept Printer.Enabled — real color-detection oracle)
- 🔒 unexport test-only API: proxy.IsHijackEndpoint→isHijackEndpoint,
  policybundle.DigestYAML→digestYAML
- ♻️ move test-only helpers to _test.go: requestIDGenerator.close,
  visibility.requestVisible, ratelimit NewLimiter/Allow

No production behavior change; go test ./..., go vet, golangci-lint all green.
- 🗑️ remove the extended-fuzz job from quality-go-bench-monthly: its 5
  targets (5 min, advisory) are fully superseded by quality-fuzz-monthly,
  which fuzzes all 10 targets for 1h each on the same day-1 schedule;
  rename the workflow to "Go Benchmarks" to match
- 🐛 fix lefthook lockfile-dedupe guard: match the expected postcss
  next-override / workspace-hoist churn by package + path only instead of
  hardcoded versions (8.5.10), which silently broke on every postcss bump
- 🔒 chart: default image.tag to the chart appVersion instead of the
  mutable "latest"; template the liveness/readiness probe path from a new
  healthPath value instead of hardcoding /health

Kept the dependency-review `false &&` guard: it is an intentional
kill-switch pending the repo's Dependency Graph setting, not dead code.
helm lint + template green; workflow YAML validated.
- 🗑️ remove orphan /cli route (website/src/app/cli/page.tsx) — never linked; CliDemo is embedded in the landing page
- 🗑️ drop unused Button variants (secondary/ghost/link) and icon size — zero usages
- 🗑️ remove phantom deps: lucide-react (docs, only used transitively via fumadocs-ui) and postcss-load-config (website+docs devDeps, only a JSDoc @type ref)
- 🐛 align cli-demo version frame to the serve frame's commit (bf59572) and 2026-05-20 build date — prior a8c742f hash wasn't in git history
…Y, CHANGELOG

- 📝 CONTRIBUTING: bump coverage target 90%→96%, add govulncheck pre-push row, reword fuzz-smoke as a subset (27 fuzzers exist, 8 run on push)
- 📝 SECURITY: resolve support-window self-contradiction — only v1.0.x supported until v1.1 ships
- 📝 README: add Migration doc row, fix image-verification link from local path to getsockguard.com/docs URL
- 📝 CHANGELOG: add [Unreleased] entries for #66 (security-testssl.sh) and #70 (landing-page image trust)
…gistry

Image trust previously compiled its keyed/keyless policy but called the
verifier with an empty digest and a nil bundle — enforce mode failed every
container create closed and warn mode logged a permanent bypass. This wires
the feature end to end.

- ✨ add internal/imagefetch: resolve image → registry manifest digest,
  discover cosign signatures (classic sha256-<digest>.sig tag + OCI 1.1
  referrers), reconstruct a sigstore bundle (keyed public-key or keyless
  Fulcio cert + Rekor inclusion-promise tlog) from the cosign annotations
- 🔒 bind every signature's simple-signing payload to the resolved digest
  before verifying — rejects valid-signature-for-another-image transplants
- ✨ imagetrust: add Candidate, VerifyCandidatesWithMode (first match wins,
  mode-aware), and LoadLiveTrustedRoot (TUF root for keyless)
- 🔄 filter/container_create: fetch candidates then verify under mode;
  load the live trust root once at startup for keyless (fail closed on error)
- 📦 promote go-containerregistry + protobuf-specs to direct deps
- 🧪 end-to-end tests over an in-memory registry: valid passes, tampered
  fails, transplanted signature rejected, unsigned → ErrNoSignatures
- 📝 document the registry-fetch + keyless-TUF requirements; note the new
  direct dep in CLAUDE.md and the feature in CHANGELOG
@vercel
Copy link
Copy Markdown

vercel Bot commented May 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sockguard-website Ready Ready Preview, Comment Jun 2, 2026 3:16am

…ty audit

A multi-agent audit of the proxy surfaced and verified 21 bypasses and
isolation gaps across request inspection, read-side filtering, and
config/admin handling. Each is fixed with regression coverage.

- 🐛 fix(plugin): inspect config.json when multipart boundary is absent (#1)
- 🐛 fix(visibility): gate container/image read sub-resources (#9)
- 🔒 security(container/exec): runtime allowlist, empty/case-insensitive root, empty-cap-set (#2,#11,#12)
- 🔒 security(build): deny BuildKit `# syntax=` frontend, cap decompressed context (#7,#10)
- 🔒 security(service): caps/sysctls/image-trust parity with container-create (#3,#6)
- 🔒 security(image-trust): anchor keyless SAN, pin verified digest, default Rekor inclusion (#5,#13,#15)
- 🐛 fix(image-load): inspect gzipped archives instead of false-denying (#20)
- 🔒 security(ownership): owner-filter image /get, stop memoizing not-found (#4,#14)
- 🔒 security(responsefilter): redact HostConfig.Mounts source + service PreviousSpec (#17,#18)
- 🔒 security(config/admin): bundle TOCTOU single-read, block env override of signed policy, reject PID-only peer profiles, admin-listener CIDR backstop + warning (#8,#16,#19,#21)
- 📝 docs: record the audit in CHANGELOG Unreleased + README recent-updates

Verified: go test ./... -race (all pass), govulncheck (0 affecting), golangci-lint (0 issues).
- 📝 changelog: cut [1.1.0] - 2026-06-01, add fresh [Unreleased] block
- 🔧 chart: bump version + appVersion to 1.1.0
- 📝 docs(README): add 'Shipped in v1.1.0' table, refresh recent-updates, clarify v1.0.0 image-trust was schema-only (enforcement wired in v1.1.0)
- 📝 docs(config/security): document image-trust end-to-end, require_rekor_inclusion default true, swarm-service policy parity, allowed_runtimes allowlist
- 📝 docs(SECURITY): rolling two-minor support window (1.1.x + 1.0.x)
- 🗑️ docs/web: relabel stale multi-host roadmap refs v1.1 → v1.2 (not in this release)
- 🐛 docs: correct allowed_runtimes semantics — an empty/unset HostConfig.Runtime selects the daemon default and is always permitted; only a non-empty runtime needs allowlisting (matches container_create.go:438)
Copy link
Copy Markdown
Member

@biggest-littlest biggest-littlest left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Approving v1.1.0 release. CI green (all 9 required checks + fuzz/integration). Smoke-tested the v1.1.0-rc multi-arch image on the Synology DS920+ NAS: /health healthy + upstream connected, GET /version proxied to real Docker, write denied 403. Image-trust config compiles + fails closed.

@s-b-e-n-s-o-n s-b-e-n-s-o-n merged commit 579b68e into main Jun 2, 2026
37 checks passed
@s-b-e-n-s-o-n s-b-e-n-s-o-n deleted the dev/v1.1 branch June 2, 2026 14:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants