Open-source SSH proxy with identity-aware shell (Rust)
Observability: Structured JSON logging via BASCULE_LOG_FORMAT=json Tracing spans on auth (method, principal, peer) Tracing spans on session lifecycle (id, principal, backend, source_ip) Tracing spans on exec requests (session_id, command) Config: [telemetry] and [metrics] sections (OTel export planned) Documentation (8 files, 489 lines): docs/quickstart.md — three-path getting started docs/configuration.md — full config reference with examples docs/authentication.md — all auth modes with setup guides docs/architecture.md — backends, traits, extension model, security docs/observability.md — logging, tracing, metrics docs/comparison.md — vs Teleport, Boundary, StrongDM images/README.md — curated image catalog README.md — features, comparison, quickstart, extension example 1557 lines Rust, 489 lines docs, 0 substrate deps. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| config | ||
| crates | ||
| docs | ||
| images | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| README.md | ||
Bascule
Identity-aware SSH proxy for modern infrastructure.
Bascule is a lightweight SSH proxy that authenticates users via SSH keys, OIDC, or AI agent tokens, then connects them to a local shell, remote host, or ephemeral container. No agents. No control plane. One binary.
Quick Start
cargo build --release -p bascule-server
./target/release/bascule --config config/bascule.example.toml
# In another terminal:
ssh -p 2222 localhost
See docs/quickstart.md for Docker and container mode.
Session Modes
| Mode | Config | Use case |
|---|---|---|
| Local PTY | (default) | Spawn a local shell |
| Remote proxy | [proxy] |
Forward to a remote SSH host |
| Container | [container] |
Ephemeral container per session |
Container mode
Each SSH session spawns a fresh container. The image defines the toolset — if it's not in the image, the operator can't use it.
[container]
image = "bascule-shell:k8s-ops"
ephemeral = true
hardened = true
memory_limit = "512m"
Features
- Three backends — local PTY, remote SSH proxy, ephemeral containers
- Identity-aware sessions — every connection authenticated and attributed
- SSH key authentication — standard authorized_keys, no surprises
- AI agent authentication — native Microsoft Entra Agent ID support
- Right-sized images — curated container images (minimal, k8s-ops, net-ops, dev)
- SessionHandler trait — extend with custom policy, audit, or recording
- Structured logging — JSON format for production observability
- Small footprint — single binary, ~7MB, <64MB memory
Comparison
| Bascule | Teleport | Boundary | |
|---|---|---|---|
| Agents | None | Required | Required |
| Control plane | None | Required | Required |
| License | Apache 2.0 | AGPL | MPL |
| Container sessions | Native | No | No |
| AI Agent Identity | Native | No | No |
| Binary size | ~7MB | ~150MB | ~100MB |
See docs/comparison.md for the full comparison.
Extending Bascule
Implement the SessionHandler trait to add custom behavior:
use bascule_core::hooks::{SessionHandler, SessionInfo};
struct MyAuditHandler;
#[async_trait]
impl SessionHandler for MyAuditHandler {
async fn on_session_start(&self, session: &SessionInfo) -> anyhow::Result<()> {
log::info!("{} connected from {}", session.principal, session.source_ip);
Ok(())
}
async fn on_exec(&self, session: &SessionInfo, command: &str) -> anyhow::Result<()> {
log::info!("{} executed: {}", session.principal, command);
Ok(())
}
}
Projects like Guildhouse use the SessionHandler trait to add authorization contexts, completion receipts, and merkle-anchored audit trails.
Documentation
License
Apache 2.0