#!/bin/bash
# secure ssh access for modern infrastructure

$ Replace static keys with
short-lived certificates

Valhalla SSH is a self-hosted SSH gateway that issues ephemeral certificates instead of static keys. JIT access requests, Slack approvals, full session recording, and structured audit logs — for every ssh connection.

~/.bastion — zsh
~$ bastion login
Authenticated as gnogueira (admin)
 
~$ bastion request prod-db-01 --reason "Investigate slow queries"
Request a1b2c3d4 created — pending approval
# Slack notification → #infra-approvals
 
~$ bastion ssh prod-db-01
Certificate issued ttl=60m principals=[gnogueira]
Session recording → recordings/2026-03-15/gnogueira_prod-db-01_7f8e9d0c.cast
Connected prod-db-01 (10.0.3.42) as ubuntu
ubuntu@prod-db-01:~$
features

Everything you need. Nothing you don't.

Production-grade SSH security, self-hosted, no vendor lock-in.

auth

SSH Certificates

Ed25519 CA signs certificates with ttl=5..1440m. Certs auto-expire. No more authorized_keys sprawl across servers.

access

Just-In-Time Access

Developers request access with a reason. Admins approve via Slack or bastion approve <id>. Access is time-boxed and audited.

recording

Session Recording

Server-side recording in asciinema v2 format. Stored in S3/MinIO. Replay any session with asciinema play. Tamper-proof.

rbac

Tag-Based Policies

Users → Groups → Policies → Host tags. Match by env=prod, role=db, team=platform. AND-logic, fine-grained.

audit

Structured Audit Log

Every event logged: logins, requests, approvals, cert issuance, sessions. Filter by --user, --type, --from/--to.

infra

Any Cloud, Any VPS

Works on AWS, OVH, Contabo, DigitalOcean — any host with sshd. Use S3 natively on AWS, MinIO everywhere else.

monitoring

Built-in Observability

Prometheus metrics + Grafana dashboards out of the box. Track active sessions, login rates, cert issuance, HTTP latency, and audit events.

security

Privilege Separation

JWT scopes separate user and admin operations. bastion login --admin for RBAC management. Reduced blast radius if token is compromised.

workflow

Request. Approve. Connect. Record.

Four steps from zero to secure session.

01

Request Access

Developer runs the CLI. API checks RBAC policies for matching group → policy → host tags.

$ bastion request prod-web-01 --reason "Deploy v2.4"
02

Approval

Slack bot sends interactive message. Admin clicks Approve/Reject. Or uses CLI: bastion approve <id>. 4-eyes principle enforced.

03

Certificate Issued

CA signs an ed25519 certificate with the remaining TTL. Certificate contains username as principal. Auto-expires.

04

Recorded Session

SSH proxy validates cert, opens session to target host, records all I/O server-side. Session uploaded to S3/MinIO on close.

$ bastion ssh prod-web-01
architecture

All-in-one. One compose file.

Simple deployment, no vendor dependencies.

  Developer
      │
      │  bastion cli
      │
      ▼
  ┌───────────────────────────────────────────────┐
  │  Caddy (reverse proxy, auto TLS)      :443    │
  └──────────────┬────────────────────────────────┘
                 │
       ┌─────────┴─────────┐
       ▼                   ▼
  ┌──────────┐       ┌──────────┐       ┌────────────┐
  │ API      │       │ Proxy    │       │ Slack      │
  │ Express  │       │ ssh2     │       │ Bot        │
  │ :3000    │       │ :2222    │       │ (optional  │
  │          │       │          │       │  approvals)│
  └──┬───┬───┘       └──┬───┬───┘       └────────────┘
     │   │              │   │
     ▼   ▼              │   ▼
  ┌────┐ ┌────┐         │  ┌──────────┐
  │PG  │ │Redis│        │  │ S3/MinIO │
  └────┘ └────┘         │  └──────────┘
                        ▼
     Prometheus   ┌──────────────┐
        +         │ Target HostsGrafana      │ :22          │
                  └──────────────┘
      
compare

Static keys are technical debt

Every key you deploy is a key you have to revoke someday.

  Static Keys Valhalla SSH
Key lifetime Permanent 5–1440 min TTL
Access model authorized_keys per host RBAC + tag policies
Approvals None Slack + CLI fallback
Recording None Server-side asciinema
Offboarding Remove from every host bastion admin user disable
Audit grep /var/log/auth.log Structured events + filters
Compliance Manual Audit trail + recording ready
Monitoring None Prometheus + Grafana built-in
use-cases

Built for teams that SSH into production

Startups

Stop sharing keys on Slack. Proper access control from day one.

  • Docker Compose — deploy in minutes
  • No per-seat pricing
  • Scales from 5 to 500 hosts

Platform Teams

Give devs production access without giving the keys.

  • JIT access with mandatory reasons
  • Tag-based policies per environment
  • Session replay for incident review

Security & Compliance

Meet audit requirements with zero manual effort.

  • Full audit trail of all SSH access
  • Tamper-proof server-side recording
  • Automatic certificate expiration
requirements

What you need to run it

Minimal resources. Maximum security.

bastion server

Where Valhalla runs

2 vCPUs4 GB RAM20 GB SSD

Docker + Compose. Ports 443 and 2222 open.
Ubuntu, Debian, Amazon Linux, RHEL — any Linux with Docker.

target hosts

Servers you SSH into

OpenSSH ≥ 6.2 • any Linux, macOS, or Windows with sshd

No agent installed. Just two lines in sshd_config:
TrustedUserCAKeys + AuthorizedPrincipalsFile

dev machine

Where devs work

Node.js ≥ 20pnpm ≥ 9 • SSH client

Install the bastion CLI. Works on macOS, Linux, WSL.
Ed25519 SSH key — ssh-keygen -t ed25519

$ ssh the right way

Self-hosted. Open source. Deploy in under 10 minutes.

~$ git clone && cp .env.example .env && pnpm docker:up