# The Auth Model

Keel has two operator paths. IAM team members use MFA-protected role assumption and short-lived STS sessions. Federated [operators](./operators) hold no general-purpose AWS key: they sign in through Cognito and the control plane evaluates per-capability grants. The IAM path remains the bootstrap and break-glass path.

## 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.

These three levels govern IAM sessions. A federated operator is not promoted to one of them; their authorization grid independently grants `read`, `logs`, `exec`, `scale`, and the other capability kinds per scope.

## One-time setup

An account administrator runs `keel auth setup` once. The guided flow:

1. Accepts AWS credentials that can create IAM users, roles, and policies. Keel holds them in memory for the duration of setup.
2. Creates the three roles, their least-privilege policies and assume policies, and the team table.
3. Creates an IAM user under `/keel/` that can assume `keel-admin`.
4. Enrolls an MFA device for the new user.
5. Verifies the new identity and switches Keel to it.
6. Requires MFA for all Keel role assumptions.

The elevated credentials are used only for setup. Keel retains the restricted IAM user credentials, which require an MFA code to assume a Keel role.

**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 level
```

Re-run `keel auth setup` after upgrading Keel to publish new versions of its policies. The command preserves MFA enforcement and existing device enrollment. It requires elevated credentials because `keel-admin` cannot modify 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 can assume one `keel-*` role and read its own MFA device. Once MFA is enforced, role assumption also requires a one-time code, so the base key alone cannot access your infrastructure.

- Sessions do not refresh automatically. Run `keel auth login` again after a session expires.
- Keel generates the MFA secret on the member's machine, displays it once, and does not transmit it or write it to the debug log.
- `keel auth logout` clears the session; `--purge` also removes the base key.
- OpenTofu receives credentials through environment variables.
- Application containers use rotating ECS task-role credentials instead of IAM user keys.
- 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 |
| Operator IdP + roster binding | Verifies a Cognito subject and maps it to an opaque principal |
| Authorization grid | Allows or denies one operator capability and scope |
| Control-plane decision log | Records allowed and denied federated requests |

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.

The authorization grid governs federated operators only. It cannot narrow an IAM caller, who already holds direct AWS permissions; that is deliberate so a broken control plane cannot take away the repair path. See [The Keel Control Plane](./control-plane) for served reads and brokered credentials.

## 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.

`keel-admin` can create infrastructure roles, attach AWS-managed policies to them, and pass those roles to ECS tasks. Code deployed through Keel can therefore use permissions beyond those granted directly to the admin session. The model prevents direct escalation from a Keel admin session to account administrator; it does not remove the authority needed to deploy infrastructure.
