bascule-oss/docs/architecture.md
Tyler King e7fc9fa5e1 feat: structured logging, tracing spans, comprehensive documentation
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>
2026-04-04 23:45:03 -04:00

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 |