fix(sandbox): default to current user profile for debian/ubuntu env (#337)

This commit is contained in:
Brandon Keiji 2025-05-13 21:13:54 +00:00 committed by GitHub
parent 0ae59056d1
commit 3be8b6dc34
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 58 additions and 4 deletions

View File

@ -8,12 +8,67 @@ import { execSync, spawnSync, spawn } from 'node:child_process';
import os from 'node:os'; import os from 'node:os';
import path from 'node:path'; import path from 'node:path';
import fs from 'node:fs'; import fs from 'node:fs';
import { readFile } from 'node:fs/promises';
import { quote } from 'shell-quote'; import { quote } from 'shell-quote';
import { import {
USER_SETTINGS_DIR, USER_SETTINGS_DIR,
SETTINGS_DIRECTORY_NAME, SETTINGS_DIRECTORY_NAME,
} from '../config/settings.js'; } 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 // node.js equivalent of scripts/sandbox_command.sh
export function sandbox_command(sandbox?: string | boolean): string { export function sandbox_command(sandbox?: string | boolean): string {
// note environment variable takes precedence over argument (from command line or settings) // 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); args.push('--authfile', emptyAuthFilePath);
} }
// specify --user as "$(id -u):$(id -g)" if SANDBOX_SET_UID_GID is 1|true // Determine if the current user's UID/GID should be passed to the sandbox.
// only necessary if user mapping is not handled by sandboxing setup on host // See shouldUseCurrentUserInSandbox for more details.
// (e.g. rootful docker on linux w/o userns-remap configured) if (await shouldUseCurrentUserInSandbox()) {
if (['1', 'true'].includes(process.env.SANDBOX_SET_UID_GID ?? '')) {
const uid = execSync('id -u').toString().trim(); const uid = execSync('id -u').toString().trim();
const gid = execSync('id -g').toString().trim(); const gid = execSync('id -g').toString().trim();
args.push('--user', `${uid}:${gid}`); args.push('--user', `${uid}:${gid}`);