From 15dd98143bc9e73373edee4ff86c40021ef31ad8 Mon Sep 17 00:00:00 2001 From: Angus Comrie Date: Fri, 5 Nov 2021 14:02:01 +0200 Subject: [PATCH] adjustments to sudo call and log test (#98) * adjustments to sudo call and log test * revert version change * adds preserveEnv config option (defaults to true) * remove env_keep entry from example sudoers conf --- config/config_schema.json | 5 +++++ docs/src/configuration.rst | 5 ++++- src/controllerTests.ts | 38 ++++++++++++++++++++++++-------------- src/serverHandlers.ts | 28 ++++++++++++++++++---------- src/types.ts | 3 +++ 5 files changed, 54 insertions(+), 25 deletions(-) diff --git a/config/config_schema.json b/config/config_schema.json index 8e28841..a1c9046 100644 --- a/config/config_schema.json +++ b/config/config_schema.json @@ -460,6 +460,11 @@ ], "default": "/usr/bin/carta_backend" }, + "preserveEnv": { + "description": "Use the --preserve-env argument when calling sudo", + "type": "boolean", + "default": true + }, "killCommand": { "description": "Path to CARTA kill script", "type": "string", diff --git a/docs/src/configuration.rst b/docs/src/configuration.rst index 2df308e..93f00f4 100644 --- a/docs/src/configuration.rst +++ b/docs/src/configuration.rst @@ -26,7 +26,10 @@ To provide the ``carta`` user with these privileges, you must make modifications .. warning:: Please only edit your sudoers configuration with ``visudo`` or equivalent. - + +.. note:: + Older versions of ``sudo`` do not support the ``--preserve-env=VARIABLE`` argument. If your version of ``sudo`` is too old, set ``"preserveEnv"`` to ``false`` in your controller configuration, and add ``Defaults env_keep += "CARTA_AUTH_TOKEN"`` to your sudoers configuration. + .. _config-authentication: Authentication diff --git a/src/controllerTests.ts b/src/controllerTests.ts index d02927b..82c8650 100644 --- a/src/controllerTests.ts +++ b/src/controllerTests.ts @@ -28,20 +28,21 @@ export async function runTests(username: string) { } await testDatabase(); if (ServerConfig.logFileTemplate) { - testLog(username); + await testLog(username); } testFrontend(); const backendProcess = await testBackendStartup(username); await testKillScript(username, backendProcess); } -function testLog(username: string) { +async function testLog(username: string) { const logLocation = ServerConfig.logFileTemplate.replace("{username}", username).replace("{pid}", "9999").replace("{datetime}", moment().format("YYYYMMDD.h_mm_ss")); try { const logStream = fs.createWriteStream(logLocation, {flags: "a"}); - logStream.write("test"); - logStream.close(); + // Transform callbacks into awaits + await new Promise(res => logStream.write("test", res)); + await new Promise(res => logStream.end(res)); fs.unlinkSync(logLocation); console.log(logSymbols.success, `Checked log writing for user ${username}`); } catch (err) { @@ -163,31 +164,40 @@ function testFrontend() { async function testBackendStartup(username: string) { const port = ServerConfig.backendPorts.max - 1; - let args = [ - "--preserve-env=CARTA_AUTH_TOKEN", + + let args: string[] = []; + if (ServerConfig.preserveEnv) { + args.push("--preserve-env=CARTA_AUTH_TOKEN"); + } + + args = args.concat([ + "-n", // run non-interactively. If password is required, sudo will bail "-u", `${username}`, ServerConfig.processCommand, "--no_http", - "true", "--debug_no_auth", - "true", - "--no_log", - ServerConfig.logFileTemplate ? "true" : "false", "--port", `${port}`, "--top_level_folder", - ServerConfig.rootFolderTemplate.replace("{username}", username), - ServerConfig.baseFolderTemplate.replace("{username}", username) - ]; + ServerConfig.rootFolderTemplate.replace("{username}", username) + ]); + + if (ServerConfig.logFileTemplate) { + args.push("--no_log"); + } if (ServerConfig.additionalArgs) { args = args.concat(ServerConfig.additionalArgs); } + // Finally, add the positional argument for the base folder + args.push(ServerConfig.baseFolderTemplate.replace("{username}", username)); + verboseLog(`running sudo ${args.join(" ")}`); - const backendProcess = spawn("sudo", args); + // Use same stdout and stderr stream for the backend process + const backendProcess = spawn("sudo", args, {stdio: "inherit"}); await delay(2000); if (backendProcess.signalCode) { throw new Error(`Backend process terminated with code ${backendProcess.signalCode}. Please check your sudoers config, processCommand option and additionalArgs section`); diff --git a/src/serverHandlers.ts b/src/serverHandlers.ts index 67b6304..81e2169 100644 --- a/src/serverHandlers.ts +++ b/src/serverHandlers.ts @@ -178,26 +178,34 @@ async function startServer(username: string) { throw {statusCode: 500, message: "No available ports for the backend process"}; } - let args = [ - "--preserve-env=CARTA_AUTH_TOKEN", + let args: string[] = []; + if (ServerConfig.preserveEnv) { + args.push("--preserve-env=CARTA_AUTH_TOKEN"); + } + + args = args.concat([ + "-n", // run non-interactively. If password is required, sudo will bail "-u", `${username}`, ServerConfig.processCommand, "--no_http", - "true", - "--no_log", - ServerConfig.logFileTemplate ? "true" : "false", "--port", `${port}`, "--top_level_folder", - ServerConfig.rootFolderTemplate.replace("{username}", username), - ServerConfig.baseFolderTemplate.replace("{username}", username) - ]; + ServerConfig.rootFolderTemplate.replace("{username}", username) + ]); + + if (ServerConfig.logFileTemplate) { + args.push("--no_log"); + } if (ServerConfig.additionalArgs) { args = args.concat(ServerConfig.additionalArgs); } + // Finally, add the positional argument for the base folder + args.push(ServerConfig.baseFolderTemplate.replace("{username}", username)); + const headerToken = v4(); const child = spawn("sudo", args, {env: {CARTA_AUTH_TOKEN: headerToken}}); setPendingProcess(username, port, headerToken, child); @@ -242,7 +250,7 @@ async function startServer(username: string) { child.on("exit", code => { console.log(`Process ${child.pid} exited with code ${code} and signal ${child.signalCode}`); deleteProcess(username); - logStream?.close(); + logStream?.end(); }); // Check for early exit of backend process @@ -257,7 +265,7 @@ async function startServer(username: string) { } catch (e) { verboseError(e); console.log(`Problem starting process for user ${username}`); - logStream?.close(); + logStream?.end(); if (e.statusCode && e.message) { throw e; } else { diff --git a/src/types.ts b/src/types.ts index 5e08f32..e5b158d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -68,7 +68,10 @@ export interface CartaServerConfig { min: number; max: number; }; + // Command to execute when starting the backend process processCommand: string; + // Use the --preserveEnv argument when calling sudo + preserveEnv: boolean; // The {username} placeholder will be replaced with the username rootFolderTemplate: string; baseFolderTemplate: string;