diff --git a/grove.hml b/grove.hml index c056b8e..db4a712 100644 --- a/grove.hml +++ b/grove.hml @@ -253,12 +253,17 @@ fn load_config() { // Secure mode - enable bubblewrap sandboxing let secure = getenv("GROVE_SECURE_MODE"); if (secure != null && (secure == "1" || to_lower(secure) == "true")) { - if (CONFIG["bwrap_available"]) { - CONFIG["secure_mode"] = true; - } else { + if (!CONFIG["bwrap_available"]) { print("WARNING: GROVE_SECURE_MODE requested but bubblewrap not found"); print(" Install bubblewrap: apt install bubblewrap"); print(" Falling back to basic sandbox mode"); + } else if (!check_user_namespaces()) { + print("WARNING: GROVE_SECURE_MODE requested but user namespaces not available"); + print(" Enable with: sudo sysctl -w kernel.unprivileged_userns_clone=1"); + print(" On Ubuntu 24.04+: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0"); + print(" Falling back to basic sandbox mode"); + } else { + CONFIG["secure_mode"] = true; } } @@ -492,11 +497,13 @@ fn build_bwrap_command(temp_file, stdin_file, timeout_sec) { // Construct bwrap command // Note: Using bash -c to set ulimits inside the sandbox (sh/dash doesn't support -u) + // Note: We don't use --unshare-net because setting up loopback requires CAP_NET_ADMIN + // which unprivileged users don't have. Network restrictions are handled by + // hemlock's --sandbox flag instead. let bwrap_cmd = `"${CONFIG["bwrap_path"]}" ` + // Namespace isolation `--unshare-user ` + `--unshare-pid ` + - `--unshare-net ` + `--unshare-ipc ` + `--unshare-uts ` + `--unshare-cgroup ` + @@ -651,11 +658,11 @@ fn handle_version() { fn build_bwrap_check_command(temp_file) { // Build bubblewrap command for type checking (lighter than execute) // hemlockc only parses, so we can be more restrictive + // Note: We don't use --unshare-net because setting up loopback requires CAP_NET_ADMIN let check_cmd = `"${CONFIG["hemlockc_path"]}" --check "${temp_file}"`; let bwrap_cmd = `"${CONFIG["bwrap_path"]}" ` + `--unshare-user ` + - `--unshare-net ` + `--unshare-ipc ` + `--uid 65534 ` + `--gid 65534 ` + @@ -751,11 +758,11 @@ fn handle_check(body) { fn build_bwrap_format_command(temp_file) { // Build bubblewrap command for formatting (needs write access to temp file) + // Note: We don't use --unshare-net because setting up loopback requires CAP_NET_ADMIN let format_cmd = `"${CONFIG["hemlock_path"]}" format "${temp_file}"`; let bwrap_cmd = `"${CONFIG["bwrap_path"]}" ` + `--unshare-user ` + - `--unshare-net ` + `--unshare-ipc ` + `--uid 65534 ` + `--gid 65534 ` +