CYGNUS TECHNOLOGIES · BASTION

LEAST-PRIVILEGE
BY CONSTRUCTION.

Bastion is how Cygnus engineers request cloud access — a guided wizard replaces tickets and hand-written policy with generated Terraform, risk-routed approval, and a signed audit trail, on one kernel across

  • AWS IAM
  • Kubernetes RBAC
  • GCP IAM
  • Azure RBAC
NO TICKETS·NO HAND-WRITTEN IAM·NO UNREVIEWED ACCESS
4
PROVIDERS · ONE KERNEL
0
KERNEL CHANGES FOR PROVIDERS 3 & 4
32
CONFORMANCE CHECKS · 8 INVARIANTS × 4
534
TESTS · 404 UNIT + 96 ROUTE + 34 AUDITOR
72
GOLDEN BYTE-PARITY CASES
0
STANDING CLOUD KEYS · OIDC ONLY
THE PROBLEM

Access runs on tickets, and tickets forget.

01

A human reads a ticket and hand-crafts IAM.

Broad is faster than precise, so roles ship over-privileged.

02

Nobody records who approved what, or why.

“Is this least-privilege?” has no answer, so review degenerates into vibes.

03

Declared access drifts from live access.

The policy in the repo stops describing the role in the account.

04

When merge means deploy, GitLab ACLs become the approval system.

Nobody chose that — and a replayed webhook can deploy what nobody approved.

05

Roles outlive their reasons.

Nothing expires, nothing is recertified, nothing is ever decommissioned.

06

Machines now request access at machine speed.

Nothing reasons about aggregate entitlement or grant velocity.

The same story repeats across AWS IAM, Kubernetes RBAC, GCP IAM, and Azure RBAC.

ACCESS-4127 · closed 2019 · the old way

approved_by:
unknown
approval_reason:
(none recorded)
policy_source:
JSON pasted in a comment
least_privilege:
unverifiable
declared_vs_live:
drifted
expires:
never
audit_trail:
ticket closed
OVER-PRIVILEGEDUNRECORDEDDRIFTEDMERGE = APPROVALREPLAYABLENEVER EXPIRESMACHINE-SPEED
CAPABILITIES

The controls are structural.

None of this is process or policy-doc. Each control is enforced by code, checked in CI, and backstopped in the cloud itself.

One kernel, four providers

AWS IAM, Kubernetes RBAC, GCP IAM, and Azure RBAC run on the same decision plane — the provider is a bundle, not a fork. Providers three and four shipped with zero kernel changes. A conformance kit holds every provider to 8 invariants — 32 mechanical checks — and 72 golden cases pin AWS output to the byte. 24 AWS permission archetypes and trust templates, mirrored in the k8s, GCP, and Azure families, do the tedium.

  • AWS IAM
  • K8S RBAC
  • GCP IAM
  • Azure RBAC
QUARANTINED, NOT DEPLOYED

The portal is the only approval authority

Bastion merges its own MRs after approval. An out-of-band GitLab merge is quarantined and never deployed. Merge rights are not approval rights.

34-TEST AUDITOR

CI re-validates every grant, independently

The pipeline re-renders each grant from its manifest and byte-diffs the result for tamper. It imports no portal code. 34 tests keep the auditor honest.

Even Admin is not root

A mandatory permissions boundary plus an org-level backstop SCP keep IAM writes away from everyone except the deploy identity.

OIDC · 3600 S

Deploys are keyless

CI assumes roles through OIDC with 3600-second credentials. There are no static cloud keys to leak. Every apply lands in a sandbox account before the target.

P-256 + WORM

The audit trail defends itself

Every governance event is hash-chained, ECDSA P-256 signed, and anchored to S3 Object Lock WORM storage. We say tamper-evident, not immutable — precision is part of the design.

TTL + CAPS

Machines get doors, not keys

Agents and CI request grants through MCP and machine APIs — TTL-required, human-sponsored, capped by budget and grant velocity.

EVIDENCE-BASED

Unused access stands down

Recertification is evidence-based, at a cadence scaled to risk. A provably-unused role walks a decommission ladder — request, soft-disable, grace, delete — never an in-use one.

THE ENGINE

From ask to evidence, in five verbs.

Every grant walks the same spine. Each step leaves an artifact; the artifacts are the audit.

01

REQUEST

Pre-flight runs before any side effect.

You describe what you need in a guided wizard. The policy preview renders as you type — type-aware intellisense, no Render button — and failures come back as fix guidance, not a stack trace.

PRE-FLIGHT · BEFORE ANY SIDE EFFECT
{  "code": "POLICY_ESCALATION",  "severity": "error",  "field": "permissionPolicyJson",  "step": 2,  "message": "Privilege escalation: grants all IAM actions (iam:*) -- the role can rewrite its own permissions.",  "remediation": "Remove the self-permission-granting actions (iam:Attach*/Put*Policy, iam:CreatePolicyVersion, …) or scope them tightly; if the role genuinely manages IAM, request a scoped exception from the platform team.",  "retryable": false}
02

GENERATE

Nothing is hand-written.

The provider bundle emits declarative Terraform — .tf.json, one file per role — plus a versioned manifest carrying risk_class and capabilities. The manifest, not the diff, is what gets reviewed and re-validated.

PROVIDERaws-iamARCHETYPEdynamodb-readwrite@v1ACCOUNT908515448388RISKelevated
GENERATED · NEVER HAND-WRITTEN
{  "module": {    "role_g-dynamodb-readwrite-scoped": {      "assume_role_policy_json": "{\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"}}],\"Version\":\"2012-10-17\"}",      "description": "Role for Golden Tester in technology",      "permission_boundary_arn": "${local.permission_boundary_arn}",      "permission_policy_json": "{\"Statement\":[{\"Action\":[\"dynamodb:ListTables\"],\"Effect\":\"Allow\",\"Resource\":\"*\"},{\"Action\":[\"dynamodb:DescribeTable\",\"dynamodb:GetItem\",\"dynamodb:BatchGetItem\",\"dynamodb:PutItem\",\"dynamodb:UpdateItem\",\"dynamodb:DeleteItem\",\"dynamodb:BatchWriteItem\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws:dynamodb:*:*:table/my-table\"}],\"Version\":\"2012-10-17\"}",      "role_name": "g-dynamodb-readwrite-scoped",      "source": "../../modules/iam_role",      "tags": {        "AppID": "golden-app",        "Archetype": "dynamodb-readwrite@v1",        "Department": "technology",        "ManagedBy": "cloud-access-portal",6 more lines
Rendered by the aws-iam provider bundle · pinned by 72 golden cases
03

APPROVE

Risk routes the review. The portal owns the merge.

Low risk may auto-approve — off by default, and only under a cloud-enforced hard ceiling; elevated goes to a manager; high adds security. Approvals are SSO-gated, with separation of duties and single-use signed links. The portal, not GitLab, merges the MR; an out-of-band merge is quarantined, never deployed.

LOWAUTOoff by default · hard ceiling enforced in-cloud
ELEVATEDMANAGER
HIGHMANAGER + SECURITY
GITLAB MERGE → deployQUARANTINED
04

DEPLOY

Sandbox first, then the target.

CI re-validates first — it re-renders the grant from the manifest and byte-diffs the output for tamper, importing no portal code. The role then applies to the sandbox account, verifies, and only then applies to the target, over keyless OIDC with 3600-second credentials. Ordered, idempotent callbacks walk the state machine; a replayed webhook is a no-op.

re-validateplanapply:sandboxverifyapply:target

MERGEDDEPLOY_QUEUEDAPPLYING_SANDBOXSANDBOX_VERIFIEDAPPLYING_TARGETPROVISIONED

05

PROVE

The grant carries its own evidence.

Every governance event lands in a hash-chained audit log, KMS-signed with ECDSA P-256 and anchored to WORM storage. Recertification runs on evidence at a cadence scaled to risk, and a provably-unused role is decommissioned down a safe ladder.

seq 41SECURITY_APPROVEDprev 9f2c…a41d
seq 42MERGEDprev 9f2c…a41dd08a…77e3
seq 43DEPLOY_TRIGGEREDprev d08a…77e35b1f…c9a0
ANCHOR_WRITTEN · alias/cap-audit-signing · s3://… (Object Lock)

Below all five beats: the boundary and the backstop SCP. 9 IAM mutation actions, denied to everyone but the deploy identity. Even Admin is not root.

MISSION

Enforced, not aspirational.

These are the platform's laws. Each one has a test, a gate, or a control behind it.

01

Build least privilege in.

A grant is born least-privilege, bounded, reviewed, and provable. Construction, not cleanup.

02

Fail closed.

Unknown routes to the strictest lane. Unset means denied. Every machine knob defaults off.

03

Keep policy in code.

Configuration decides what an org wants. The engine decides what is allowed.

04

Separate the duties.

You never approve your own request. Security is never the manager who already approved. One approval authority, always.

05

Overstate nothing.

The audit chain is tamper-evident, signed, and WORM-anchored. We say provable — never “immutable”. Claims match mechanisms.

06

Keep the responsibility human.

The tedium is reusable. The responsibility stays human.

CONTACT US FOR MORE INFO

See Bastion for your team.

Tell us a little about your team and what you're trying to govern. We'll follow up with a walkthrough and next steps — no obligation, no sales runaround.

Prefer email? Reach the platform team directly. info@cygnustechnologies.net

GET ACCESS

PROVE YOUR NEXT GRANT.

The safe path is the fast path.

Request your first grant in the wizard, watch it deploy sandbox-first, and keep the evidence for as long as the role lives.

Tell us about your team and we'll set you up — details templated, nothing to compose. The demo runs on sample data: no AWS, no sign-in.

NO TICKETS · NO HAND-WRITTEN IAM · NO UNREVIEWED ACCESS — NO SHARED SECRETS · NO STANDING KEYS · NO SILENT MERGES