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>
90 lines
3.1 KiB
Markdown
90 lines
3.1 KiB
Markdown
# Architecture
|
|
|
|
## Overview
|
|
|
|
Bascule is a single-binary SSH proxy with three pluggable layers:
|
|
|
|
```
|
|
Client SSH → Bascule Server
|
|
│
|
|
├── AuthProvider (SSH keys / OIDC / Agent ID)
|
|
│
|
|
├── SessionHandler hooks
|
|
│ on_session_start → build_session_env → on_exec → on_session_end
|
|
│
|
|
└── SessionBackend
|
|
├── LocalPty (portable-pty → /bin/bash)
|
|
├── RemoteProxy (upstream SSH → target host)
|
|
└── Container (docker/podman → ephemeral image)
|
|
```
|
|
|
|
## Session Backends
|
|
|
|
### Local PTY (default)
|
|
|
|
Spawns a process on this machine via `portable-pty`. The process gets a real PTY with terminal emulation, resize support, and environment variables.
|
|
|
|
### Remote Proxy
|
|
|
|
Opens a second SSH connection to a target host via `russh` client. Bridges I/O between the client and upstream channels. The client sees a transparent connection to the target host, but Bascule mediates authentication and applies SessionHandler hooks.
|
|
|
|
### Container
|
|
|
|
Spawns an ephemeral container per session using docker, podman, or nerdctl. The container is destroyed on disconnect. The container image defines what tools are available — if it's not in the image, the operator can't use it.
|
|
|
|
## Extension Model
|
|
|
|
### AuthProvider trait
|
|
|
|
Implement to add custom authentication:
|
|
|
|
```rust
|
|
#[async_trait]
|
|
pub trait AuthProvider: Send + Sync + 'static {
|
|
async fn check_public_key(&self, user: &str, key: &PublicKey) -> bool;
|
|
async fn check_password(&self, user: &str, password: &str) -> bool;
|
|
fn principal_for_user(&self, user: &str) -> String;
|
|
}
|
|
```
|
|
|
|
### SessionHandler trait
|
|
|
|
Implement to add policy, audit, or custom behavior:
|
|
|
|
```rust
|
|
#[async_trait]
|
|
pub trait SessionHandler: Send + Sync + 'static {
|
|
async fn on_session_start(&self, session: &SessionInfo) -> anyhow::Result<()>;
|
|
async fn build_session_env(&self, session: &SessionInfo) -> HashMap<String, String>;
|
|
async fn on_exec(&self, session: &SessionInfo, command: &str) -> anyhow::Result<()>;
|
|
async fn on_session_end(&self, session: &SessionInfo) -> anyhow::Result<()>;
|
|
fn display_name(&self, session: &SessionInfo) -> String;
|
|
}
|
|
```
|
|
|
|
All methods have default implementations (accept/passthrough). Override only what you need.
|
|
|
|
## Security Model
|
|
|
|
### Container hardening (default)
|
|
|
|
When `hardened = true`:
|
|
- `--security-opt no-new-privileges`
|
|
- `--cap-drop ALL`
|
|
- `--cap-add SETUID --cap-add SETGID` (minimal for shell)
|
|
|
|
### Ephemeral sessions
|
|
|
|
When `ephemeral = true` (default), the container is `--rm` and destroyed on disconnect. Nothing persists between sessions.
|
|
|
|
### Network isolation
|
|
|
|
Set `network = "none"` to completely isolate the container from the network. The operator can run local tools but can't reach external services.
|
|
|
|
## Crate Structure
|
|
|
|
| Crate | Purpose |
|
|
|-------|---------|
|
|
| `bascule-core` | Library — server, handler, auth, PTY, proxy, container, hooks |
|
|
| `bascule-server` | Binary — CLI, config loading, tracing setup |
|
|
| `bascule-auth-agent-id` | Optional — Entra Agent ID authentication |
|