If you discover a security vulnerability, please report it responsibly:
- Do not open a public issue
- Email the maintainers with a description of the vulnerability
- Include steps to reproduce if possible
- Allow reasonable time for a fix before public disclosure
- Tower runs with Docker socket access (
docker.sock) to manage worker containers - Workers run in isolated Docker containers, destroyed after each job
- No shared volumes between Tower and workers - config is injected via
put_archive, results extracted viaget_archive
All worker containers run with security hardening:
cap_drop=["ALL"]- no Linux capabilitiesno-new-privileges:true- prevent privilege escalationpids_limit=100- fork bomb protectionipc_mode="private"- isolated IPC namespace- Internal network (no internet access, ICC enabled for gateway access)
- Optional gVisor kernel-level isolation via
WORKER_RUNTIME=runsc
- Bearer token auth via
TOWER_API_KEYenvironment variable - Timing-safe comparison (
hmac.compare_digest) - Public endpoints:
/health,/engines,/profiles,/docs,/ui
- SSRF: Webhook URLs validated against internal hosts at request time and before firing (DNS rebinding defense)
- Path traversal: Profile loading and hook injection validated with
Path.is_relative_to() - Input validation: Agent ID, profile, and plugin names restricted to
^[a-zA-Z0-9_-]{1,64}$ - Error sanitization: Internal paths and Docker details stripped from error responses
- Result size limits:
MAX_RESULT_SIZEprevents oversized container output from causing OOM - XSS prevention: Dashboard uses
escapeHtml()for user-controlled content, API key stored in sessionStorage (not localStorage) - Null-byte stripping: Worker
parse-job.jsstrips null bytes and truncates oversized shell arguments - Config clamping: All numeric env vars are auto-clamped to valid ranges at startup, preventing misconfiguration
| Version | Supported |
|---|---|
| 0.3.x | Yes |