Browser-based SSH key management without shared keys
Browser-based SSH key management with BastionSSH: per-person keys, roles, audit logs and rotation, plus the SSH hygiene habits we follow.
By FutureGen Systems
Ask a small team how they reach their servers and the answer is usually a mix: a shared deploy.pem passed around on chat, an ~/.ssh/config on one laptop that nobody else has, a crontab on a box nobody remembers setting up, and no record of who ran what. We hit the same mess running servers for our own products and for clients, so we built BastionSSH: browser-based SSH key management, server inventory and terminal sessions in one self-hosted, open-source tool.
This post covers what BastionSSH does, and, more usefully, the SSH hygiene habits we'd recommend whatever tool you use.
Why browser-based SSH key management
SSH itself is fine. The problems sit around it:
- Keys live on laptops. When a laptop is lost or someone leaves, you have to work out which servers trusted that key.
- Keys get shared. One key for "the team" means you can't revoke one person's access without breaking everyone's.
- Inventory lives in people's heads. Which IP is staging? Which user do we log in as? Is that box still running?
- There's no audit trail. Shell history on each server is partial, editable and spread across machines.
Moving access into a central, self-hosted web application fixes most of this structurally. Keys are stored in one place, encrypted at rest. People sign in with their own account and role. Every connection and command is recorded in one log.
What BastionSSH does
BastionSSH runs entirely in the browser over WebSocket and xterm.js. There is no desktop app and no local terminal required. You add SSH keys and servers once, then open a full interactive terminal session to any server from the web UI.
The main pieces:
- SSH key management: generate, import, organise and rotate multiple keys. All keys are encrypted at rest, and because it's self-hosted, your data never leaves your infrastructure.
- Server inventory and terminal: real interactive SSH sessions in the browser, with your servers organised in one list.
- Roles: Owner, Admin, Operator and Viewer, so not everyone who can see a server can change its configuration.
- Shared libraries: servers, keys and commands shared across the team, with invites by email or link.
- Audit log: every connection, command run and configuration change is recorded.
- Saved commands: named, parameterised commands attached to a server for one-click runs, with run history and category grouping.
- App-level cron jobs: scheduled commands that run over SSH from the application rather than from each server's crontab.
- Bring your own AI: connect OpenAI, Anthropic Claude, or a local model via Ollama, LM Studio or any OpenAI-compatible endpoint to suggest commands from plain English, explain terminal output and help diagnose errors.
It ships via Docker in one command, supports PostgreSQL or SQLite, and includes OAuth/SSO and TOTP two-factor authentication. There's no telemetry and no cloud lock-in. It is MIT-licensed and listed with our other open-source projects.
Why app-level cron matters
Server crontabs are invisible. A job fails silently, or a box is rebuilt and the job quietly disappears. BastionSSH schedules commands from the application itself, over SSH, so nothing is installed on your servers. You get one place to see every scheduled job, its run history, and failure alerts. For small fleets that is often all the "job scheduler" you need.
A note on the AI features
The AI assistant is optional and uses whichever provider you configure. If you're working on servers holding sensitive data, point it at a local model through Ollama or LM Studio so terminal output never goes to a third party. And treat suggestions as suggestions: read a generated command before you run it, especially anything with rm, chmod -R or a pipe into sh.
SSH hygiene we'd recommend anyway
A good tool helps, but most SSH incidents come from habits. These apply whether you use BastionSSH, a commercial bastion, or plain OpenSSH.
1. One key per person, never shared
Every person gets their own key, tied to their own identity. Shared keys make revocation impossible without collateral damage, and make audit logs meaningless because every entry says "deploy".
The same applies to automation: give each CI pipeline or scheduled job its own key, scoped to what it needs.
2. Use modern key types and protect them
Generate Ed25519 keys unless something old forces RSA:
ssh-keygen -t ed25519 -C "name@company-laptop-2026" -f ~/.ssh/id_ed25519_work
Put a passphrase on personal keys and use an agent so you aren't typing it constantly. A descriptive comment (who, which device, when) makes it far easier to spot stale keys later.
3. Rotate keys on a schedule and on events
Rotate on two triggers:
- On events: someone leaves, a laptop is lost, a key may have been exposed in a repo or chat. Rotate the same day.
- On a schedule: pick a period you'll actually keep to, and rotate everything on it.
Rotation means: create the new key, add it to authorized_keys, confirm you can log in with it, then remove the old one. Doing it in that order avoids locking yourself out. A central tool makes this practical, because you can see every server that trusts a key before you remove it.
4. Lock down the server side
In /etc/ssh/sshd_config, the basics we set on almost every server:
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
Log in as a named user and escalate with sudo, which leaves its own log. Reload sshd from an existing session and test a fresh login before you close it.
5. Keep an audit trail you trust
You should be able to answer "who connected to this server last Tuesday, and what did they run?" Shell history on the box isn't enough, since it's per-user and easily changed. Centralised logging of sessions, either through a tool like BastionSSH or by shipping logs off the server, is what makes incident review possible.
6. Review access regularly
Every so often, list each server's authorized_keys and match every entry to a current person or system. Anything you can't explain gets removed.
Self-hosting BastionSSH sensibly
Because BastionSSH holds the keys to your servers, treat its own host as your most sensitive machine:
- run it behind HTTPS, and restrict who can reach it (VPN, IP allow-list, or SSO in front);
- turn on TOTP 2FA for every account;
- back up its database, and the encryption key, separately and securely;
- keep it updated, like any internet-facing service.
Try it
BastionSSH is free and open source. The source and Docker instructions are linked from the project page. If you'd like help tightening SSH access, setting up a bastion, or generally getting a small server fleet under control, that is part of our server and sysadmin work, and you're welcome to get in touch.