Appearance
The Auth Model
Keel uses IAM role assumption with short-lived STS credentials, guarded by MFA. This page explains what keel auth setup builds, what lives on disk, and precisely what a stolen credential can and cannot do.
Access levels
Keel enforces three access levels via IAM roles with least-privilege policies:
| Level | IAM Role | Permissions |
|---|---|---|
| Admin | keel-admin | Provision, destroy, manage team, require MFA. Not IAM bootstrap — see below |
| Developer | keel-developer | Deploy, exec, scale, config vars, logs, CodeBuild |
| Viewer | keel-viewer | Read-only: status, logs, dashboard, apps list |
Commands check access levels at the start of execution: keel up and keel destroy require Admin, keel deploy requires Developer, keel logs works for all roles including Viewer.
One-time setup
An account admin runs keel auth setup once. It is a guided six-step flow, not a single action:
- Take AWS credentials that can create IAM users, roles, and policies — held in memory for the run and never written to disk
- Create the three roles (
keel-admin,keel-developer,keel-viewer) with least-privilege policies, the per-role assume policies, and the team table - Create an IAM user for you under
/keel/that can assumekeel-adminand nothing else - Enrol an MFA device for it — scan a QR code, or type the printed secret
- Verify the new identity can assume
keel-admin, then switch Keel over to it - Require MFA to assume any Keel role
Steps 3–5 are the part worth understanding: the credentials you start with can administer your whole account; the ones Keel keeps afterwards can do exactly one thing, and only with a code from your phone. Setup does this itself rather than leaving it as a follow-up command, because a follow-up command is one that gets skipped — and skipping it would leave you running Keel on unrestricted credentials forever.
Keep your original AWS credentials safe
They are the way back in if you lose your MFA device — the first admin has nobody else who can reset it for them.
bash
keel auth setup # admin, one time; needs a terminal for the MFA step
keel auth setup --dry-run # preview every policy document without touching AWS
keel auth login # start a session — prompts for a one-time code
keel auth whoami # check identity and access levelRe-run keel auth setup after upgrading Keel. It publishes new versions of the existing policies rather than recreating them, keeps whatever MFA enforcement is already in place, and resumes rather than restarting — an enrolled device is kept. It needs the elevated credentials again, because keel-admin deliberately cannot rewrite Keel's own policies.
Credential handling
Keel stores two credentials per app+environment in ~/.keel/contexts/<app>/:
| File | What it is | Lifetime |
|---|---|---|
base_credentials | A long-lived IAM user access key, stored in plaintext at 0600 | Until rotated or the user is revoked |
credentials | An STS session from assuming a Keel role | 8 hours |
The base key is deliberately close to useless on its own. The only actions its policy grants are sts:AssumeRole on exactly one keel-* role and reading its own MFA device — and once MFA is enforced, that assume also needs a one-time code. A copy of the file, wherever it ends up, cannot reach your infrastructure.
- Sessions are not refreshed automatically. When one expires, run
keel auth loginagain; every command that needs credentials says so if they have lapsed - The MFA secret is generated on the member's machine, shown once, and never transmitted, stored, or written to Keel's debug log
keel auth logoutclears the session;--purgealso removes the base key- OpenTofu receives credentials via environment variables, never written to disk
- Application containers receive rotating ECS task-role credentials through the AWS SDK credential chain, never an IAM user key
- Every service has its own runtime IAM role and security group
How access is enforced
| Layer | Role |
|---|---|
| IAM assume policy on the member's user | The enforcement point — grants exactly one keel-* role |
| MFA condition on the role's trust policy | Makes the access key insufficient on its own |
keel-mfa-enroll-policy | Held only between team add and team activate, never alongside an assume policy |
keel-team DynamoDB table | Roster and audit trail (who added whom, when, current status) |
| Cached STS credentials | What Keel's access check inspects on every privileged command |
Because the assume policy allows a single role, a member cannot escalate by editing their local config or passing a different --role. Admins can only ever attach one of the three Keel assume policies, or the MFA enrollment policy, to a Keel-managed user — the keel-admin policy constrains iam:AttachUserPolicy with an iam:PolicyARN condition, so keel auth team cannot be used to grant arbitrary permissions.
keel-admin also cannot rewrite Keel's own IAM. It holds read-only access over role/keel-* and policy/keel-* — since policy/keel-* includes keel-admin-policy itself, write access there would let an admin session publish a new version of its own policy granting everything. Changing those documents is a bootstrap act, done by keel auth setup with elevated credentials. The one exception is iam:UpdateAssumeRolePolicy, which keel auth mfa enforce needs; it cannot grant permissions, only change who may assume.
What this does not protect against
MFA makes an exfiltrated key useless — a copy in a backup, a chat message, or terminal scrollback. It does not protect a machine that is already compromised: something running as you can wait for a legitimate login and take the session from credentials, which is plaintext on disk for the life of the session.
Being clear about the limit of the admin boundary too: keel-admin can still create the IAM roles your infrastructure uses, attach AWS-managed policies to them, and pass them to ECS tasks — so it can run code with permissions Keel never granted directly. That is inherent to a tool that deploys infrastructure. What is closed is the direct path from an admin session to account administrator.