ljexec is a lightweight and deterministic Linux jail launcher written in C.
Find a file
2025-04-20 12:32:16 +02:00
scripts Added script to build squashfs from Alpine Linux mini root 2025-04-19 21:20:04 +02:00
src Some refactoring and introduction of mount_squashfs prototype, see #1 2025-04-20 12:32:16 +02:00
LICENSE Cleaned up includes, added LICENSE file and updated license headers 2025-04-19 20:39:26 +02:00
Makefile Some refactoring and introduction of mount_squashfs prototype, see #1 2025-04-20 12:32:16 +02:00
README.md Cleaned up includes, added LICENSE file and updated license headers 2025-04-19 20:39:26 +02:00
TODO.md Updated TODO 2025-04-19 14:46:29 +02:00

ljexec

ljexec is a lightweight and deterministic Linux jail launcher written in C.
It allows unprivileged users to run programs in isolated environments using user namespaces, chroot, and optional OverlayFS layers.


Features

  • Rootless by design no root privileges required at runtime
  • OverlayFS support writable layer over a read-only base
  • SquashFS support use compressed images as base rootfs
  • Read-only by default unless explicitly overridden
  • Resource Control via Cgroups - fine-grained CPU and memory limits using cgroupv2
  • Minimal dependencies uses only Linux syscalls and standard C
  • Clean mount lifecycle private mount namespace with automatic teardown
  • Compact and modular well-structured, documented C code

🚀 Usage

ljexec --root /path/to/rootfs [options]
ljexec --image /path/to/rootfs.squashfs [options]

Options

Option Description
--exec CMD Command to run inside the jail (default: /bin/sh)
--root DIR Use a directory as the root filesystem (read-only by default)
--image FILE Use a read-only SquashFS image as the base filesystem
--rw Enable writable root filesystem (only with --root)
--overlay-dir DIR Directory to store upper/ and work/ overlay directories
--mem MBYTES Limit the jail's memory usage in megabytes (e.g. --mem 512)
--cpu PERCENT Limit the jail's CPU usage as percentage (e.g. --cpu 50 = half CPU)

🧩 Rootless Cgroup Limitations

Memory and CPU limits via --mem and --cpu use cgroup v2 and work best if the system allows user-created cgroups.

In most systemd environments, this works if the user slice has +cpu +memory enabled:

sudo sh -c 'echo +cpu +memory > /sys/fs/cgroup/user.slice/user-$(id -u).slice/user@$(id -u).service/cgroup.subtree_control'

If that is not possible or you're outside systemd, you can define an alternative writable cgroup root via:

mkdir -p /tmp/mycg
sudo mount -t cgroup2 none /tmp/mycg
echo "+memory +cpu" | sudo tee /tmp/mycg/cgroup.subtree_control
sudo chown -R $(id -u):$(id -g) /tmp/mycg
sudo chmod -R 770 /tmp/mycg

LJEXEC_CGROOT=/tmp/mycg ./ljexec --mem 512 ...

All cgroup creation and configuration is handled within the mount namespace, and ljexec will log errors but still launch the jail in case of limitations.


📦 Examples

ljexec --root /opt/alpine --exec "/bin/sh"

➡️ Launches an interactive shell inside a read-only root filesystem from the directory /opt/alpine. Since neither --rw nor --overlay-dir is provided, the rootfs is mounted as read-only by default. 📌 Useful for testing a minimal rootfs without making changes.

ljexec --root /opt/alpine --rw --exec "/bin/sh"

➡️ Same as the first example, but the --rw flag makes the root filesystem writable. ⚠️ Changes are persistent and affect the original root directory directly. 📌 Good for when you explicitly want to modify the rootfs.

mkdir -p /tmp/my-overlay
ljexec --root /opt/alpine --overlay-dir /tmp/my-overlay --exec "/bin/sh"

➡️ Creates an OverlayFS mount over /opt/alpine, with upper and work directories under /tmp/my-overlay. This makes the jail filesystem writable without modifying the original rootfs. 📌 Ideal for sessions where persistence is required.

ljexec --image /opt/alpine.squashfs --overlay-dir /tmp/my-overlay --exec "/bin/sh"

➡️ Uses a SquashFS image (/opt/alpine.squashfs) as a compressed read-only base. Overlay is applied from /tmp/my-overlay, making the system writable. 📌 Great for environments with a reproducible image base.

ljexec --image /opt/alpine.squashfs --overlay-dir /tmp/my-overlay --exec "/bin/sh" --mem 512 --cpu 25

➡️ Uses a SquashFS image (/opt/alpine.squashfs) as a compressed read-only base. Overlay is applied from /tmp/my-overlay, making the system writable. The jail is resource-restricted with memory limited to 512 MB and CPU limited to 25%. 📌 Great for predictable production environments with a reproducible image base.


🔐 Security Model

  • Uses user namespaces and chroot for isolation
  • No setuid binaries or root access required
  • All mounts are done in a private namespace
  • Optional OverlayFS ensures data separation and resettable environments

🛠 Build Instructions

make
./ljexec --help

📁 Project Structure

src/
├── main.c        # CLI & entrypoint
├── log.c/.h      # Simple logging utilities
├── mount.c/.h    # Filesystem mount logic
├── dev.c/.h      # /dev setup inside the jail
├── nsmap.c/.h    # UID/GID map support for user namespaces

📝 License

MIT License see LICENSE file for details.


❤️ Why ljexec?

This project was born out of frustration with overly complex container runtimes.
ljexec focuses on clarity, determinism, and simplicity, making it a perfect base for:

  • Embedded systems
  • Developer sandboxes
  • Minimal containerization
  • Custom init environments

🌱 Future Enhancement Ideas

These features are not implemented yet but are considered for future versions:

  • Integrated SquashFS Builder
    CLI tool to build minimal rootfs images directly from a directory or Dockerfile-like spec.

  • Mesh-Based Horizontal Scaling
    Peer-to-peer deployment of jails using local mesh discovery and synchronization for clustered workloads.

  • Network Isolation
    Lightweight per-jail networking via unshare --net, virtual bridges or user-mode networking with proxying.

  • Integration with Runit
    Clean service supervision and restart capabilities using runit as a native jail manager.


🧭 Project Manifest

ljexec is built from a desire for autonomy, simplicity, and clarity in system tooling. It exists as a direct response to the growing complexity and opacity in container ecosystems.

Core Values

🔹 Minimalism
Only the essentials. No background daemons, no socket activation, no frameworks. Just Linux, chroot, namespaces, and overlay mounts — all under your control.

🔹 Transparency
Every behavior is explicit and predictable. The mount lifecycle, the namespace setup, and the environment are entirely deterministic.

🔹 Rootless by Default
ljexec empowers unprivileged users to isolate processes securely without special system privileges.

🔹 Reproducibility & Resilience
The system should work the same across environments, without requiring network access or external dependencies. Ideal for embedded, air-gapped, or low-trust scenarios.

🔹 Manual-first & Scriptable
Designed to be understood and composed by humans. No hidden state, no orchestration. Write simple scripts and know exactly what happens.

🔹 Host Respect
Your host is not a playground — it deserves clarity. ljexec avoids global side effects, leaves no daemons running, and doesnt expect control over the system.

Why This Matters

Too many tools today assume control over your system and bury complexity under automation.
ljexec is different — it's not a platform, but a focused tool for those who demand clarity, determinism, and trust in every line of code that runs.