124 lines
5.4 KiB
Markdown
124 lines
5.4 KiB
Markdown
English | [日本語](security-hardening.ja.md)
|
|
|
|
# Security Hardening Guide
|
|
|
|
MAESTRO runs LLM-driven tasks that execute code, fetch the web, drive a browser,
|
|
and optionally run SSH commands. **Treat it as a privileged service.** This
|
|
guide turns the baseline in [SECURITY.md](../SECURITY.md) into an actionable,
|
|
ordered checklist for moving from "works on my laptop" to "safe for others to
|
|
reach."
|
|
|
|
For how to report a vulnerability, see [SECURITY.md](../SECURITY.md). For where
|
|
secrets live and their file permissions, see its *Secrets and Data* section.
|
|
|
|
## Threat model in one paragraph
|
|
|
|
Anyone who can reach the UI/API can create tasks, and a task can run tools
|
|
(Bash, web, browser, files, and — if enabled — SSH/MCP). Without authentication
|
|
that means anyone who can reach the port can run code on the host. It ships
|
|
**unauthenticated**: on bare metal it binds to localhost, but the Docker default
|
|
publishes the port to your LAN — so securing it before you leave it exposed is
|
|
on you. Everything below is that checklist.
|
|
|
|
## 1. Network exposure
|
|
|
|
- The app binds to `127.0.0.1` by default on bare metal, but Docker Compose
|
|
publishes `9876` on **all interfaces** by default — so a Docker deployment is
|
|
LAN-reachable and unauthenticated out of the box. Enable auth and TLS before
|
|
leaving it on any shared network, or pin the Compose mapping to
|
|
`127.0.0.1:9876:9876` (and reach it over an SSH tunnel) to keep it local-only.
|
|
- Front it with TLS — either MAESTRO's native HTTPS (`server.tls`) or a reverse
|
|
proxy — whenever it is reachable beyond loopback.
|
|
|
|
## 2. Authentication
|
|
|
|
Authentication is **off by default**. Before exposing the service beyond
|
|
localhost, turn it on:
|
|
|
|
- **OAuth** (Google / Gitea) or **local accounts** (email + password). Configure
|
|
under `auth` in `config.yaml` or via Settings → Authentication.
|
|
- Set `auth.primary_provider` (`google` | `gitea` | `local`) when more than one
|
|
provider is enabled to avoid an unintended login path.
|
|
- Restrict who becomes admin via `auth.admin_emails`.
|
|
- Set a stable `auth.session_secret` (or rely on the persisted
|
|
`data/secrets/session-secret.key`); share it across nodes in a multi-node
|
|
deployment so sessions survive failover.
|
|
|
|
## 3. Authorization and visibility
|
|
|
|
Tasks, schedules, and jobs carry an owner and a visibility scope: `private`
|
|
(owner + admin), `org` (members of the same organization), or `public` (any
|
|
logged-in user). Default new resources to `private` and grant `org`/`public`
|
|
only deliberately. Admins can see everything — keep the admin set small.
|
|
|
|
## 4. The Bash sandbox
|
|
|
|
The `Bash` tool runs inside a sandbox controlled by `safety.bash_sandbox`:
|
|
|
|
- `auto` (default) — use bubblewrap when available, otherwise a hardened
|
|
allowlist.
|
|
- `always` — **required for multi-user / untrusted deployments**; fails closed
|
|
if bubblewrap is unavailable rather than silently downgrading.
|
|
- `off` — no isolation; only for a trusted single operator.
|
|
|
|
bubblewrap needs unprivileged user namespaces. See
|
|
[operations/bash-sandbox-provisioning.md](operations/bash-sandbox-provisioning.md)
|
|
and, for containers, [docker.md](docker.md).
|
|
|
|
## 5. Transport security (TLS)
|
|
|
|
- Enable `server.tls` for native HTTPS, or terminate TLS at a reverse proxy.
|
|
- Set `auth.secure_cookie: true` whenever TLS terminates upstream (behind a
|
|
proxy) so the session cookie carries the `Secure` flag. Native TLS sets it
|
|
automatically.
|
|
- The default self-signed certificate makes browsers warn; install a real
|
|
certificate for anything others use.
|
|
|
|
## 6. The metrics endpoint
|
|
|
|
`/metrics` exposes operational data. In production, restrict it:
|
|
|
|
- set a `bearer_token` (worker and/or gateway metrics config), and/or
|
|
- limit `allowed_hosts` to the scraper's source IPs.
|
|
|
|
Do not leave `/metrics` open on an internet-facing deployment.
|
|
|
|
## 7. Tools and integrations
|
|
|
|
Each enabled capability widens the blast radius. Before granting access to
|
|
untrusted users, review which tools and integrations are on:
|
|
|
|
- **SSH** — runs commands on remote hosts; credentials are envelope-encrypted
|
|
with the master key. Enable only for trusted operators.
|
|
- **MCP** — external tool servers; credentials require `MCP_ENCRYPTION_KEY`.
|
|
- **Browser** — drives a real Chromium; treat fetched/automated content as
|
|
untrusted input.
|
|
|
|
## 8. Secrets
|
|
|
|
See the *Secrets and Data* section of [SECURITY.md](../SECURITY.md). In short:
|
|
generated secrets live under `data/secrets/` at mode `0600`, are gitignored and
|
|
dockerignored, and core dumps are excluded because they can contain decrypted
|
|
keys. Rotate after any suspected exposure and review the audit log.
|
|
|
|
## 9. Updates and monitoring
|
|
|
|
- Track releases and apply security fixes promptly (fixes land on the latest
|
|
release and `main`).
|
|
- Review the audit log after permission or configuration changes.
|
|
|
|
## Production checklist
|
|
|
|
Copy-paste before exposing MAESTRO to anyone but yourself:
|
|
|
|
- [ ] Authentication enabled (`auth.*`), `primary_provider` set, `admin_emails` minimal
|
|
- [ ] Stable `auth.session_secret` (shared across nodes if clustered)
|
|
- [ ] `safety.bash_sandbox: always`
|
|
- [ ] TLS terminated (native `server.tls` or a proxy) + `secure_cookie` matched
|
|
- [ ] Bind/port restricted to the intended interface
|
|
- [ ] `/metrics` protected (`bearer_token` and/or `allowed_hosts`)
|
|
- [ ] New resources default to `private`; `org`/`public` granted deliberately
|
|
- [ ] SSH/MCP/browser reviewed; enabled only as needed
|
|
- [ ] Secrets out of version control and backups; rotation plan in place
|
|
- [ ] A process to apply security updates
|