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:
1
HOWTO.md
1
HOWTO.md
@@ -494,6 +494,7 @@ Key settings in `cameleer-server-app/src/main/resources/application.yml`. All cu
|
||||
| `cameleer.server.runtime.enabled` | `true` | `CAMELEER_SERVER_RUNTIME_ENABLED` | Enable Docker orchestration |
|
||||
| `cameleer.server.runtime.baseimage` | `cameleer-runtime-base:latest` | `CAMELEER_SERVER_RUNTIME_BASEIMAGE` | Base Docker image for app containers |
|
||||
| `cameleer.server.runtime.dockernetwork` | `cameleer` | `CAMELEER_SERVER_RUNTIME_DOCKERNETWORK` | Primary Docker network |
|
||||
| `cameleer.server.runtime.dockerruntime` | *(empty = auto)* | `CAMELEER_SERVER_RUNTIME_DOCKERRUNTIME` | Container runtime override. Empty auto-detects gVisor (`runsc`) when registered with the daemon and falls back to the daemon default. Set to e.g. `kata` to force a specific runtime, or `runc` to force the default even if `runsc` is installed. |
|
||||
| `cameleer.server.runtime.jarstoragepath` | `/data/jars` | `CAMELEER_SERVER_RUNTIME_JARSTORAGEPATH` | JAR file storage directory |
|
||||
| `cameleer.server.runtime.jardockervolume` | *(empty)* | `CAMELEER_SERVER_RUNTIME_JARDOCKERVOLUME` | Docker volume for JAR sharing |
|
||||
| `cameleer.server.runtime.routingmode` | `path` | `CAMELEER_SERVER_RUNTIME_ROUTINGMODE` | `path` or `subdomain` Traefik routing |
|
||||
|
||||
@@ -47,6 +47,11 @@ cameleer:
|
||||
jarstoragepath: ${CAMELEER_SERVER_RUNTIME_JARSTORAGEPATH:/data/jars}
|
||||
baseimage: ${CAMELEER_SERVER_RUNTIME_BASEIMAGE:gitea.siegeln.net/cameleer/cameleer-runtime-base:latest}
|
||||
dockernetwork: ${CAMELEER_SERVER_RUNTIME_DOCKERNETWORK:cameleer}
|
||||
# Container runtime override. Empty (default) auto-detects: uses runsc
|
||||
# (gVisor) if the daemon has it registered, otherwise the daemon default
|
||||
# (runc). Set to a registered runtime name (e.g. "kata", "runc") to
|
||||
# force a specific runtime. See issue #152 for the threat model.
|
||||
dockerruntime: ${CAMELEER_SERVER_RUNTIME_DOCKERRUNTIME:}
|
||||
agenthealthport: 9464
|
||||
healthchecktimeout: 60
|
||||
container:
|
||||
|
||||
@@ -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