summaryrefslogtreecommitdiff
path: root/packages/cli/src/utils/sandbox.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli/src/utils/sandbox.ts')
-rw-r--r--packages/cli/src/utils/sandbox.ts62
1 files changed, 58 insertions, 4 deletions
diff --git a/packages/cli/src/utils/sandbox.ts b/packages/cli/src/utils/sandbox.ts
index b1605184..a70aced1 100644
--- a/packages/cli/src/utils/sandbox.ts
+++ b/packages/cli/src/utils/sandbox.ts
@@ -8,12 +8,67 @@ import { execSync, spawnSync, spawn } from 'node:child_process';
import os from 'node:os';
import path from 'node:path';
import fs from 'node:fs';
+import { readFile } from 'node:fs/promises';
import { quote } from 'shell-quote';
import {
USER_SETTINGS_DIR,
SETTINGS_DIRECTORY_NAME,
} from '../config/settings.js';
+/**
+ * Determines whether the sandbox container should be run with the current user's UID and GID.
+ * This is often necessary on Linux systems (especially Debian/Ubuntu based) when using
+ * rootful Docker without userns-remap configured, to avoid permission issues with
+ * mounted volumes.
+ *
+ * The behavior is controlled by the `SANDBOX_SET_UID_GID` environment variable:
+ * - If `SANDBOX_SET_UID_GID` is "1" or "true", this function returns `true`.
+ * - If `SANDBOX_SET_UID_GID` is "0" or "false", this function returns `false`.
+ * - If `SANDBOX_SET_UID_GID` is not set:
+ * - On Debian/Ubuntu Linux, it defaults to `true`.
+ * - On other OSes, or if OS detection fails, it defaults to `false`.
+ *
+ * For more context on running Docker containers as non-root, see:
+ * https://medium.com/redbubble/running-a-docker-container-as-a-non-root-user-7d2e00f8ee15
+ *
+ * @returns {Promise<boolean>} A promise that resolves to true if the current user's UID/GID should be used, false otherwise.
+ */
+async function shouldUseCurrentUserInSandbox(): Promise<boolean> {
+ const envVar = process.env.SANDBOX_SET_UID_GID?.toLowerCase().trim();
+
+ if (envVar === '1' || envVar === 'true') {
+ return true;
+ }
+ if (envVar === '0' || envVar === 'false') {
+ return false;
+ }
+
+ // If environment variable is not explicitly set, check for Debian/Ubuntu Linux
+ if (os.platform() === 'linux') {
+ try {
+ const osReleaseContent = await readFile('/etc/os-release', 'utf8');
+ if (
+ osReleaseContent.includes('ID=debian') ||
+ osReleaseContent.includes('ID=ubuntu') ||
+ osReleaseContent.match(/^ID_LIKE=.*debian.*/m) || // Covers derivatives
+ osReleaseContent.match(/^ID_LIKE=.*ubuntu.*/m) // Covers derivatives
+ ) {
+ console.log(
+ 'INFO: Defaulting to use current user UID/GID for Debian/Ubuntu-based Linux.',
+ );
+ return true;
+ }
+ } catch (_err) {
+ // Silently ignore if /etc/os-release is not found or unreadable.
+ // The default (false) will be applied in this case.
+ console.warn(
+ 'Warning: Could not read /etc/os-release to auto-detect Debian/Ubuntu for UID/GID default.',
+ );
+ }
+ }
+ return false; // Default to false if no other condition is met
+}
+
// node.js equivalent of scripts/sandbox_command.sh
export function sandbox_command(sandbox?: string | boolean): string {
// note environment variable takes precedence over argument (from command line or settings)
@@ -388,10 +443,9 @@ export async function start_sandbox(sandbox: string) {
args.push('--authfile', emptyAuthFilePath);
}
- // specify --user as "$(id -u):$(id -g)" if SANDBOX_SET_UID_GID is 1|true
- // only necessary if user mapping is not handled by sandboxing setup on host
- // (e.g. rootful docker on linux w/o userns-remap configured)
- if (['1', 'true'].includes(process.env.SANDBOX_SET_UID_GID ?? '')) {
+ // Determine if the current user's UID/GID should be passed to the sandbox.
+ // See shouldUseCurrentUserInSandbox for more details.
+ if (await shouldUseCurrentUserInSandbox()) {
const uid = execSync('id -u').toString().trim();
const gid = execSync('id -g').toString().trim();
args.push('--user', `${uid}:${gid}`);