zeroclaw/docs/frictionless-security.md

317 lines
8.3 KiB
Markdown

# Frictionless Security: Zero Impact on Wizard
> ⚠️ **Status: Proposal / Roadmap**
>
> This document describes proposed approaches and may include hypothetical commands or config.
> For current runtime behavior, see [config-reference.md](config-reference.md), [operations-runbook.md](operations-runbook.md), and [troubleshooting.md](troubleshooting.md).
## Core Principle
> **"Security features should be like airbags — present, protective, and invisible until needed."**
## Design: Silent Auto-Detection
### 1. No New Wizard Steps (Stays 9 Steps, < 60 Seconds)
```rust
// Wizard remains UNCHANGED
// Security features auto-detect in background
pub fn run_wizard() -> Result<Config> {
// ... existing 9 steps, no changes ...
let config = Config {
// ... existing fields ...
// NEW: Auto-detected security (not shown in wizard)
security: SecurityConfig::autodetect(), // Silent!
};
config.save()?;
Ok(config)
}
```
### 2. Auto-Detection Logic (Runs Once at First Start)
```rust
// src/security/detect.rs
impl SecurityConfig {
/// Detect available sandboxing and enable automatically
/// Returns smart defaults based on platform + available tools
pub fn autodetect() -> Self {
Self {
// Sandbox: prefer Landlock (native), then Firejail, then none
sandbox: SandboxConfig::autodetect(),
// Resource limits: always enable monitoring
resources: ResourceLimits::default(),
// Audit: enable by default, log to config dir
audit: AuditConfig::default(),
// Everything else: safe defaults
..SecurityConfig::default()
}
}
}
impl SandboxConfig {
pub fn autodetect() -> Self {
#[cfg(target_os = "linux")]
{
// Prefer Landlock (native, no dependency)
if Self::probe_landlock() {
return Self {
enabled: true,
backend: SandboxBackend::Landlock,
..Self::default()
};
}
// Fallback: Firejail if installed
if Self::probe_firejail() {
return Self {
enabled: true,
backend: SandboxBackend::Firejail,
..Self::default()
};
}
}
#[cfg(target_os = "macos")]
{
// Try Bubblewrap on macOS
if Self::probe_bubblewrap() {
return Self {
enabled: true,
backend: SandboxBackend::Bubblewrap,
..Self::default()
};
}
}
// Fallback: disabled (but still has application-layer security)
Self {
enabled: false,
backend: SandboxBackend::None,
..Self::default()
}
}
#[cfg(target_os = "linux")]
fn probe_landlock() -> bool {
// Try creating a minimal Landlock ruleset
// If it works, kernel supports Landlock
landlock::Ruleset::new()
.set_access_fs(landlock::AccessFS::read_file)
.add_path(Path::new("/tmp"), landlock::AccessFS::read_file)
.map(|ruleset| ruleset.restrict_self().is_ok())
.unwrap_or(false)
}
fn probe_firejail() -> bool {
// Check if firejail command exists
std::process::Command::new("firejail")
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
}
```
### 3. First Run: Silent Logging
```bash
$ zeroclaw agent -m "hello"
# First time: silent detection
[INFO] Detecting security features...
[INFO] ✓ Landlock sandbox enabled (kernel 6.2+)
[INFO] ✓ Memory monitoring active (512MB limit)
[INFO] ✓ Audit logging enabled (~/.config/zeroclaw/audit.log)
# Subsequent runs: quiet
$ zeroclaw agent -m "hello"
[agent] Thinking...
```
### 4. Config File: All Defaults Hidden
```toml
# ~/.config/zeroclaw/config.toml
# These sections are NOT written unless user customizes
# [security.sandbox]
# enabled = true # (default, auto-detected)
# backend = "landlock" # (default, auto-detected)
# [security.resources]
# max_memory_mb = 512 # (default)
# [security.audit]
# enabled = true # (default)
```
Only when user changes something:
```toml
[security.sandbox]
enabled = false # User explicitly disabled
[security.resources]
max_memory_mb = 1024 # User increased limit
```
### 5. Advanced Users: Explicit Control
```bash
# Check what's active
$ zeroclaw security --status
Security Status:
✓ Sandbox: Landlock (Linux kernel 6.2)
✓ Memory monitoring: 512MB limit
✓ Audit logging: ~/.config/zeroclaw/audit.log
47 events logged today
# Disable sandbox explicitly (writes to config)
$ zeroclaw config set security.sandbox.enabled false
# Enable specific backend
$ zeroclaw config set security.sandbox.backend firejail
# Adjust limits
$ zeroclaw config set security.resources.max_memory_mb 2048
```
### 6. Graceful Degradation
| Platform | Best Available | Fallback | Worst Case |
|----------|---------------|----------|------------|
| **Linux 5.13+** | Landlock | None | App-layer only |
| **Linux (any)** | Firejail | Landlock | App-layer only |
| **macOS** | Bubblewrap | None | App-layer only |
| **Windows** | None | - | App-layer only |
**App-layer security is always present** — this is the existing allowlist/path blocking/injection protection that's already comprehensive.
---
## Config Schema Extension
```rust
// src/config/schema.rs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityConfig {
/// Sandbox configuration (auto-detected if not set)
#[serde(default)]
pub sandbox: SandboxConfig,
/// Resource limits (defaults applied if not set)
#[serde(default)]
pub resources: ResourceLimits,
/// Audit logging (enabled by default)
#[serde(default)]
pub audit: AuditConfig,
}
impl Default for SecurityConfig {
fn default() -> Self {
Self {
sandbox: SandboxConfig::autodetect(), // Silent detection!
resources: ResourceLimits::default(),
audit: AuditConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SandboxConfig {
/// Enable sandboxing (default: auto-detected)
#[serde(default)]
pub enabled: Option<bool>, // None = auto-detect
/// Sandbox backend (default: auto-detect)
#[serde(default)]
pub backend: SandboxBackend,
/// Custom Firejail args (optional)
#[serde(default)]
pub firejail_args: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SandboxBackend {
Auto, // Auto-detect (default)
Landlock, // Linux kernel LSM
Firejail, // User-space sandbox
Bubblewrap, // User namespaces
Docker, // Container (heavy)
None, // Disabled
}
impl Default for SandboxBackend {
fn default() -> Self {
Self::Auto // Always auto-detect by default
}
}
```
---
## User Experience Comparison
### Before (Current)
```bash
$ zeroclaw onboard
[1/9] Workspace Setup...
[2/9] AI Provider...
...
[9/9] Workspace Files...
✓ Security: Supervised | workspace-scoped
```
### After (With Frictionless Security)
```bash
$ zeroclaw onboard
[1/9] Workspace Setup...
[2/9] AI Provider...
...
[9/9] Workspace Files...
✓ Security: Supervised | workspace-scoped | Landlock sandbox ✓
# ↑ Just one extra word, silent auto-detection!
```
### Advanced User (Explicit Control)
```bash
$ zeroclaw onboard --security-level paranoid
[1/9] Workspace Setup...
...
✓ Security: Paranoid | Landlock + Firejail | Audit signed
```
---
## Backward Compatibility
| Scenario | Behavior |
|----------|----------|
| **Existing config** | Works unchanged, new features opt-in |
| **New install** | Auto-detects and enables available security |
| **No sandbox available** | Falls back to app-layer (still secure) |
| **User disables** | One config flag: `sandbox.enabled = false` |
---
## Summary
**Zero impact on wizard** — stays 9 steps, < 60 seconds
**Zero new prompts** silent auto-detection
**Zero breaking changes** backward compatible
**Opt-out available** explicit config flags
**Status visibility** `zeroclaw security --status`
The wizard remains "quick setup universal applications" security is just **quietly better**.