From c072b1b5dfafe8cf067e2849eb051bf182db404f Mon Sep 17 00:00:00 2001 From: Angus Comrie Date: Thu, 25 Nov 2021 16:29:53 +0200 Subject: [PATCH] Angus/backport 98 (#102) * backport #98 * version bump --- config/config_schema.json | 5 ++++ docs/src/configuration.rst | 5 +++- package-lock.json | 5 ++-- package.json | 2 +- src/controllerTests.ts | 51 ++++++++++++++++++++++++-------------- src/serverHandlers.ts | 35 ++++++++++++++++++-------- src/types.ts | 5 +++- 7 files changed, 73 insertions(+), 35 deletions(-) diff --git a/config/config_schema.json b/config/config_schema.json index ea5a4d5..85729e2 100644 --- a/config/config_schema.json +++ b/config/config_schema.json @@ -455,6 +455,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/package-lock.json b/package-lock.json index 5385a90..68e99cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,12 @@ { "name": "carta-controller", - "version": "2.0.4", + "version": "2.0.5", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "2.0.4", + "name": "carta-controller", + "version": "2.0.5", "license": "ISC", "dependencies": { "@pm2/io": "^5.0.0", diff --git a/package.json b/package.json index 70c408d..5f135d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "carta-controller", - "version": "2.0.4", + "version": "2.0.5", "description": "NodeJS-based controller for CARTA", "repository": "https://github.com/CARTAvis/carta-controller", "homepage": "https://www.cartavis.org", diff --git a/src/controllerTests.ts b/src/controllerTests.ts index b37278a..b2930a0 100644 --- a/src/controllerTests.ts +++ b/src/controllerTests.ts @@ -28,23 +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) { - const logLocation = ServerConfig.logFileTemplate - .replace("{username}", username) - .replace("{pid}", "9999") - .replace("{datetime}", moment().format("YYYYMMDD.h_mm_ss")); +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) { @@ -167,25 +165,40 @@ function testFrontend() { async function testBackendStartup(username: string) { const port = ServerConfig.backendPorts.max - 1; - let args = [ - "--preserve-env=CARTA_AUTH_TOKEN", - "-u", `${username}`, + + 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), - ]; + "--no_http", + "--debug_no_auth", + "--port", + `${port}`, + "--top_level_folder", + 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 3e9f724..33a663e 100644 --- a/src/serverHandlers.ts +++ b/src/serverHandlers.ts @@ -179,21 +179,34 @@ async function startServer(username: string) { throw {statusCode: 500, message: "No available ports for the backend process"}; } - let args = [ - "--preserve-env=CARTA_AUTH_TOKEN", - "-u", `${username}`, + 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), - ]; + "--no_http", + "--port", + `${port}`, + "--top_level_folder", + 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); @@ -241,7 +254,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 @@ -256,7 +269,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 fb2a96d..060142a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -65,8 +65,11 @@ export interface CartaServerConfig { backendPorts: { 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;