This document tracks security assumptions, boundaries, and design decisions for Podclaw.
Podclaw assumes a two-host model:
- Production host -- your real homelab or workstation. Out of scope for Podclaw experiments. OpenClaw containers should never touch this host.
- Sacrificial host -- a dedicated machine running Ubuntu 24.04 LTS with Incus. Acceptable assumption: this machine can be rooted or reinstalled without serious loss.
-
We assume:
- OpenClaw and any agents it controls may be malicious or compromised.
- AI coding agents may make mistakes, generate unsafe commands, or mis-handle secrets.
- The Incus daemon on the sacrificial host is trusted but exposed only locally.
-
We want to prevent:
- Lateral movement from the sacrificial host into production hosts or other important infrastructure.
- Access to SSH keys, API tokens, or sensitive data outside this lab.
- Host-level persistence that survives a full wipe/reinstall of the sacrificial host.
Allowed
- Creating/modifying Incus containers using profiles defined in
profiles/. - Running rootless Podman / Docker inside Incus containers.
- Installing and running OpenClaw and supporting tooling in those containers.
- Pulling container images from public registries (GHCR, Docker Hub).
- Storing non-sensitive logs and build artifacts.
Not allowed
- Adding Incus
diskdevices that mount host paths outside container rootfs. - Setting
security.privileged=trueon containers, unless explicitly documented and justified. - Adding new network devices that reach other VLANs / management networks.
- Copying SSH private keys, API tokens, or secrets from other machines into the lab host.
- Configuring the lab host as a router or gateway for the rest of the network.
- Default stance:
- OpenClaw gateways bind to 127.0.0.1 inside their Incus container.
- Access from outside the container is via the container's LAN IP (bridged profile) or SSH port forwarding.
- LAN exposure:
- Only recommended through an explicit reverse proxy or load balancer that:
- Terminates TLS.
- Enforces authentication.
- Is documented with config and diagrams.
- Only recommended through an explicit reverse proxy or load balancer that:
- No OpenClaw instance from this lab should be exposed directly to the public internet.
- Incus profiles should:
- Set sensible CPU and RAM limits for containers where practical.
- Use a dedicated storage pool if possible, so lab cleanup is just a pool wipe.
- If the lab becomes unstable or confusing:
- Preferred remediation is to:
- Stop and delete all Incus containers on the sacrificial host.
- Optionally wipe the Incus storage pool.
- Rebuild from a clean Ubuntu 24.04 install using Podclaw configs.
- Preferred remediation is to:
These are gotchas discovered while building the cloud-init automation. They apply to anyone running rootless Podman inside unprivileged Incus system containers on Ubuntu 24.04.
Ubuntu 24.04 sets kernel.apparmor_restrict_unprivileged_userns=1, which blocks rootless
Podman (and any unprivileged user namespace creation) inside Incus containers. You must
install AppArmor profiles granting userns permission to: podman, conmon, crun,
slirp4netns, and pasta.
Example profile:
abi <abi/4.0>,
include <tunables/global>
profile podman /usr/bin/podman flags=(unconfined) {
userns,
}
Load with apparmor_parser -r. Must be done before running OpenClaw's setup script.
Rootless Podman with --userns keep-id triggers storage-chown-by-maps, which recursively
chowns the entire image overlay. For large images (3+ GB) this takes 10+ minutes and causes
systemd timeouts. Fix: use fuse-overlayfs as the storage driver.
# ~/.config/containers/storage.conf
[storage]
driver = "overlay"
[storage.options.overlay]
mount_program = "/usr/bin/fuse-overlayfs"This handles UID mapping in FUSE and skips the chown entirely.
Rootless podman build inside Incus fails without security.nesting=true. The Incus
profile must also set security.syscalls.intercept.mknod=true and
security.syscalls.intercept.setxattr=true.
The runcmd order must be:
- Install
fuse-overlayfsandapparmor-utils(via packages) - Create and load AppArmor userns profiles
- Create the service user and configure
storage.conf - Run OpenClaw's
setup-podman.sh/scripts/podman/setup.sh
All rootless Podman prerequisites must be in place before the setup script runs.
Pre-creating the openclaw user with useradd -m -s /usr/sbin/nologin in cloud-init
runcmd works -- the setup script detects the existing user and skips creation. However,
using cloud-init's users: directive with system: true inhibits home directory creation
and causes conflicts.
- OpenClaw's
scripts/podman/setup.shrefuses to run as root. Run it as the service user. sudo -iufails for users with/usr/sbin/nologinshell; usesudo -uinstead.- Cloning repos as root then running scripts as another user causes permission mismatches --
chownthe clone before running setup. - The Quadlet service name is
openclaw.service, notopenclaw-gateway.service. - Quadlet-generated units are transient --
systemctl enablefails with "Unit is transient or generated". This is expected; the unit starts via the systemd generator. - The Quadlet uses
--bind lan(0.0.0.0). This requires eithergateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true(lab only) or an explicitgateway.controlUi.allowedOriginslist inopenclaw.json. Pre-create the config before the setup script runs (it skips creation if the file exists).
OpenClaw ships daily releases (e.g. v2026.3.24). The cloud-init template clones main
at container launch time with no pinned commit or tag. This means every fresh container
gets whatever is current on main at boot.
Trade-offs:
- Pro: No version drift -- each fresh container is up-to-date automatically.
- Con: A breaking change on
maincan silently break new container launches. - Current decision: Accept the risk for lab use. OpenClaw's daily releases are generally
backward-compatible and
scripts/podman/setup.shhandles most wiring. - If stability becomes a problem: Pin to a release tag by changing the clone step to
git clone --branch v2026.X.Y --depth 1 https://github.com/openclaw/openclaw /opt/openclaw.
As of March 2026, the root-level setup-podman.sh still exists but scripts/podman/setup.sh
is the newer maintained path (includes Quadlet template support via openclaw.container.in).
- Decide whether to pin container CPU cores for more deterministic performance.
- Add automated checks (scripts or CI) that lint cloud-init and profiles for disallowed patterns.
- Add example OpenClaw reverse-proxy + TLS configuration.
- Replace
dangerouslyAllowHostHeaderOriginFallbackwith explicitgateway.controlUi.allowedOriginsbefore any non-lab deployment.