Running an untrusted OCI image
Pick a strict profile, deny network by default, and constrain the filesystem when the workload is hostile.
When the image comes from someone you do not trust, the goal is to grant the workload the least it needs and nothing more. This guide covers the posture for running a hostile OCI image.
Start strict, loosen deliberately
ZViz applies a default enforcement stack — five namespaces, all 41 capabilities dropped, Landlock, seccomp, cgroups v2. For untrusted work, start from the strictest profile and widen only where the workload fails for a legitimate reason:
zviz run untrusted ~/bundle --profile hostile-tenant
Deny network by default
Egress is default-deny. A workload that should never phone home simply cannot. When a workload legitimately needs the network, grant the specific access it requires rather than opening egress wholesale.
Constrain the filesystem
The Landlock ruleset limits which paths the workload can read and write. Mount in only the directories the
workload needs, read-only where possible. ZViz auto-mounts /proc (nosuid+nodev+noexec), /sys (read-only),
and a private /dev tmpfs, so you do not need mounts[] entries for those.
Verify what is blocked
Dangerous syscalls return EPERM. If the workload tries ptrace, mount, unshare, or init_module, it
is denied at the seccomp boundary before the kernel runs the call. Run with --verbose to see the
allow/deny decisions as they happen.
When a workload does not fit
If the image genuinely needs ptrace, mount, unshare, or nested containers, ZViz will block it by design.
That is a signal to run it under gVisor instead, which emulates those syscalls. See the
threat model for where ZViz’s boundary sits.
Frequently Asked Questions
What is the safest profile to start from?
Start from the strictest profile for untrusted work and loosen only what the workload demonstrably needs. It is easier to grant one capability back than to notice one you should have removed.
How do I let the workload reach the network?
Network egress is default-deny. You grant the specific access the workload needs rather than opening everything, so a compromised workload cannot exfiltrate silently.
Related
Build from source and run your first container
Compile ZViz with Zig 0.15+, extract an OCI rootfs, and run it under a selective-denial profile.
GuideKernel and Landlock requirements
Why ZViz needs Linux 5.13+, cgroups v2, and (on Ubuntu 24.04+) an AppArmor profile — and what happens without them.
GuideTuning cgroups v2 limits
Cap memory, PIDs, and CPU per container so a runaway or hostile workload stays contained.