feat(sandbox): add Node.js code execution support to sandbox
This commit is contained in:
@@ -4,9 +4,6 @@ from typing import List, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
import yaml
|
||||
|
||||
SANDBOX_USER_ID = 1000
|
||||
SANDBOX_GROUP_ID = 1000
|
||||
|
||||
DEFAULT_PYTHON_LIB_REQUIREMENTS_AMD = [
|
||||
"/usr/local/lib/python3.12",
|
||||
"/usr/lib/python3",
|
||||
@@ -15,13 +12,18 @@ DEFAULT_PYTHON_LIB_REQUIREMENTS_AMD = [
|
||||
"/etc/nsswitch.conf",
|
||||
"/etc/hosts",
|
||||
"/etc/resolv.conf",
|
||||
"/run/systemd/resolve/stub-resolv.conf",
|
||||
"/run/resolvconf/resolv.conf",
|
||||
"/etc/localtime",
|
||||
"/usr/share/zoneinfo",
|
||||
"/etc/timezone",
|
||||
]
|
||||
|
||||
DEFAULT_NODEJS_LIB_REQUIREMENTS = [
|
||||
"/etc/ssl/certs/ca-certificates.crt",
|
||||
"/etc/nsswitch.conf",
|
||||
"/etc/resolv.conf",
|
||||
"/etc/hosts",
|
||||
]
|
||||
|
||||
|
||||
class AppConfig(BaseModel):
|
||||
"""Application configuration"""
|
||||
@@ -43,83 +45,77 @@ class Config(BaseModel):
|
||||
max_workers: int = 4
|
||||
max_requests: int = 50
|
||||
worker_timeout: int = 30
|
||||
nodejs_path: str = "node"
|
||||
|
||||
enable_network: bool = True
|
||||
enable_preload: bool = False
|
||||
|
||||
python_path: str = ""
|
||||
python_lib_paths: list = Field(default=DEFAULT_PYTHON_LIB_REQUIREMENTS_AMD)
|
||||
python_deps_update_interval: str = "30m"
|
||||
|
||||
nodejs_path: str = ""
|
||||
nodejs_lib_paths: list = Field(default=DEFAULT_NODEJS_LIB_REQUIREMENTS)
|
||||
|
||||
allowed_syscalls: List[int] = Field(default_factory=list)
|
||||
proxy: ProxyConfig = Field(default_factory=ProxyConfig)
|
||||
|
||||
sandbox_user: str = "sandbox"
|
||||
sandbox_uid: int = 65537
|
||||
sandbox_gid: int = 0
|
||||
|
||||
def set_sandbox_gid(self, gid: int):
|
||||
"""Update sandbox GID dynamically"""
|
||||
self.sandbox_gid = gid
|
||||
|
||||
def override_with_env(self):
|
||||
"""Override configuration with environment variables"""
|
||||
env_map = {
|
||||
"DEBUG": ("app.debug", lambda v: v.lower() in ("true", "1", "yes")),
|
||||
"MAX_WORKERS": ("max_workers", int),
|
||||
"MAX_REQUESTS": ("max_requests", int),
|
||||
"SANDBOX_PORT": ("app.port", int),
|
||||
"WORKER_TIMEOUT": ("worker_timeout", int),
|
||||
"API_KEY": ("app.key", str),
|
||||
"NODEJS_PATH": ("nodejs_path", str),
|
||||
"ENABLE_NETWORK": ("enable_network", lambda v: v.lower() in ("true", "1", "yes")),
|
||||
"ENABLE_PRELOAD": ("enable_preload", lambda v: v.lower() in ("true", "1", "yes")),
|
||||
"ALLOWED_SYSCALLS": ("allowed_syscalls", lambda v: [int(x) for x in v.split(",")]),
|
||||
"SOCKS5_PROXY": ("proxy.socks5", str),
|
||||
"HTTP_PROXY": ("proxy.http", str),
|
||||
"HTTPS_PROXY": ("proxy.https", str),
|
||||
"PYTHON_PATH": ("python_path", str),
|
||||
"PYTHON_LIB_PATH": ("python_lib_paths", lambda v: v.split(",")),
|
||||
"PYTHON_DEPS_UPDATE_INTERVAL": ("python_deps_update_interval", str),
|
||||
"NODEJS_LIB_PATH": ("nodejs_lib_paths", lambda v: v.split(",")),
|
||||
}
|
||||
|
||||
for env_var, (attr_path, cast) in env_map.items():
|
||||
value = os.getenv(env_var)
|
||||
if value is not None:
|
||||
# Support nested attributes like 'app.debug'
|
||||
parts = attr_path.split(".")
|
||||
obj = self
|
||||
for part in parts[:-1]:
|
||||
obj = getattr(obj, part)
|
||||
setattr(obj, parts[-1], cast(value))
|
||||
|
||||
|
||||
# Global configuration instance
|
||||
_config: Optional[Config] = None
|
||||
|
||||
|
||||
def load_config(config_path: str) -> Config:
|
||||
"""Load configuration from YAML file"""
|
||||
def load_config(config_path: str = "config.yaml") -> Config:
|
||||
"""Load configuration from YAML file and override with env variables"""
|
||||
global _config
|
||||
|
||||
# Load from file
|
||||
if os.path.exists(config_path):
|
||||
with open(config_path, 'r') as f:
|
||||
data = yaml.safe_load(f)
|
||||
data = yaml.safe_load(f) or {}
|
||||
_config = Config(**data)
|
||||
else:
|
||||
_config = Config()
|
||||
|
||||
# Override with environment variables
|
||||
if os.getenv("DEBUG"):
|
||||
_config.app.debug = os.getenv("DEBUG").lower() in ("true", "1", "yes")
|
||||
|
||||
if os.getenv("MAX_WORKERS"):
|
||||
_config.max_workers = int(os.getenv("MAX_WORKERS"))
|
||||
|
||||
if os.getenv("MAX_REQUESTS"):
|
||||
_config.max_requests = int(os.getenv("MAX_REQUESTS"))
|
||||
|
||||
if os.getenv("SANDBOX_PORT"):
|
||||
_config.app.port = int(os.getenv("SANDBOX_PORT"))
|
||||
|
||||
if os.getenv("WORKER_TIMEOUT"):
|
||||
_config.worker_timeout = int(os.getenv("WORKER_TIMEOUT"))
|
||||
|
||||
if os.getenv("API_KEY"):
|
||||
_config.app.key = os.getenv("API_KEY")
|
||||
|
||||
if os.getenv("NODEJS_PATH"):
|
||||
_config.nodejs_path = os.getenv("NODEJS_PATH")
|
||||
|
||||
if os.getenv("ENABLE_NETWORK"):
|
||||
_config.enable_network = os.getenv("ENABLE_NETWORK").lower() in ("true", "1", "yes")
|
||||
|
||||
if os.getenv("ENABLE_PRELOAD"):
|
||||
_config.enable_preload = os.getenv("ENABLE_PRELOAD").lower() in ("true", "1", "yes")
|
||||
|
||||
if os.getenv("ALLOWED_SYSCALLS"):
|
||||
_config.allowed_syscalls = [int(x) for x in os.getenv("ALLOWED_SYSCALLS").split(",")]
|
||||
|
||||
if os.getenv("SOCKS5_PROXY"):
|
||||
_config.proxy.socks5 = os.getenv("SOCKS5_PROXY")
|
||||
|
||||
if os.getenv("HTTP_PROXY"):
|
||||
_config.proxy.http = os.getenv("HTTP_PROXY")
|
||||
|
||||
if os.getenv("HTTPS_PROXY"):
|
||||
_config.proxy.https = os.getenv("HTTPS_PROXY")
|
||||
|
||||
# python
|
||||
if os.getenv("PYTHON_PATH"):
|
||||
_config.python_path = os.getenv("PYTHON_PATH")
|
||||
|
||||
if os.getenv("PYTHON_LIB_PATH"):
|
||||
_config.python_lib_paths = os.getenv("PYTHON_LIB_PATH").split(',')
|
||||
|
||||
if os.getenv("PYTHON_DEPS_UPDATE_INTERVAL"):
|
||||
_config.python_deps_update_interval = os.getenv("PYTHON_DEPS_UPDATE_INTERVAL")
|
||||
|
||||
# Override from environment
|
||||
_config.override_with_env()
|
||||
return _config
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user