SSH access control

Short-lived SSH access with 2FA: native shell, browser terminal, and audit trails

Permanent SSH keys are convenient until they become invisible standing access. CertLocker now brings the same short-lived, verified, auditable access model to native SSH clients, browser terminals, and automation paths that use SSH exec channels.

June 5, 2026 · 11 min read · SSH, 2FA, Zero Trust

Server access has two failure modes that show up in almost every growing infrastructure team. First, permanent SSH material spreads: PEM files on laptops, shared deploy keys, old entries in authorized_keys, and emergency credentials that quietly become normal. Second, access evidence is scattered: a VPN log here, a shell history there, a ticket comment somewhere else.

Short-lived SSH access fixes the first problem by making the credential expire. Two-factor authentication fixes the second-order problem: a token should not be enough if the token is attached to a real human. Audit trails fix the third problem by turning access into an event stream that security, platform, and compliance teams can review without reconstructing a session from fragments.

CertLocker now applies that model across three access paths: native shell access through a standard SSH client, the in-browser terminal in the CertLocker application, and automation that depends on SSH exec channels, including Ansible module execution.

Why short-lived SSH access beats permanent keys

Permanent SSH keys are standing access. They may be protected by a passphrase, a hardware key, an SSH agent, or a laptop login, but the access grant itself has no natural end. If the key stays authorized on a target host, the access stays available until someone removes it.

That is the operational weakness. Offboarding means finding every place the key exists. Contractor access means remembering to clean up after the contract ends. Incident response means asking whether the exposed key existed on one host, ten hosts, or every host built from the same image.

A short-lived SSH token changes the control point. The token is validated at connection time against CertLocker, scoped to the token's hosts or groups, and bounded by the token lifecycle. If the token expires, is revoked, is single-use, or fails policy checks such as host resolution or CIDR restrictions, the SSH connection does not get a usable route to the target.

standing key model:
  key exists -> host accepts it -> cleanup is server-by-server

short-lived token model:
  token presented -> CertLocker validates it -> route opens only for the approved target

This is also the direction zero trust guidance points infrastructure teams toward. NIST's SP 800-207 Zero Trust Architecture emphasizes per-session access decisions, least privilege, and reducing implicit trust. SSH is a natural place to apply that model because shell access is powerful, risky, and often too broadly granted.

Where 2FA fits into SSH

Two-factor authentication for SSH is not just a login-page concern. If a user-linked SSH token opens a shell on a production host, the SSH handshake itself should require the user to prove control of their second factor before the target session starts.

CertLocker does this through SSH keyboard-interactive authentication. The native SSH client connects to the CertLocker bastion listener, presents the token-style username, and receives a challenge. If the token is linked to a user, CertLocker checks that user's 2FA method and prompts for the right code.

  • Email 2FA prompts the operator for the verification code sent to their email address.
  • TOTP 2FA prompts for the authenticator app code.
  • Only after the code is accepted does CertLocker open the SSH route to the resolved host.

The important detail is that the second factor is not bolted onto a web dashboard while SSH remains a separate trust path. The SSH connection itself participates in the 2FA flow. NIST's current SP 800-63B authentication guidance treats authenticator assurance as part of the authentication ceremony, which is exactly where SSH access decisions need the extra proof.

Native shell access still feels like SSH

The access path remains familiar for operators. CertLocker exposes an SSH listener, commonly on port 2222. The username is the token plus, when needed, the target host name:

ssh -p 2222 -l '<token-id>@<host-name>' <bastion-host>

The -l form matters because the username can already contain an @ when the token is targeting a specific host. That keeps host routing explicit without inventing a custom CLI.

For repeat access, operators can use normal SSH config aliases:

Host dev03 dev-03
    HostName dev01.certlocker.io
    Port 2222
    User <dev-token>@dev-03
    PreferredAuthentications keyboard-interactive
    KbdInteractiveAuthentication yes
    PasswordAuthentication no
    PubkeyAuthentication no
    StrictHostKeyChecking accept-new

That turns the daily command into ssh dev03, while the CertLocker token, host selection, 2FA challenge, and audit path still happen underneath. The result is a better operator experience than forcing people into a special wrapper for every shell session.

This also lines up with standard OpenSSH behavior. OpenSSH documents client configuration and authentication preferences, including settings such as PreferredAuthentications. CertLocker uses those standard knobs so native clients can prefer keyboard-interactive instead of falling back to public-key behavior that is not part of the token flow.

The browser terminal uses the same access decision

The CertLocker browser terminal is not a weaker parallel path. It uses the same core idea: a token, a selected host, an authenticated web session, an SSH connection through the edge, and the same keyboard-interactive challenge before the shell becomes available.

In the product, a user can launch a terminal from an eligible token, pick a host when the token covers multiple hosts, and work in an xterm.js shell. The token detail view now makes the operational state visible before launch: host scope, linked gateway secret, bastion route, expiry, protocols, and the masked access code used by the terminal path.

If 2FA is required, the web terminal does not skip the SSH authentication ceremony. It opens the terminal modal, connects to the selected host route, and pauses at the keyboard-interactive challenge. In the email 2FA case, the code is sent to the linked member and the terminal remains in "verification required" state until the code is submitted.

This is especially useful for short access windows. If someone needs to inspect a service from a phone browser, handle a quick incident handoff, or pop a terminal into a separate browser tab, they do not need a separate SSH app or a copied PEM file. They still need a valid token, an authenticated CertLocker session, the token access code, and the 2FA response.

The browser path also keeps the product model consistent: tokens define access, hosts define targets, bastions define routes, and audit records define evidence.

SSH exec support makes automation possible

Interactive shells are only one half of SSH. Tools such as Ansible often need an SSH exec channel so they can run commands, transfer module payloads, execute Python, and return structured results. A bastion that only supports interactive shell sessions will work for a human typing commands but fail for automation with errors like exec request failed on channel 0.

CertLocker's bastion now supports exec in addition to interactive shell proxying. That means Ansible can target CertLocker token SSH endpoints instead of direct VM IPs, as long as the token transport group is configured for keyboard-interactive auth and does not inherit unrelated public-key or sudo defaults.

ansible_port: 2222
ansible_connection: ssh
ansible_become: false
ansible_ssh_args: >-
  -o ControlMaster=no
  -o ControlPersist=no
  -o PreferredAuthentications=keyboard-interactive
  -o KbdInteractiveAuthentication=yes
  -o PasswordAuthentication=no
  -o PubkeyAuthentication=no

That configuration is intentionally explicit. CertLocker token SSH behaves differently from a normal public-key deployment account, so the inventory group should say so. The operational win is that a connectivity check can be as simple as:

ansible certlocker_tokens -m ping

The access is still token-based. Route selection is resolved by CertLocker, not by Ansible guessing which inventory host maps to which backend. For non-production labs, that is a useful bridge between human JIT access and infrastructure automation experiments.

Audit trails are the difference between access and evidence

Short lifetimes reduce risk, but audit trails make access review practical. Security teams do not only need to know whether a token exists. They need to answer questions like:

  • Which user accessed this host?
  • Which token was used?
  • Was the target resolved by direct host name or group scope?
  • Was the token modified, revoked, or rotated before or after the session?
  • Which account, groups, and client context were attached to the event?

CertLocker's audit model centralizes those events rather than leaving teams to correlate SSH logs, VPN logs, ticket history, and local shell history. Token creation, modification, status changes, access validation, bastion state, and terminal flows all become part of the same evidence story.

That matters for compliance-heavy teams, but it also matters for normal engineering operations. During an incident, the useful question is not "who theoretically had a key?" It is "who actually connected, to what, when, under which token, and what changed around that time?"

What happens during a CertLocker SSH connection

The native SSH flow is compact:

  1. The SSH client connects to the CertLocker bastion listener and sends a token-style username.
  2. CertLocker parses the token and optional host suffix, then validates the token for the SSH protocol.
  3. The token service checks account status, token status, token protocol, host resolution, linked secret material, group access, CIDR policy, and single-use behavior where configured.
  4. If the token is linked to a user, CertLocker prompts for email or TOTP 2FA through keyboard-interactive authentication.
  5. After successful authentication, CertLocker opens either an interactive shell or an exec channel to the resolved target.
  6. The connection and related token activity are available through the audit plane.

The browser terminal follows the same access decision, but the keyboard-interactive challenge is bridged over the terminal WebSocket so the user can enter the code in the web UI before the shell stream becomes active.

Operational guardrails for teams adopting token SSH

The technical model is only useful if the operating model is disciplined. The best rollout pattern is conservative:

  • Start with non-production hosts. Prove token resolution, host naming, 2FA prompts, and audit visibility before changing production access paths.
  • Use short default durations. A token that lasts hours is easier to reason about than a key that lasts years.
  • Link human SSH tokens to users. That is what allows the 2FA challenge to represent the person, not just the token string.
  • Keep automation tokens separate. CI and Ansible experiments should use purpose-specific tokens with narrow host or group scope.
  • Prefer aliases over shared wrapper scripts. Native SSH config makes the workflow familiar while preserving the CertLocker authentication path.
  • Review audit records after every rollout step. Do not wait for an incident to learn whether the evidence is readable.

How this changes the SSH access conversation

The old SSH question was: "Who has a key on this server?" That question is still useful, but it is too static for modern infrastructure.

The better question is: "Who can request access, for how long, to which targets, with what second factor, and what evidence is produced when they connect?"

CertLocker's short-lived SSH access moves the answer into one product surface. Native SSH remains native. The browser terminal remains convenient. Ansible gets the exec support it needs. Security gets 2FA and audit trails. Operators get aliases that feel like normal SSH instead of a new ritual every time they need a shell.

That is the practical version of zero trust SSH: fewer permanent credentials, more per-session verification, narrower target scope, and access evidence that survives the moment.

Related reading and references

Ready to replace standing SSH access?

CertLocker gives infrastructure teams token-based SSH, 2FA challenges, scoped hosts, browser terminals, and audit evidence in one workflow.