KEYROLD: Putting an Expiration Date on SSH Keys

Why I Started Building KEYROLD
Modern platforms like Talos show that operating without SSH can be clean, secure, and API-driven. Access becomes a controlled API interaction, often based on strong identity and X.509-style expiry.
In the real world, many environments are not there yet.
Classic Linux fleets still rely on SSH for:
- break-glass access
- legacy workloads
- mixed estates (Debian, Ubuntu, RHEL, edge nodes)
- "just log in and fix it" operational paths
That is fine, but it leaves one recurring problem:
SSH is only as safe as the keys you forgot to remove.
KEYROLD exists to make "forgotten keys" a hard problem to create.
The Gap: authorized_keys Has No Expiration
SSH public keys are strong cryptography, but the trust model is blunt:
- keys are copied to the target system
- they remain valid until someone removes them
- there is no native expiry, renewal workflow, or lifecycle visibility
At scale, this creates a quiet risk:
- contractor keys stay behind
- laptop loss incidents become harder to reason about
- drift accumulates across many nodes
- audits turn into archaeology
The Idea Behind KEYROLD
KEYROLD adds a small control layer on the target system, without replacing OpenSSH.
Core characteristics:
- Key expiry as a first-class concept
- A small local data model (SQLite) to track keys, owners, and validity windows
- Automatic deactivation when a key expires
- Optional remote control to revoke or extend keys across fleets
- Minimal operational footprint, packaged as a
.deb
KEYROLD is intentionally scoped. It is not a full PAM suite and not a bastion product. It is "lifecycle glue" for SSH keys.
How It Works (High Level)
KEYROLD runs as a system service and maintains a small SQLite database.
Storage Model (SQLite)
The database tracks:
- key fingerprint
- principal or user mapping
- validity window (not_before, not_after)
- status (active, disabled, revoked)
- origin metadata (who issued it, ticket reference, optional reason)
Enforcement
KEYROLD can enforce expiry in two practical ways:
- Managed
authorized_keysfragments
- KEYROLD writes and maintains a managed section or dedicated include file
- expired keys are removed or commented out deterministically
AuthorizedKeysCommandmode
- OpenSSH asks KEYROLD for allowed keys at login time
- KEYROLD returns only keys that are valid "now"
- the canonical source of truth becomes the database, not a flat file
Both approaches keep OpenSSH as-is and avoid patching SSH itself.
Remote Control (Optional)
KEYROLD supports a small API surface for controlled fleet operations:
- revoke a key fingerprint immediately
- extend validity for a key
- list keys nearing expiry
- export audit events
This can be used in two modes:
- Standalone mode: local-only, no central server required
- Control plane mode: a central component provides workflow, approvals, and visibility
In control plane mode, nodes can also phone home:
- "these keys will expire soon"
- "a key is expired and was disabled"
- "a human approved an extension"
This supports your "ask for extension or block" workflow without forcing a dedicated heavy platform.
Design Principles
Drop-in for OpenSSH
KEYROLD should work on existing Linux hosts without invasive changes.
Explicit Lifecycle State
Validity windows, revocations, and renewals should be data, not tribal knowledge.
Secure by Default
If remote control is enabled, it must be authenticated and auditable. No unauthenticated "revoke endpoints".
Small and Operable
SQLite, systemd, a few config files. No mandatory Kafka, no mandatory Kubernetes.
Open Source First
The goal is a transparent security toolchain that can be reviewed, forked, and integrated.
Packaging and Deployment
KEYROLD will be shipped as a Debian package for:
- Ubuntu (ubuntu-latest)
- Debian 11, 12, 13
- architectures: amd64 and arm64
Filesystem Layout
/usr/sbin/keyroldd(daemon)/usr/bin/keyroldctl(CLI)/etc/keyrold/config.yaml(config)/var/lib/keyrold/keyrold.db(SQLite database)/var/log/keyrold/(optional log directory, depending on journald setup)
systemd Units
keyrold.service- optional
keyrold-rotate.service+ timer if file-based enforcement is used - optional healthcheck integration for monitoring
Usage Example
# /etc/keyrold/config.yaml
mode: authorized_keys_command
database: /var/lib/keyrold/keyrold.db
enforcement:
max_validity_days: 90
warning_days: 7
remote_control:
enabled: true
endpoint: https://keyrold-control.internal
auth_method: mtls
# Add a key with 30-day validity
keyroldctl add-key \
--user alice \
--key "ssh-rsa AAAAB3..." \
--valid-for 30d \
--reason "TICKET-1234"
# List all keys
keyroldctl list-keys
# Extend a key by 7 days
keyroldctl extend \
--fingerprint SHA256:... \
--extend-by 7d \
--reason "Project extension"
# Revoke immediately
keyroldctl revoke \
--fingerprint SHA256:... \
--reason "Device lost"
How KEYROLD Relates to "SSH-Free" Platforms
Talos-like designs are a strong direction, especially for Kubernetes nodes that can be treated as appliances.
KEYROLD does not compete with that model. It supports the reality that not all systems can move there quickly, and some never will.
KEYROLD aims to make classic SSH safer by default, with:
- time-bounded keys
- visible lifecycle state
- controlled revocation and renewal
Project Status
KEYROLD is currently a concept and early prototype direction. The initial public milestone is an MVP that can:
- import keys and assign validity windows
- enforce expiry reliably (file mode or AuthorizedKeysCommand mode)
- produce auditable events
- ship as
.debfor amd64 and arm64
What Comes Next
- MVP implementation and public repository
- Debian packaging and GitHub Actions build pipeline
- Minimal web UI for approvals and renewal workflow (optional component)
- Integration hooks for existing security stacks (for example SIEM, Wazuh-like dashboards)
If you're interested in this project or have thoughts on the approach, feel free to contact me.