RustFS storage onboarding (self-hosted)¶
For operators connecting a RustFS object-storage instance to Cytario. RustFS is the S3-compatible provider for on-premises / self-hosted deployments. The work is split two ways. You prepare the instance once per organization: the OIDC trust, the per-organization access policies, and their groups (steps 1–3). Cytario then manages day-to-day access on its own: it holds no RustFS administrative credentials, never creates users, groups, or per-organization policies on the instance — but it does write the bucket policies that grant your users access, automatically, when buckets are registered and shared in the portal. The admin portal records the connection and its buckets (catalog-only registration).
Using the hosted offering with AWS storage? Storage on-boarding is a guided, one-click flow in the admin portal instead — see Storage management.
Prerequisites¶
- RustFS 1.0.0 (GA) or later with STS (web-identity federation) enabled.
- Cytario's Keycloak identity service (the same service your users sign in to), reachable from the RustFS instance.
- Admin access to the RustFS instance (
mcwith a configured alias, or the RustFS admin API) to create policies, groups, and users. - The buckets the organization will use must already exist on the instance — Cytario does not create buckets. CORS must be configured by you (see step 4).
1. Configure the OIDC trust¶
Point the RustFS instance at Cytario's Keycloak via the
RUSTFS_IDENTITY_OPENID_* configuration family:
RUSTFS_IDENTITY_OPENID_CONFIG_URL— the realm's.well-known/openid-configurationURL (for a Cytario-hosted deployment:https://auth.cytario.com/realms/cytario/.well-known/openid-configuration).RUSTFS_IDENTITY_OPENID_CLIENT_ID=rustfs— the dedicated RustFS client. Cytario presents tokens to your instance from several surfaces (the web app's credential mint, the admin portal's connection verify, the job broker's mints, the cytario CLI's workstation federation), each issued to its own client —rustfsis the one audience Cytario injects into all of them. Do not point RustFS at any other client: RustFS rejects a token whoseauddoes not contain the configured client id, regardless of any other setting.RUSTFS_IDENTITY_OPENID_OTHER_AUDIENCES— the client ids that appear in presented tokens alongsiderustfs. RustFS's audience check is two-part: the token'saudmust contain the configured client id, and every additional audience in the token must be listed here. Set it to exactly this list:
RUSTFS_IDENTITY_OPENID_OTHER_AUDIENCES="account,cytario-web,admin-portal,admin-portal-service-account,job-broker,cytario-cli"
RUSTFS_IDENTITY_OPENID_CLAIM_NAME=groups— the claim the credential mint resolves against IAM groups. It must be exactlygroups; Cytario's mapper emits the marker names into it on every client that presents tokens.
The groups claim carries two kinds of values: the reserved organization
marker cy-<alias>, and one composite entry per org-scoped group the
user belongs to — cy-<alias>_<path> (e.g. Lab/TeamX → cy-acme_Lab_TeamX)
— where <path> is the group's organization-relative path with every /
replaced by _. A literal _ in a group name becomes :_, and a literal
: becomes :: — so a group named Team_X under Lab is emitted as
cy-acme_Lab_Team:_X, and groups whose names contain _ or : are fully
supported, never dropped. There is no separate management marker: the organization's
root admins group is emitted as its ordinary composite cy-<alias>_admins,
and a sub-scope admin group (e.g. Lab/admins) as cy-<alias>_Lab_admins.
The composite binds the organization and the group into a single value: bucket
policies condition on it with a single-valued StringEquals, so a group path
alone (a path another organization could also hold) can never satisfy another
organization's policy. All of these values are legal RustFS IAM group names
(no /; the encoded path uses only _, :, and alphanumerics, all within
RustFS's legal claim-derived name charset), and every one of them can resolve
against an IAM group — so a
sub-scope admins group, once provisioned, carries the management policy
itself.
Note on aliases: an organization alias must not contain _ (nor / nor
whitespace) — _ is the composite separator, and an alias containing it could
make a value like cy-a_b ambiguous between two organizations (org a's
group b vs. org a_b's marker). Group names have no such restriction: any
group name is emitted, escaped or not. An organization whose alias violates
these charset rules
cannot use RustFS storage — the claim values are refused fail-closed and the
organization gets no credentials.
Notes:
- No client secret is required for this trust. The credential paths above
only need RustFS to verify Cytario-issued tokens, which RustFS does in
public-client mode. Administer your instance with your own root credentials
(console key login or
mc) — that path is unaffected by the OIDC configuration. Console single sign-on through Cytario's identity service is available on request; it additionally requires a client secret and registering your console callback URL on Cytario's side. - Client ids are a wire contract: the
rustfsclient id and theOTHER_AUDIENCESlist above must match what Cytario's identity service emits. If Cytario ever adds a presenting client, the onboarding documentation is updated with the new list — a token carrying an audience that is neither the configured client id nor listed inOTHER_AUDIENCESis rejected. - Cyberduck is not supported for RustFS connections yet — the Cyberduck
profile download is offered for AWS-backed connections only. (Adding
Cyberduck's client id to
OTHER_AUDIENCESdoes not enable it: its tokens do not carry therustfsaudience, so they fail the audience check regardless.)
2. Create the per-organization policies¶
Two IAM policies per organization, created in the RustFS Console (Identity → Policies → Create Policy). The policy name is the binding key — it must match the claim value the Cytario mapper emits, exactly as written below.
The admission policy — cy-<org-alias>¶
Attached to the organization-marker group that every member holds. Its only purpose is admission: Cytario's credential mint denies any session that resolves zero policies, so the marker group must carry one — but it grants no data access. Reading, writing, and sharing data all come from the bucket policies Cytario writes automatically when you register and share buckets (step 5) — never from this document.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AdmissionOnly",
"Effect": "Allow",
"Action": "s3:GetBucketLocation",
"Resource": ["arn:aws:s3:::<your-bucket>"]
}
]
}
Add each bucket's ARN to the Resource list as you register more buckets —
the mint must resolve a policy covering the bucket it mints against.
The management policy — cy-<org-alias>_admins¶
Attached to the organization's admins group. This is what lets Cytario read and write the bucket policies that grant your users access, and keep CORS current:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "BucketPolicyManagement",
"Effect": "Allow",
"Action": [
"s3:GetBucketPolicy",
"s3:PutBucketPolicy",
"s3:PutBucketCors",
"s3:GetBucketLocation"
],
"Resource": ["arn:aws:s3:::<your-bucket>"]
}
]
}
Extend the Resource list as you register more buckets, exactly as with the
admission policy.
Why
GetBucketLocationand not more? Every S3 client asks for the bucket's region on connect (it exposes the region string only — no keys, no data), so it is the smallest useful action to carry the admission. Deliberately absent: any data-plane action (that would bypass the portal's share management — the bucket policies Cytario writes are the single source of data access) ands3:GetBucketOwnershipControls(RustFS's policy action registry rejects it as unimplemented).
3. Create the claim-named groups and attach the policies¶
In the RustFS Console (Identity → Groups), create exactly two groups per organization and attach the matching policy from step 2:
| Group name | Policy | Who the claim value covers |
|---|---|---|
cy-<org-alias> |
cy-<org-alias> (admission) |
every organization member |
cy-<org-alias>_admins |
cy-<org-alias>_admins (management) |
the organization's root admins |
Cytario's credential mint resolves each token groups value against IAM
groups by exact name — the group name is the encoded claim value, copied
verbatim. RustFS requires each group to have at least one member; a
placeholder access-key user is fine (Identity → Users → Create User), since
the OIDC mapping resolves by group name, not by membership of specific
humans.
No other IAM groups or policies are needed. Ordinary organization groups
(e.g. Lab/TeamX) never get an IAM counterpart — the bucket policies
Cytario writes match them via the claim composite (e.g.
cy-acme_Lab_TeamX) directly in the policy condition, so there is nothing
to provision per group. The only exception: if a sub-scope has its own
admins (e.g. Lab/admins) who manage shares in the portal, create one
additional group named exactly as that scope's composite —
cy-<org-alias>_Lab_admins (every / in the scope path becomes _, a
literal _ becomes :_, a literal : becomes ::) — and attach the
management policy to it too.
If a group is renamed, deleted, or loses its attached policy, the organization's sessions resolve zero policies and are denied at the mint (fail-closed) — re-check these names first when access breaks.
4. Create the buckets and configure CORS¶
Create each bucket the organization will register (Buckets → Create Bucket in the RustFS Console), and apply the CORS configuration so the browser can read and write directly. In the Console's bucket settings, set the CORS rules to:
{
"CORSRules": [
{
"AllowedOrigins": [
"https://<your-cytario-web-host>",
"https://<your-cytario-app-host>"
],
"AllowedMethods": ["GET", "HEAD", "PUT", "DELETE", "POST"],
"AllowedHeaders": ["*"],
"ExposeHeaders": [
"ETag",
"Content-Range",
"Accept-Ranges",
"Content-Length"
],
"MaxAgeSeconds": 3000
}
]
}
If you provision without the Console, the S3 API equivalent works too
(RustFS honors the S3 PutBucketCors semantics):
aws --endpoint-url https://<rustfs-endpoint> s3api put-bucket-cors \
--bucket <bucket> --cors-configuration file://cors.json
5. Register the connection and buckets in the admin portal¶
In the admin portal (Infrastructure providers → Connect account), select the RustFS provider type and enter:
- an optional display name (defaults to
RustFS <endpoint host>), and - the RustFS endpoint URL (
https://…).
No AWS account id, region, or CloudFormation hand-off is involved.
Verify connection runs a test web-identity assume against the endpoint. A verify that resolves zero mapped policies fails with a message naming the operator prerequisite — usually a missing claim-named group or an unattached policy in step 3. A failed verify keeps the record with its cause; provision the missing piece and use Re-verify to heal it.
Then register the organization's buckets under the connection (Storage → Add bucket). Registration records the bucket and its access levels — the portal writes the bucket policies that grant your users access on top of your step-2 management policy, but it does not create buckets, users, or groups on the instance. What you provisioned in steps 2–4 is what Cytario's policies can grant.
6. How users connect¶
Organization members see the registered buckets in the main app's Add Connection flow — see Connect your storage. Nothing else is required on the RustFS side.
When you stop using a connected account, see Disconnecting storage / off-boarding.