fix: set nsjail time_limit from job timeout so configured defaults are respected (#8389)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hugocasa
2026-03-16 13:06:31 +01:00
committed by GitHub
parent 2907084ca6
commit 65a92d9899
25 changed files with 94 additions and 26 deletions
@@ -3,6 +3,7 @@ name: "ansible run script"
mode: ONCE
hostname: "ansible"
log_level: ERROR
time_limit: {TIMEOUT}
rlimit_as: 4096
rlimit_cpu: 1000
@@ -3,6 +3,7 @@ name: "bash run script"
mode: ONCE
hostname: "bash"
log_level: ERROR
time_limit: {TIMEOUT}
disable_rl: true
@@ -3,6 +3,7 @@ name: "{LANG} run script"
mode: ONCE
hostname: "{LANG}"
log_level: ERROR
time_limit: {TIMEOUT}
disable_rl: true
@@ -3,6 +3,7 @@ name: "csharp run script"
mode: ONCE
hostname: "csharp"
log_level: ERROR
time_limit: {TIMEOUT}
disable_rl: true
@@ -3,6 +3,7 @@ name: "go run script"
mode: ONCE
hostname: "go"
log_level: ERROR
time_limit: {TIMEOUT}
disable_rl: true
@@ -3,6 +3,7 @@ name: "java run script"
mode: ONCE
hostname: "java"
log_level: ERROR
time_limit: {TIMEOUT}
disable_rl: true
@@ -3,6 +3,7 @@ name: "nu run script"
mode: ONCE
hostname: "nu"
log_level: ERROR
time_limit: {TIMEOUT}
disable_rl: true
@@ -3,6 +3,7 @@ name: "php run script"
mode: ONCE
hostname: "php"
log_level: ERROR
time_limit: {TIMEOUT}
disable_rl: true
@@ -3,6 +3,7 @@ name: "powershell run script"
mode: ONCE
hostname: "powershell"
log_level: ERROR
time_limit: {TIMEOUT}
disable_rl: true
@@ -3,6 +3,7 @@ name: "python run script"
mode: ONCE
hostname: "python"
log_level: ERROR
time_limit: {TIMEOUT}
rlimit_as: 4096
rlimit_cpu: 1000
@@ -3,6 +3,7 @@ name: "ruby run script"
mode: ONCE
hostname: "ruby"
log_level: ERROR
time_limit: {TIMEOUT}
disable_rl: true
@@ -3,6 +3,7 @@ name: "rust run script"
mode: ONCE
hostname: "rust"
log_level: ERROR
time_limit: {TIMEOUT}
disable_rl: true
@@ -30,7 +30,8 @@ use crate::{
bash_executor::BIN_BASH,
common::{
build_command_with_isolation, check_executor_binary_exists, get_reserved_variables,
read_and_check_result, start_child_process, transform_json, OccupancyMetrics,
read_and_check_result, resolve_nsjail_timeout, start_child_process, transform_json,
OccupancyMetrics,
},
handle_child::handle_child,
is_sandboxing_enabled,
@@ -1180,6 +1181,8 @@ mount {{
)
})
.join("\n");
let nsjail_timeout =
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
let _ = write_file(
job_dir,
"run.config.proto",
@@ -1193,7 +1196,8 @@ mount {{
.replace(
"{ADDITIONAL_PYTHON_PATHS}",
additional_python_paths_folders.as_str(),
),
)
.replace("{TIMEOUT}", &nsjail_timeout),
)?;
} else {
reserved_variables.insert("PYTHONPATH".to_string(), additional_python_paths_folders);
+6 -2
View File
@@ -41,7 +41,8 @@ use crate::handle_child::run_future_with_polling_update_job_poller;
use crate::{
common::{
build_args_map, build_command_with_isolation, get_reserved_variables, read_file,
read_file_content, start_child_process, OccupancyMetrics, DEV_CONF_NSJAIL,
read_file_content, resolve_nsjail_timeout, start_child_process, OccupancyMetrics,
DEV_CONF_NSJAIL,
},
get_proxy_envs_for_lang,
handle_child::handle_child,
@@ -192,6 +193,8 @@ exit $exit_status
// Use nsjail if globally enabled OR if script has #sandbox annotation
let nsjail = (is_sandboxing_enabled() || annotation.sandbox) && is_regular_job;
let child = if nsjail {
let nsjail_timeout =
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
let _ = write_file(
job_dir,
"run.config.proto",
@@ -200,7 +203,8 @@ exit $exit_status
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
.replace("{SHARED_MOUNT}", shared_mount)
.replace("{TRACING_PROXY_CA_CERT_PATH}", &*TRACING_PROXY_CA_CERT_PATH)
.replace("#{DEV}", DEV_CONF_NSJAIL),
.replace("#{DEV}", DEV_CONF_NSJAIL)
.replace("{TIMEOUT}", &nsjail_timeout),
)?;
let mut cmd_args = vec![
"--config",
+7 -3
View File
@@ -15,8 +15,9 @@ use windmill_queue::{append_logs, CanceledBy, MiniPulledJob, PrecomputedAgentInf
use crate::{
common::{
build_command_with_isolation, create_args_and_out_file, get_reserved_variables,
parse_npm_config, read_file, read_file_content, read_result, start_child_process,
write_file_binary, MaybeLock, OccupancyMetrics, StreamNotifier, DEV_CONF_NSJAIL,
parse_npm_config, read_file, read_file_content, read_result, resolve_nsjail_timeout,
start_child_process, write_file_binary, MaybeLock, OccupancyMetrics, StreamNotifier,
DEV_CONF_NSJAIL,
},
get_proxy_envs_for_lang,
handle_child::handle_child,
@@ -1774,6 +1775,8 @@ try {{
//do not cache local dependencies
let child = if is_sandboxing_enabled() || annotation.sandbox {
let nsjail_timeout =
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
let _ = write_file(
job_dir,
"run.config.proto",
@@ -1793,7 +1796,8 @@ try {{
),
)
.replace("{TRACING_PROXY_CA_CERT_PATH}", &*TRACING_PROXY_CA_CERT_PATH)
.replace("#{DEV}", DEV_CONF_NSJAIL),
.replace("#{DEV}", DEV_CONF_NSJAIL)
.replace("{TIMEOUT}", &nsjail_timeout),
)?;
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
+11
View File
@@ -805,6 +805,17 @@ pub async fn resolve_job_timeout(
}
}
/// Compute the nsjail timeout (in seconds) with a 15s buffer so handle_child fires first.
pub async fn resolve_nsjail_timeout(
conn: &Connection,
w_id: &str,
job_id: Uuid,
custom_timeout: Option<i32>,
) -> String {
let (duration, _, _) = resolve_job_timeout(conn, w_id, job_id, custom_timeout).await;
(duration.as_secs() + 15).to_string()
}
async fn hash_args(
#[allow(unused)] db: &DB,
#[allow(unused)] client: &AuthedClient,
@@ -27,7 +27,8 @@ use windmill_queue::CanceledBy;
use crate::{
common::{
build_command_with_isolation, check_executor_binary_exists, create_args_and_out_file,
get_reserved_variables, read_result, start_child_process, DEV_CONF_NSJAIL,
get_reserved_variables, read_result, resolve_nsjail_timeout, start_child_process,
DEV_CONF_NSJAIL,
},
get_proxy_envs_for_lang,
handle_child::handle_child,
@@ -582,6 +583,8 @@ pub async fn handle_csharp_job(
get_reserved_variables(job, &client.token, conn, parent_runnable_path).await?;
let child = if is_sandboxing_enabled() {
let nsjail_timeout =
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
write_file(
job_dir,
"run.config.proto",
@@ -592,7 +595,8 @@ pub async fn handle_csharp_job(
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
.replace("{SHARED_MOUNT}", shared_mount)
.replace("{TRACING_PROXY_CA_CERT_PATH}", &*TRACING_PROXY_CA_CERT_PATH)
.replace("#{DEV}", DEV_CONF_NSJAIL),
.replace("#{DEV}", DEV_CONF_NSJAIL)
.replace("{TIMEOUT}", &nsjail_timeout),
)?;
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
nsjail_cmd
+6 -2
View File
@@ -22,7 +22,8 @@ use windmill_queue::{append_logs, CanceledBy, MiniPulledJob};
use crate::{
common::{
build_command_with_isolation, capitalize, create_args_and_out_file, get_reserved_variables,
read_result, start_child_process, OccupancyMetrics, DEV_CONF_NSJAIL,
read_result, resolve_nsjail_timeout, start_child_process, OccupancyMetrics,
DEV_CONF_NSJAIL,
},
handle_child::handle_child,
is_sandboxing_enabled, read_ee_registry, DISABLE_NUSER, GOPRIVATE, GOPROXY, GO_BIN_CACHE_DIR,
@@ -338,6 +339,8 @@ func Run(req Req) (interface{{}}, error){{
get_reserved_variables(job, &client.token, conn, parent_runnable_path).await?;
let child = if is_sandboxing_enabled() {
let nsjail_timeout =
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
let _ = write_file(
job_dir,
"run.config.proto",
@@ -346,7 +349,8 @@ func Run(req Req) (interface{{}}, error){{
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
.replace("{SHARED_MOUNT}", shared_mount)
.replace("{TRACING_PROXY_CA_CERT_PATH}", &*TRACING_PROXY_CA_CERT_PATH)
.replace("#{DEV}", DEV_CONF_NSJAIL),
.replace("#{DEV}", DEV_CONF_NSJAIL)
.replace("{TIMEOUT}", &nsjail_timeout),
)?;
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
nsjail_cmd
+5 -2
View File
@@ -23,7 +23,7 @@ use windmill_queue::{append_logs, CanceledBy, MiniPulledJob};
use crate::{
common::{
build_command_with_isolation, create_args_and_out_file, get_reserved_variables,
read_result, start_child_process, OccupancyMetrics,
read_result, resolve_nsjail_timeout, start_child_process, OccupancyMetrics,
},
handle_child, is_sandboxing_enabled, read_ee_registry,
universal_pkg_installer::{par_install_language_dependencies_all_at_once, RequiredDependency},
@@ -600,6 +600,8 @@ async fn run<'a>(
)
.await;
let nsjail_timeout =
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
write_file(
job_dir,
"run.config.proto",
@@ -608,7 +610,8 @@ async fn run<'a>(
.replace("{CACHE_DIR}", &*JAVA_CACHE_DIR)
.replace("{SHARED_MOUNT}", &shared_mount)
// .replace("{CACHED_TARGET}", &shared_mount)
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string()),
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
.replace("{TIMEOUT}", &nsjail_timeout),
)?;
let mut cmd = Command::new(NSJAIL_PATH.as_str());
cmd.env_clear()
+6 -2
View File
@@ -14,7 +14,8 @@ use windmill_queue::{append_logs, CanceledBy, MiniPulledJob};
use crate::{
common::{
build_command_with_isolation, create_args_and_out_file, get_reserved_variables,
read_result, start_child_process, OccupancyMetrics, DEV_CONF_NSJAIL,
read_result, resolve_nsjail_timeout, start_child_process, OccupancyMetrics,
DEV_CONF_NSJAIL,
},
get_proxy_envs_for_lang, handle_child, is_sandboxing_enabled, DISABLE_NUSER, NSJAIL_PATH,
PATH_ENV, TRACING_PROXY_CA_CERT_PATH,
@@ -245,6 +246,8 @@ async fn run<'a>(
)
.await;
let nsjail_timeout =
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
write_file(
job_dir,
"run.config.proto",
@@ -254,7 +257,8 @@ async fn run<'a>(
.replace("{SHARED_MOUNT}", &shared_mount)
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
.replace("{TRACING_PROXY_CA_CERT_PATH}", &*TRACING_PROXY_CA_CERT_PATH)
.replace("#{DEV}", DEV_CONF_NSJAIL),
.replace("#{DEV}", DEV_CONF_NSJAIL)
.replace("{TIMEOUT}", &nsjail_timeout),
)?;
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
nsjail_cmd
+6 -2
View File
@@ -19,7 +19,8 @@ use windmill_queue::{append_logs, CanceledBy};
use crate::{
common::{
build_command_with_isolation, check_executor_binary_exists, create_args_and_out_file,
get_reserved_variables, read_result, start_child_process, MaybeLock, OccupancyMetrics,
get_reserved_variables, read_result, resolve_nsjail_timeout, start_child_process,
MaybeLock, OccupancyMetrics,
},
handle_child::handle_child,
is_sandboxing_enabled, COMPOSER_CACHE_DIR, COMPOSER_PATH, DISABLE_NUSER, NSJAIL_PATH, PHP_PATH,
@@ -294,13 +295,16 @@ try {{
let (reserved_variables, _) = tokio::try_join!(reserved_variables_args_out_f, write_wrapper_f)?;
let child = if is_sandboxing_enabled() {
let nsjail_timeout =
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
let _ = write_file(
job_dir,
"run.config.proto",
&NSJAIL_CONFIG_RUN_PHP_CONTENT
.replace("{JOB_DIR}", job_dir)
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
.replace("{SHARED_MOUNT}", shared_mount),
.replace("{SHARED_MOUNT}", shared_mount)
.replace("{TIMEOUT}", &nsjail_timeout),
)?;
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
+5 -2
View File
@@ -24,7 +24,7 @@ lazy_static::lazy_static! {
use crate::{
common::{
build_args_map, build_command_with_isolation, get_reserved_variables, read_file,
read_file_content, start_child_process, OccupancyMetrics,
read_file_content, resolve_nsjail_timeout, start_child_process, OccupancyMetrics,
},
handle_child::handle_child,
is_sandboxing_enabled, read_ee_registry, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV,
@@ -542,6 +542,8 @@ $env:PSModulePath = \"{};$PSModulePathBackup\"",
let nsjail = is_sandboxing_enabled() && is_regular_job;
let child = if nsjail {
let nsjail_timeout =
resolve_nsjail_timeout(db, &job.workspace_id, job.id, job.timeout).await;
let _ = write_file(
job_dir,
"run.config.proto",
@@ -549,7 +551,8 @@ $env:PSModulePath = \"{};$PSModulePathBackup\"",
.replace("{JOB_DIR}", job_dir)
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
.replace("{SHARED_MOUNT}", shared_mount)
.replace("{CACHE_DIR}", &*POWERSHELL_CACHE_DIR),
.replace("{CACHE_DIR}", &*POWERSHELL_CACHE_DIR)
.replace("{TIMEOUT}", &nsjail_timeout),
)?;
let cmd_args = vec![
"--config",
@@ -128,7 +128,8 @@ use windmill_object_store::OBJECT_STORE_SETTINGS;
use crate::{
common::{
build_command_with_isolation, create_args_and_out_file, get_reserved_variables, read_file,
read_result, start_child_process, OccupancyMetrics, StreamNotifier, DEV_CONF_NSJAIL,
read_result, resolve_nsjail_timeout, start_child_process, OccupancyMetrics, StreamNotifier,
DEV_CONF_NSJAIL,
},
get_proxy_envs_for_lang,
handle_child::handle_child,
@@ -875,6 +876,8 @@ mount {{
)
})
.join("\n");
let nsjail_timeout =
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
let _ = write_file(
job_dir,
"run.config.proto",
@@ -891,7 +894,8 @@ mount {{
additional_python_paths_folders.as_str(),
)
.replace("{TRACING_PROXY_CA_CERT_PATH}", &*TRACING_PROXY_CA_CERT_PATH)
.replace("#{DEV}", DEV_CONF_NSJAIL),
.replace("#{DEV}", DEV_CONF_NSJAIL)
.replace("{TIMEOUT}", &nsjail_timeout),
)?;
} else {
reserved_variables.insert("PYTHONPATH".to_string(), additional_python_paths_folders);
+6 -2
View File
@@ -23,7 +23,8 @@ use windmill_queue::{append_logs, CanceledBy, MiniPulledJob};
use crate::{
common::{
build_command_with_isolation, create_args_and_out_file, get_reserved_variables,
read_result, start_child_process, OccupancyMetrics, DEV_CONF_NSJAIL,
read_result, resolve_nsjail_timeout, start_child_process, OccupancyMetrics,
DEV_CONF_NSJAIL,
},
get_proxy_envs_for_lang,
handle_child::{self},
@@ -792,6 +793,8 @@ mount {{
})
.join("\n");
let nsjail_timeout =
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
write_file(
job_dir,
"run.config.proto",
@@ -801,7 +804,8 @@ mount {{
.replace("{SHARED_DEPENDENCIES}", &shared_deps)
.replace("{TRACING_PROXY_CA_CERT_PATH}", &*TRACING_PROXY_CA_CERT_PATH)
.replace("#{DEV}", DEV_CONF_NSJAIL)
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string()),
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
.replace("{TIMEOUT}", &nsjail_timeout),
)?;
let mut cmd = Command::new(NSJAIL_PATH.as_str());
cmd.env_clear()
+6 -3
View File
@@ -23,8 +23,8 @@ use windmill_queue::{append_logs, CanceledBy};
use crate::{
common::{
build_command_with_isolation, check_executor_binary_exists, create_args_and_out_file,
get_reserved_variables, read_result, start_child_process, OccupancyMetrics,
DEV_CONF_NSJAIL,
get_reserved_variables, read_result, resolve_nsjail_timeout, start_child_process,
OccupancyMetrics, DEV_CONF_NSJAIL,
},
get_proxy_envs_for_lang,
handle_child::handle_child,
@@ -682,6 +682,8 @@ pub async fn handle_rust_job(
append_logs(&job.id, &job.workspace_id, logs2, conn).await;
let child = if is_sandboxing_enabled() {
let nsjail_timeout =
resolve_nsjail_timeout(conn, &job.workspace_id, job.id, job.timeout).await;
let _ = write_file(
job_dir,
"run.config.proto",
@@ -692,7 +694,8 @@ pub async fn handle_rust_job(
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
.replace("{TRACING_PROXY_CA_CERT_PATH}", &*TRACING_PROXY_CA_CERT_PATH)
.replace("#{DEV}", DEV_CONF_NSJAIL)
.replace("{SHARED_MOUNT}", shared_mount),
.replace("{SHARED_MOUNT}", shared_mount)
.replace("{TIMEOUT}", &nsjail_timeout),
)?;
let mut nsjail_cmd = Command::new(NSJAIL_PATH.as_str());
nsjail_cmd