andrewmusselman opened a new pull request, #922:
URL: https://github.com/apache/tooling-trusted-releases/pull/922
# Invalidate SSH keys when user account is disabled
Fixes #737
## Problem
When an LDAP account is banned or deleted, the user's SSH keys remain valid.
The SSH authentication flow in `atr/ssh.py` validates key existence and
expiration but does not check account status. Additionally, workflow SSH keys
(20-minute TTL) have no mechanism for immediate revocation before expiry.
## Solution
Three layers of defense:
1. **LDAP check during SSH authentication** (new) — `begin_auth()` rejects
banned/deleted users before loading keys; `validate_public_key()` rejects
banned/deleted users before accepting workflow keys
2. **`revoked` flag on workflow SSH keys** (new) — allows immediate
invalidation of in-flight workflow keys before their 20-minute TTL expires
3. **Admin revocation page** (new) — allows admins to delete all persistent
SSH keys and revoke all workflow keys for a user instantly
## Changes
### New files
- `atr/admin/templates/revoke-user-ssh-keys.html` — admin page template with
two tables (persistent keys, workflow keys)
- `migrations/versions/0062_2026.03.19_ef59ffaf.py` — Alembic migration
adding `revoked` column to `workflowsshkey`
- `tests/e2e/admin/test_revoke_ssh_keys.py` — 8 e2e tests (page rendering,
form validation, revocation flow, nav link)
### Modified files
- `atr/ssh.py` — adds LDAP account status check in `begin_auth()` and
`validate_public_key()`, checks `revoked` flag on workflow keys
- `atr/models/sql.py` — adds `revoked: bool` field to `WorkflowSSHKey`
- `atr/storage/writers/ssh.py` — adds `FoundationAdmin` class with
`revoke_all_user_keys()`
- `atr/storage/__init__.py` — wires `ssh` into `WriteAsFoundationAdmin`
- `atr/admin/__init__.py` — adds `RevokeUserSSHKeysForm`, GET/POST route
handlers
- `atr/templates/includes/topnav.html` — adds "Revoke user SSH keys" nav
link under Admin
- `atr/docs/authentication-security.md` — documents SSH key security
properties and implementation references
- `atr/docs/authorization-security.md` — documents SSH admin revocation
access control
- `tests/e2e/admin/helpers.py` — adds `REVOKE_SSH_KEYS_PATH` constant
- `tests/e2e/admin/conftest.py` — adds `page_revoke_ssh_keys` fixture
## Design decisions
- **Persistent keys deleted, workflow keys marked revoked**: Persistent keys
have no TTL — deleting them is clean. Workflow keys have a 20-minute TTL;
marking them `revoked=True` preserves an audit trail of what was in flight when
revocation happened.
- **LDAP failure = auth rejected**: If LDAP is unreachable during SSH auth,
authentication fails closed. SSH write access can modify releases, so
availability is less critical than preventing banned users from authenticating.
- **Separate page from PAT revocation**: Keeps each admin page focused on
one credential type. Same form pattern (REVOKE confirmation, PRG redirect,
audit logged).
- **Migration applies automatically**: `db.init_database()` runs `alembic
upgrade head` on server startup, so no manual migration step is needed.
## Testing
- `make e2e` — 103 passed, 9 errors (pre-existing failures in
`e2e/announce/test_get.py`, unrelated to this change)
- `make unit` — 356 passed, 13 skipped
---
## Rebase confirmation details (optional but encouraged)
```
$ git fetch upstream main
From github.com:apache/tooling-trusted-releases
* branch main -> FETCH_HEAD
$ git rebase upstream/main
Current branch ssh-invalidate-737 is up to date.
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]