mirror of
https://github.com/ToolJet/ToolJet
synced 2026-04-21 21:47:17 +00:00
* feat(nsjail): add Python execution configurations * enhance Python security mode detection and configuration * consolidate Python execution configurations and remove USERNS mode
249 lines
9.1 KiB
INI
249 lines
9.1 KiB
INI
# ============================================================================
|
|
# 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/<bundle-id>" dst: "/packages" is_bind: true rw: false }
|
|
|
|
# ============================================================================
|
|
# USER CODE MOUNT (Dynamic)
|
|
# ============================================================================
|
|
# This will be dynamically added at execution time:
|
|
# mount { src: "/tmp/executions/<exec-id>/code.py" dst: "/home/code.py" is_bind: true rw: false }
|
|
# mount { src: "/tmp/executions/<exec-id>/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/<id>:/packages \
|
|
# --bindmount /tmp/executions/<id>/code.py:/home/code.py \
|
|
# -- /python/bin/python3 /home/code.py
|
|
# ============================================================================
|