# ============================================================================ # nsjail Configuration: FULL Mode (CAP_SYS_ADMIN) # ============================================================================ # # This configuration provides maximum isolation for Python code execution: # - 7 Linux namespaces (mount, PID, network, IPC, UTS, user, cgroup) # - cgroups v2 resource limits (if kernel supports) # - Seccomp syscall filtering # - Complete filesystem isolation # - Network isolation (no loopback) # # Requirements: # - CAP_SYS_ADMIN capability # - Kernel 4.6+ for all features # - cgroups v2 (kernel 5.2+) for hard memory limits # # ============================================================================ name: "tooljet-python-full" description: "ToolJet Python Execution - FULL Mode (Maximum Isolation)" mode: ONCE # ============================================================================ # NAMESPACE ISOLATION - All 7 namespaces enabled # ============================================================================ clone_newuser: true # User namespace - UID/GID mapping clone_newns: true # Mount namespace - Isolated filesystem clone_newpid: true # PID namespace - Process tree isolation clone_newipc: true # IPC namespace - No shared memory with host clone_newuts: true # UTS namespace - Hostname isolation clone_newnet: true # Network namespace - No network interfaces clone_newcgroup: true # Cgroup namespace - Resource isolation # Map sandbox user to nobody (UID 65534) uidmap { inside_id: "0" outside_id: "65534" count: 1 } gidmap { inside_id: "0" outside_id: "65534" count: 1 } # ============================================================================ # CAPABILITIES AND PRIVILEGES # ============================================================================ # Drop all capabilities inside sandbox keep_caps: false # Enforce no_new_privs (prevent privilege escalation) disable_no_new_privs: false # ============================================================================ # TIME AND RESOURCE LIMITS # ============================================================================ # Wall-clock timeout (10 seconds total execution time) time_limit: 10 # CPU time limit (5 seconds of actual CPU usage) rlimit_cpu: 5 # Address space limit (512MB - fallback if cgroups unavailable) rlimit_as: 536870912 # 512MB in bytes # File size limit (1MB max file write) rlimit_fsize: 1048576 # Open file descriptors limit rlimit_nofile: 64 # Process limit (only 1 process allowed - no forking) rlimit_nproc: 1 # Core dumps disabled rlimit_core: 0 # ============================================================================ # CGROUPS V2 - Hard Resource Limits (preferred) # ============================================================================ # Note: Requires cgroups v2 (kernel 5.2+) and proper cgroup delegation # If unavailable, falls back to rlimits above use_cgroupv2: true # Hard memory limit (128MB - OOM killer enforced) cgroup_mem_max: 134217728 # 128MB in bytes # Disable memory swap cgroup_mem_swap_max: 0 # CPU shares (relative weight for CPU scheduling) cgroup_cpu_ms_per_sec: 1000 # 100% of 1 CPU core max # Maximum number of processes in cgroup cgroup_pids_max: 1 # ============================================================================ # NETWORK ISOLATION # ============================================================================ # Disable loopback interface (no 127.0.0.1) # This prevents access to localhost services and cloud metadata APIs iface_no_lo: true # ============================================================================ # FILESYSTEM MOUNTS # ============================================================================ # Working directory inside sandbox cwd: "/home" # Hostname for sandbox hostname: "tooljet-sandbox" # System libraries (read-only) mount { src: "/usr" dst: "/usr" is_bind: true rw: false } mount { src: "/lib" dst: "/lib" is_bind: true rw: false } mount { src: "/lib64" dst: "/lib64" is_bind: true rw: false mandatory: false } mount { src: "/usr/lib" dst: "/usr/lib" is_bind: true rw: false } # ld.so.cache for dynamic linking (read-only) mount { src: "/etc/ld.so.cache" dst: "/etc/ld.so.cache" is_bind: true rw: false mandatory: false } # Essential devices mount { src: "/dev/null" dst: "/dev/null" is_bind: true rw: true } mount { src: "/dev/zero" dst: "/dev/zero" is_bind: true rw: false } mount { src: "/dev/urandom" dst: "/dev/urandom" is_bind: true rw: false } # Writable tmpfs for working directory (50MB limit) mount { dst: "/home" fstype: "tmpfs" rw: true options: "size=52428800" } # Writable tmpfs for /tmp (16MB limit) mount { dst: "/tmp" fstype: "tmpfs" rw: true options: "size=16777216" } # Create minimal /etc/passwd and /etc/group mount { src_content: "sandbox:x:0:0:Sandbox User:/home:/bin/false" dst: "/etc/passwd" } mount { src_content: "sandbox:x:0:" dst: "/etc/group" } # ============================================================================ # PYTHON RUNTIME MOUNT # ============================================================================ # This will be dynamically added by the executor: # mount { src: "/opt/python-runtime" dst: "/python" is_bind: true rw: false } # ============================================================================ # PACKAGE BUNDLE MOUNT (Dynamic) # ============================================================================ # This will be dynamically added if bundle exists: # mount { src: "/tmp/bundles/" dst: "/packages" is_bind: true rw: false } # ============================================================================ # USER CODE MOUNT (Dynamic) # ============================================================================ # This will be dynamically added at execution time: # mount { src: "/tmp/executions//code.py" dst: "/home/code.py" is_bind: true rw: false } # mount { src: "/tmp/executions//state.json" dst: "/home/state.json" is_bind: true rw: false } # ============================================================================ # ENVIRONMENT VARIABLES # ============================================================================ # Clean environment (do NOT inherit from parent) keep_env: false # Set minimal safe environment envar: "PATH=/usr/local/bin:/usr/bin:/bin" envar: "HOME=/home" envar: "PYTHONPATH=/packages/site-packages" envar: "PYTHONDONTWRITEBYTECODE=1" envar: "PYTHONUNBUFFERED=1" envar: "LANG=C.UTF-8" envar: "LC_ALL=C.UTF-8" # Limit thread usage for numpy/scipy envar: "OMP_NUM_THREADS=2" envar: "OPENBLAS_NUM_THREADS=2" envar: "MKL_NUM_THREADS=2" # ============================================================================ # SECCOMP SYSCALL FILTER # ============================================================================ # Block dangerous syscalls that could: # - Escape sandbox (mount, pivot_root, etc.) # - Debug other processes (ptrace) # - Load kernel modules # - Reboot system # # Network syscalls are allowed here because network namespace already # blocks all network access. Attempting socket() will fail with EACCES. # ============================================================================ # ptrace: Return EPERM instead of killing process (for testability) seccomp_string: "ERRNO(1) {" seccomp_string: " ptrace" seccomp_string: "}" # Kill process on these syscalls seccomp_string: "KILL {" # Debugging/inspection seccomp_string: " process_vm_readv," seccomp_string: " process_vm_writev," # Kernel module loading seccomp_string: " init_module," seccomp_string: " finit_module," seccomp_string: " delete_module," # System control seccomp_string: " kexec_load," seccomp_string: " kexec_file_load," seccomp_string: " reboot," seccomp_string: " sethostname," seccomp_string: " setdomainname," # Namespace manipulation (redundant but explicit) seccomp_string: " unshare," seccomp_string: " setns," # Filesystem manipulation seccomp_string: " mount," seccomp_string: " umount," seccomp_string: " umount2," seccomp_string: " pivot_root," seccomp_string: " chroot," # Performance monitoring seccomp_string: " perf_event_open," # BPF (could be used for privilege escalation) seccomp_string: " bpf," # User namespace creation (prevent nested sandboxes) seccomp_string: " clone," seccomp_string: " clone3" seccomp_string: "}" # Allow all other syscalls seccomp_string: "DEFAULT ALLOW" # ============================================================================ # LOGGING # ============================================================================ # Log level: FATAL, ERROR, WARNING, INFO, DEBUG log_level: WARNING # Don't log every syscall (performance) log_fd: -1 # ============================================================================ # EXECUTION # ============================================================================ # The actual command to execute is provided at runtime: # nsjail --config /etc/nsjail/python-execution-full.cfg \ # --bindmount_ro /opt/python-runtime:/python \ # --bindmount_ro /tmp/bundles/:/packages \ # --bindmount /tmp/executions//code.py:/home/code.py \ # -- /python/bin/python3 /home/code.py # ============================================================================