New crate: bascule-shell (471 lines, 1.8MB binary) Login shell that detects identity + platform attestation at startup. Wraps bash/zsh/fish — operator works normally, identity travels with them. Identity detection (priority order): 1. Entra via WSL2 interop 2. Azure CLI 3. Kerberos TGT 4. Cached OIDC token 5. System user (fallback) Platform attestation: TPM 2.0 PCR values via tpm2_pcrread (PCRs 0,1,2,7,10,14) IMA measurement log hash + count Keylime agent state Entra device compliance (WSL2 only) Composite SHA-256 hash over all evidence Shell features: Banner with identity + attestation summary BASCULE_* env vars injected into inner shell --info mode for dry-run display --json mode for machine-readable output --exec mode for single-command execution Configurable via ~/.config/bascule/shell.toml Tested on Fedora with real TPM 2.0: 6 PCRs successfully read from hardware All env vars propagated to inner shell 1.8MB binary, 0 substrate deps Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3.4 KiB
3.4 KiB
Bascule
Identity-aware SSH proxy for modern infrastructure.
Bascule is a lightweight SSH proxy that authenticates users via SSH keys 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 file
- AI agent authentication — Microsoft Entra Agent ID support (optional feature)
- Session limiting — configurable max concurrent sessions
- 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 |
| Auth | SSH keys, Entra Agent ID | OIDC, SAML, GitHub | OIDC, LDAP |
| 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 custom authorization, audit logging, and session governance.
Documentation
Roadmap
- OIDC authentication (Keycloak, Entra, Okta, Google)
- Certificate-based authentication
- OpenTelemetry OTLP trace export
- Prometheus metrics endpoint
- Session recording
- Web UI for session management
License
Apache 2.0