Connecting storage (self-hosted) / on-boarding¶
For OSS self-hosted Cytario deployments that run without the admin
portal and connect AWS storage. The operator pre-provisions the OIDC
identity provider and the IAM data-plane roles out of band, then lists
them in a deploy-time config file. There is no Cytario Admin Role and no
runtime role creation in this variant — the automated iam:CreateRole /
permissions-boundary machinery of the hosted (SaaS/EE) admin portal does not
exist here.
Connecting RustFS (or another S3-compatible) storage instead? See RustFS storage onboarding — the authorization model is customer-managed claim-named groups, not IAM roles.
Using the hosted offering? Storage on-boarding is a guided, one-click flow in the admin portal instead — you do not perform any of the steps below.
Prerequisites¶
- An S3 bucket (or S3-compatible endpoint) holding the imagery.
- A Keycloak realm issuing tokens to cytario-web, reachable by AWS STS.
- AWS credentials able to create an IAM OIDC provider and IAM roles.
1. Provision the OIDC provider and data-plane roles¶
Deploy oss-provider-roles.yaml:
aws cloudformation deploy \
--template-file oss-provider-roles.yaml \
--stack-name cytario-oss-storage \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
OrgAlias=<your-org-alias> \
OidcProviderHost=auth.example.com/realms/cytario \
OidcThumbprint=<sha1-thumbprint-of-issuer-CA> \
CytarioWebClientId=<cytario-web-client-id> \
DataBucketArn=arn:aws:s3:::<your-bucket>
This creates:
- OIDC provider trusting the Keycloak issuer.
cytario-oss-readwrite— read/write, sharing-capable. Its trust policy conditions onaws:PrincipalTag/ORG == <org alias>; its inline policy grants S3 read/write pluss3:PutBucketPolicy(allORG-conditioned).cytario-oss-readonly— read-only, carrying an explicitDenyon all write actions includings3:PutBucketPolicy. The Deny holds even if the role is later added to a broader Allow.
Both roles are created under the reserved path /cytario/provider-roles/.
2. Author the bucket-policy baseline¶
The bucket policy (and, under SSE-KMS, the CMK key policy) must condition each
Allow (s3:ListBucket, s3:GetObject, kms:Decrypt) on
aws:PrincipalTag/ORG == <org alias>, plus the per-group tag for group-scoped
grants. This is the authoritative cross-tenant boundary for every client,
including any direct-federated (Cyberduck) client. cytario-web writes the
runtime grant delta under the read/write role; the baseline is your
responsibility. Managed statements use the Cytario Sid prefix so they are
mergeable and revocable while foreign statements are preserved.
3. Write provider-config.yaml¶
Copy provider-config.sample.yaml,
replace the role ARNs with the stack outputs (ReadWriteRoleArn,
ReadOnlyRoleArn), and validate against
provider-config.schema.json.
Mount it where cytario-web's OSS provider loader expects it. The system accepts
only what is declared here — it never accepts a caller-supplied role
identifier or endpoint.
A read-only entry MUST set allowsSharing: false and reference the read-only
role.
4. Confidential client + distinct Cyberduck client id¶
Even though the self-hosted variant has no admin-portal Admin Policy, the same
two preconditions protect the direct-federated path via the bucket policy and
the read-only Deny. Register cytario-web as a confidential OIDC client and
give Cyberduck a distinct client id (CYBERDUCK_CLIENT_ID != CLIENT_ID).
5. Verify¶
The pre-production acceptance harnesses live in the cytario-docs repository under
e2e/acceptance/. Both fail closed (non-zero exit) on any failure to prove
their assertion:
cd e2e/acceptance && npm install
# Cross-tenant denial (needs two organizations / two buckets configured):
ORG_A_WEB_IDENTITY_TOKEN=... ORG_A_ROLE_ARN=... ORG_B_BUCKET=... npm run cross-tenant
# Confidential client + distinct client id:
OIDC_ISSUER=... CLIENT_ID=... CYBERDUCK_CLIENT_ID=... CLIENT_SECRET=... npm run azp-floor
For health verification, confirm cytario-web can list and read the configured
bucket under cytario-oss-readwrite and cannot write under
cytario-oss-readonly.
When you stop using a connected account, remember to clean it up — see Disconnecting storage / off-boarding.