Files
s3-gateway/README.md
T
57_WolveandClaude Opus 5.5 152ea01107 patches: add v1.11.0 owner-scoped access patch and its leil-rig proof
0003-owner-scoped-access.patch changes authorization, applied after the
de-brand and dark-theme patches:

- a bucket's ACL and policy may name only the owner's keys (accounts on
  the owner's UserID), whoever writes them, root included;
- public access is read-only: all-users READ, and "*" allowed only
  s3:GetObject and s3:ListBucket;
- the bucket policy never denies the owner's key;
- DeleteBucket, PutBucketVersioning, PutObjectLockConfiguration and
  PutBucketOwnershipControls are reserved to root and admins;
- an ACL with the all-users READ grant can be written back unchanged.

docs/owner-scoped-access.md has the rules, what v1.11.0 already did,
and the leil-rig proof procedure with its 2026-11-10 result: 63 of 63
on the patched build, 29 of 63 failing on stock v1.11.0.
tests/owner-scope/ holds the proof script and a stdlib-only SigV4
request helper. README and the pipeline doc list the patch and how to
re-verify it after a rebase.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 23:50:47 -06:00

9.9 KiB
Raw Blame History

s3-gateway

Downstream build pipeline that produces de-branded versitygw releases — the upstream Versity S3 Gateway with Versity trademarks and logos removed from the admin web UI.

Same pattern as scoutfs-build: pull an upstream release tag, apply our patch series scoped to that tag, build with upstream's GoReleaser, and publish to Gitea Releases.

Artifact names are intentionally unchanged — the binary stays versitygw and the deb/rpm packages stay versitygw, so builds are drop-in compatible with existing configs, systemd units, and the versitygw CLI. Only the embedded web UI is rebranded to "S3 Gateway".

The v1.11.0 series also carries one behavioural patch, owner-scoped bucket access: a bucket's ACL and policy may name only the owner's own keys, public access is read-only, and deleting a bucket or changing its versioning, object lock or ownership controls is left to the gateway's root and admins. See docs/owner-scoped-access.md.

 upstream tag (vX.Y.Z)            this repo
 github.com/versity/versitygw     patches/<tag>/*.patch
        │                                │
        ▼                                ▼
   ci/clone-source.sh  ───►  ci/apply-patches.sh  ───►  ci/build.sh  ───►  ci/publish-release.sh
   (shallow clone tag)      (git am --3way)            (goreleaser)        (Gitea release assets)

ci/support-matrix.yaml records which upstream versions have a curated patch set and maps each to its patch directory.

Layout

s3-gateway/
├── ci/
│   ├── support-matrix.yaml   # upstream tag -> patch directory (registry)
│   ├── clone-source.sh       # shallow-clone upstream versitygw at a tag
│   ├── apply-patches.sh      # git am --3way patches/<tag>/*.patch
│   ├── build.sh              # goreleaser release (binaries + deb/rpm)
│   ├── publish-release.sh    # upload dist/* to a Gitea release
│   └── run.sh                # local end-to-end: clone + patch + build one tag
├── patches/
│   ├── v1.11.0/              # the supported series
│   │   ├── 0001-webui-debrand.patch
│   │   ├── 0002-webui-dark-theme.patch
│   │   └── 0003-owner-scoped-access.patch
│   └── archive/              # superseded; kept, not built
│       ├── v1.10.0/
│       ├── v1.8.0/
│       ├── v1.7.0/
│       ├── v1.6.0/
│       └── v1.5.0/
├── tests/owner-scope/        # leil-rig proof test for 0003 (proof.sh, s3req.py)
├── .gitea/workflows/build.yml
└── docs/
    ├── multi-version-pipeline.md
    └── owner-scoped-access.md  # what 0003 does, and its proof test

Build locally

Prerequisites: git, go, and goreleaser.

# Build one upstream tag end-to-end → artifacts in src/dist/
VGW_TAG=v1.11.0 bash ci/run.sh

# Fast offline build against a local upstream clone:
VGW_REPO_URL="/path/to/versitygw" VGW_TAG=v1.11.0 bash ci/run.sh

Or run the steps individually:

VGW_TAG=v1.11.0 bash ci/clone-source.sh   # → src/
VGW_TAG=v1.11.0 bash ci/apply-patches.sh  # git am the series onto src/
VGW_TAG=v1.11.0 bash ci/build.sh          # goreleaser → src/dist/

src/ and src/dist/ are gitignored.

Patches

Patch sets live in patches/<dir>/ as git format-patch files and are applied with git am --3way, in filename order. The directory for a tag comes from the patches: field in ci/support-matrix.yaml (defaults to the tag name).

That matrix is an allowlist: ci/apply-patches.sh refuses any tag it does not list, so a stray tag push or a mistyped dispatch can't publish a build of a superseded release.

Currently maintained:

Upstream tag Patch set Contents
v1.11.0 v1.11.0/ 0001-webui-debrand.patch, 0002-webui-dark-theme.patch, 0003-owner-scoped-access.patch

v1.11.0 is the only supported version. It supersedes v1.10.0 with 42 further upstream fixes, so the older series — v1.10.0, v1.8.0, v1.7.0, v1.6.0 and v1.5.0 — are no longer built. Their patches are kept under patches/archive/ for reference and for a deliberate rebuild; see that directory's README.

Note that protection against CVE-2026-111943 (the Go crypto/x509 eMudhra distrust, announced in upstream's v1.10.0 release notes) comes from the Go toolchain these binaries are built with, not from the upstream tag. Build with Go 1.26.10 / 1.27.3 or later.

What the de-brand patch does

  • Sidebar/login logos → a plain "S3 Gateway" wordmark.
  • Page titles: "VersityGW Dashboard" → "Dashboard" (Buckets, Users, Explorer and the v1.8.0 IAM pages too).
  • Browser tab titles → "S3 Gateway – …".
  • Removes the sidebar Resources section (external github.com/versity links).
  • Replaces the Versity favicon with a neutral SVG; deletes the Versity logo PNGs.
  • Removes the visible "© Versity Software Inc." footer from the login page.

Deliberately kept (license compliance): the per-file Apache-2.0 license headers — which carry the upstream copyright notice as an HTML comment at the top of each file — and the NOTICE file. Upstream is Apache-2.0; the de-brand strips the visible Versity trademarks (logos, wordmarks, and the login footer) while preserving the copyright notice in the source headers.

What the dark-theme patch does

0002-webui-dark-theme.patch re-skins the admin UI to match the kanrisha web app — a near-black palette with indigo/blue accents — without touching the page markup. It works entirely through the two shared asset files plus bundled fonts:

  • tailwind-config.js — remaps the named color tokens (surface, charcoal, primary, accent) onto the dark palette and switches the sans/mono fonts to Inter / JetBrains Mono.
  • theme.css — sets the dark body, restyles the bespoke components (sidebar active state, dropdowns, toggles, checkboxes, file rows, login) in indigo, and adds an override layer that flips the built-in Tailwind white/gray/status utilities the markup uses.
  • fonts.css + assets/fonts/*.woff2 — self-hosts Inter and JetBrains Mono (variable, latin + latin-ext) so the UI stays fully offline, matching the existing self-hosted Roboto.

The only markup change is the login page's light background gradient, which becomes a solid dark surface. It is part of the supported v1.11.0 series, and of every archived set except v1.5.0.

What the owner-scoped access patch does

0003-owner-scoped-access.patch changes authorization in auth/ (plus one call in s3api/controllers/bucket-put.go). A bucket's owner is the account its ACL names; the owner's keys are every account with that account's UserID.

  • Only the owner's keys. PutBucketPolicy, PutBucketAcl (body and grant headers) and CreateBucket's grant headers — so also versitygw admin create-bucket --grant-* — refuse any account whose UserID is not the owner's. Root is refused too.
  • Public means read. all-users gets READ only; a policy may allow "*" only s3:GetObject and s3:ListBucket. public-read-write and every other public grant are refused.
  • A policy adds to the ACL. An Allow from either is enough and a policy Deny wins — except that the owner's key is never denied.
  • Root/admin only: DeleteBucket, PutBucketVersioning, PutObjectLockConfiguration, PutBucketOwnershipControls (and DeleteBucketOwnershipControls).
  • An ACL holding the all-users READ grant can be written back unchanged (upstream answers 500).

It is the series' only patch outside webui/, and it has unit tests: after a rebase run go test ./auth/ ./s3api/... (on Linux — on Windows upstream's standalone-IAM, posix and CLI tests fail with or without the series) and the leil-rig proof test in docs/owner-scoped-access.md.

Adding / refreshing a version

When upstream cuts a new release you want a build of, you rebase the patch series onto it and add a patch directory. See docs/multi-version-pipeline.md for the full recipe. In short:

# Rebase the series onto the new tag, re-export into patches/vX.Y.Z/
git clone https://github.com/versity/versitygw && cd versitygw
git -c core.autocrlf=false checkout -b debrand vX.Y.Z
git am --3way /path/to/s3-gateway/patches/<prev>/*.patch   # resolve any conflicts
git format-patch --binary vX.Y.Z..HEAD -o /path/to/s3-gateway/patches/vX.Y.Z/

Then add the tag to ci/support-matrix.yaml, commit, and build it (see below).

CI

.gitea/workflows/build.yml builds one version per run and publishes it to a Gitea release in this repo. It triggers two ways — no scheduled/cron builds:

  • By new tag — push a vX.Y.Z tag to this repo; CI builds upstream versitygw vX.Y.Z with patches/vX.Y.Z/.
  • One-shot — run the build workflow manually (workflow_dispatch) and enter the upstream tag to build.

Either way the tag must appear in ci/support-matrix.yaml; if it does not, the patch step fails the run rather than publishing an un-de-branded or superseded build.

Configuration:

  • Runner: any Linux runner (runs-on: ubuntu-latest). The Go toolchain comes from the golang container, so the runner only needs Docker.
  • Secret: TOKEN_GITEA — a Gitea PAT with write:repository (clones this repo + creates releases / uploads assets).
  • Variable (optional): VGW_REPO_URL to override the upstream URL.

License

Apache-2.0, matching versitygw upstream. Upstream copyright and NOTICE are retained in every build.