fix(seaweedfs): manage S3 IAM as per-identity files over the filer gRPC API - #36
Merged
Merged
Conversation
duckhawk
force-pushed
the
fix/seaweedfs-credential-propagation
branch
from
July 27, 2026 16:44
a16b7ce to
23d0ef6
Compare
This was referenced Jul 27, 2026
duckhawk
force-pushed
the
fix/seaweedfs-credential-propagation
branch
from
July 28, 2026 15:28
063bdd8 to
a7c929e
Compare
…PC API
The Full profile could not hold S3 credentials. The e2e runs kept failing with "The
access key ID you provided does not exist in our records" for a key the credentials
Secret still advertised, gone from the backend with no pod restart to explain it —
and once that was addressed the store stopped reaching Ready at all, sitting on
"IAM config did not persist as written (found=false, 0 of 1 identities present)":
the driver's own write was invisible to the driver's own read.
Reproducing against a stock SeaweedFS 4.39 `weed server -filer -s3` unravelled the
chain, and it invalidates the shared-file design this driver had, not just its
transport:
- the S3 gateway loads IAM through filer.ReadInsideFiler, which returns only the
entry's inline Content and never reads file chunks. Of the filer's HTTP upload
paths, multipart POST (what the driver used) stores inline content but leaves
FileSize at 0 — the gateway sees the identities, but an HTTP GET serves an empty
body, so every read-modify-write started from "empty" and clobbered the file;
raw PUT round-trips over HTTP but stores chunks, which the gateway cannot see at
all, so authentication silently stays anonymous;
- on 4.39 the single /etc/iam/identity.json is legacy input, not state: the first
gateway to load it MIGRATES every identity into per-file form under
/etc/iam/identities/ and renames the original away. Managing that file races the
migration, and revocation through it is broken outright — the identity removed
from the legacy file has already been migrated, and its per-file copy keeps
working.
The driver now manages per-identity files the way the gateway's own credential
manager writes them: one <name>.json per identity under /etc/iam/identities/, over
the filer gRPC API, payload in the entry's inline content, snake_case JSON keys
matching the generated iam_pb.Identity tags the gateway decodes with. That removes
the shared-file hazard structurally — a reconcile can only ever touch the identity
it owns — so the fail-closed read-modify-write machinery, the admin-Secret bootstrap
annotation and the per-cluster identity lock are gone rather than patched again.
What remains guarded: strict decoding, so a non-identity payload is an error and
never an empty identity to overwrite; write verification by read-back, because an
accepted write is not proof the credential is readable; and revocation that cannot
claim success without confirming the file is gone.
Reaching the filer needed one more fix. The main ClusterIP Service — the one every
endpoint helper resolves to — published only s3 and the filer HTTP port, so gRPC
dials to 18888 were blackholed rather than refused and surfaced as "dial tcp
10.223.237.10:18888: i/o timeout" a dial deadline later. This predates the IAM work:
quota.go sets the per-bucket quota over the same target, so bucket quotas on a Full
store could never have worked either. The regression test derives the ports from the
endpoint helpers instead of restating the Service's list, so a helper that starts
dialing a new port fails until the Service publishes it.
Validated against stock 4.39: the live lifecycle test (write admin, write an app
identity, rotate, assert the neighbour is untouched, revoke, repeat the delete)
passes — kept in the tree behind LIVE_FILER_GRPC, skipped without it — and the
gateway enforces the result: unsigned request 403, issued key 200, revoked key
InvalidAccessKeyId. The e2e specs that caught this (full round-trip,
full-highredundancy filer failover) now pass end to end.
The in-process protocol fakes are gone in favour of driver-level behaviour tests
over an identityFiles stub: a protocol fake here validated our expectations of the
filer rather than the filer itself, and hid all of the above through several e2e
cycles.
Not addressed here: the same Full round-trip also failed once with "Unable to write
to one or more targets ... internal error" before any credential was lost, which
points at the volume servers rather than IAM and needs its own investigation.
Signed-off-by: v.oleynikov <vasily.oleynikov@flant.com>
duckhawk
force-pushed
the
fix/seaweedfs-credential-propagation
branch
from
July 29, 2026 02:44
a7c929e to
e1c9a79
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
The
Fullprofile (SeaweedFS) could not hold S3 credentials. Two e2e runs failed withmc: <ERROR> ... The access key ID you provided does not exist in our recordsfor a key the credentials Secret still advertised, with no pod restart to explain the loss. Fixing that exposed the next layer: the store then never reachedReadyat all, sitting onBackendReady=False msg="configuring S3 admin identity: IAM config did not persist as written (found=false, 0 of 1 identities present)"— the driver's own write was invisible to the driver's own read.Cause
Reproduced against a stock SeaweedFS 4.39
weed server -filer -s3, which unravelled the whole chain — and invalidated the shared-file design this driver had, not just its transport.1. The gateway and the filer's HTTP API disagree about where file content lives. The S3 gateway loads IAM through
filer.ReadInsideFiler, which returns only the entry's inlineContentand never reads file chunks. Of the filer's upload paths:FileSizeat 0 — the gateway sees the identities, but an HTTPGETserves an empty body. So the driver's read-modify-write always started from "empty" and clobbered the file;2. On 4.39 the single
/etc/iam/identity.jsonis legacy input, not state. The first gateway to load it migrates every identity into per-file form under/etc/iam/identities/and renames the original away. Any design that keeps read-modify-writing the legacy file races that migration — the verified write from this branch's first commits was deleted from under the verifier — and revocation through it is broken outright: the identity removed from the legacy file has already been migrated, and its per-file copy keeps working.3. The filer gRPC port was not reachable. With the rewrite in place the Full store stalled on
dial tcp 10.223.237.10:18888: i/o timeout. The filer pods listen on 18888 and the headless Service publishes it, but the main ClusterIP Service — the one every endpoint helper resolves to — declared onlys3and the filer HTTP port, so those dials were blackholed rather than refused. This one predates the IAM work:quota.gosets the per-bucket quota over the same target, so bucket quotas on a Full store could never have worked either.Fix
The driver now manages per-identity files the way the gateway's own credential manager writes them: one
<name>.jsonper identity under/etc/iam/identities/, over the filer gRPC API, payload in the entry's inline content,snake_caseJSON keys matching the generatediam_pb.Identitytags the gateway decodes with.That removes the shared-file hazard structurally — a reconcile can only ever touch the identity it owns — so the fail-closed
mutateIdentitiesmachinery, the admin-Secret bootstrap annotation and the per-cluster identity lock are gone rather than patched again.What remains guarded:
And the main Service now publishes the filer gRPC port, fixing both IAM and bucket quotas.
Validation
Against a stock SeaweedFS 4.39:
LIVE_FILER_GRPC, skipped without it;403, issued key200, revoked keyInvalidAccessKeyId.Tests
The in-process protocol fakes are gone in favour of driver-level behaviour tests over an
identityFilesstub: a protocol fake here validated our expectations of the filer rather than the filer itself, and hid all of the above through several e2e cycles. Covered: an access touching only its own identity file, reuse vs. rotation, deletion removing only its own file, and the on-disk JSON matching the gateway's schema.The Service regression test derives the ports from the endpoint helpers instead of restating the Service's list, so a helper that starts dialing a new port fails until the Service publishes it.
The
fullandfull-highredundancye2e specs are the end-to-end check.Not in scope
The same
fullround-trip also failed once withUnable to write to one or more targets ... internal errorbefore any credential was lost. That points at the volume servers rather than IAM and needs its own investigation.