docs(runtime): document hardening contract and runtime override (#152)
Surfaces the multi-tenant container hardening contract introduced in the prior commit so operators and integrators know what is enforced and why. - application.yml: declare `cameleer.server.runtime.dockerruntime` alongside the other runtime properties (empty = auto-detect runsc). - HOWTO.md: add the override row to the Runtime config table. - SERVER-CAPABILITIES.md: new "Multi-Tenant Runtime Sandboxing" section describing the cap_drop, no-new-privileges, AppArmor, read-only rootfs, pids_limit, /tmp tmpfs, and runsc auto-detect contract — plus the on-disk state caveat that motivates issue #153. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,24 @@ Each server instance serves exactly one tenant. Multiple tenants share infrastru
|
||||
|
||||
---
|
||||
|
||||
## Multi-Tenant Runtime Sandboxing
|
||||
|
||||
When the server orchestrates tenant containers (SaaS / managed mode), every container is launched with an unconditional hardening contract — Java 17 has no `SecurityManager`, so isolation must live below the JVM. Camel ships components that turn a header into shell (`camel-exec`, `camel-bean`, `camel-groovy`, `camel-mvel`, `camel-velocity`), so tenant JARs are treated as hostile by default.
|
||||
|
||||
| Layer | What is enforced |
|
||||
|---|---|
|
||||
| Capabilities | `cap_drop` every Linux capability the SDK enumerates (effectively ALL — outbound TCP needs none). |
|
||||
| Privilege escalation | `no-new-privileges` — setuid binaries cannot escalate. |
|
||||
| MAC profile | `apparmor=docker-default`. The Docker daemon's default seccomp profile is applied implicitly. |
|
||||
| Filesystem | `read_only` rootfs. `/tmp` is a 256m tmpfs (`rw,nosuid` — `noexec` is intentionally **not** set so JNI native libs from Netty/Snappy/LZ4/Zstd can `dlopen`). |
|
||||
| Resource caps | `pids_limit=512` per container; CPU and memory limits per tenant config. |
|
||||
| Container runtime | Auto-detects gVisor (`runsc`) via `docker info` and uses it when registered with the daemon. Override with `CAMELEER_SERVER_RUNTIME_DOCKERRUNTIME` (e.g. `kata`, or `runc` to force the default). |
|
||||
| Network | Per-tenant Docker bridge `cameleer-tenant-{slug}` + per-env discovery network `cameleer-env-{tenantId}-{envSlug}`. Tenants cannot reach each other's containers. |
|
||||
|
||||
**Implication for tenants writing on-disk state**: with `read_only` rootfs, anything that needs durable disk (Kafka Streams RocksDB stores, Hibernate L2 cache, log files outside stdout) must be on a writeable volume. Per-app `containerConfig.writeableVolumes` support is tracked separately — see issue #153.
|
||||
|
||||
---
|
||||
|
||||
## Agent Protocol
|
||||
|
||||
### Lifecycle
|
||||
|
||||
Reference in New Issue
Block a user