Skip to content

Commit cfc453e

Browse files
fix: Fix starting a workspace when user's container does not contain required openssl version
Signed-off-by: Roman Nikitenko <rnikiten@redhat.com> Assisted-by: Cursor AI
1 parent dde8b6d commit cfc453e

7 files changed

Lines changed: 149 additions & 64 deletions

File tree

build/dockerfiles/linux-libc-ubi9.Dockerfile

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,12 @@ RUN NODE_ARCH=$(echo "console.log(process.arch)" | node) \
8080
&& VSCODE_MANGLE_WORKERS=2 NODE_OPTIONS="--max-old-space-size=8192" ./node_modules/.bin/gulp vscode-reh-web-linux-${NODE_ARCH}-min \
8181
&& cp -r ../vscode-reh-web-linux-${NODE_ARCH} /checode \
8282
# cache shared libs from this image to provide them to a user's container
83-
&& mkdir -p /checode/ld_libs \
84-
&& find /usr/lib64 -name 'libbrotli*' 2>/dev/null | xargs -I {} cp -t /checode/ld_libs {} \
85-
&& find /usr/lib64 -name 'libnode.so*' -exec cp -P -t /checode/ld_libs/ {} + \
86-
&& find /usr/lib64 -name 'libz.so*' -exec cp -P -t /checode/ld_libs/ {} +
83+
&& mkdir -p /checode/ld_libs/core /checode/ld_libs/openssl \
84+
&& find /usr/lib64 -name 'libbrotli*' -exec cp -P -t /checode/ld_libs/core/ {} + \
85+
&& find /usr/lib64 -name 'libnode.so*' -exec cp -P -t /checode/ld_libs/core/ {} + \
86+
&& find /usr/lib64 -name 'libz.so*' -exec cp -P -t /checode/ld_libs/core/ {} + \
87+
&& find /usr/lib64 -name 'libssl.so*' -exec cp -P -t /checode/ld_libs/openssl/ {} + \
88+
&& find /usr/lib64 -name 'libcrypto.so*' -exec cp -P -t /checode/ld_libs/openssl/ {} +
8789

8890
RUN chmod a+x /checode/out/server-main.js \
8991
&& chgrp -R 0 /checode && chmod -R g+rwX /checode

build/scripts/entrypoint-volume.sh

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ get_openssl_version() {
5050
openssl_version=$(openssl version -v | cut -d' ' -f2 | cut -d'.' -f1)
5151
elif command -v rpm >/dev/null 2>&1; then
5252
echo "[INFO] rpm command is available"
53-
openssl_version=$(rpm -qa | grep openssl-libs | cut -d'-' -f3 | cut -d'.' -f1)
53+
openssl_version=$(rpm -q --qf '%{VERSION}\n' openssl-libs 2>/dev/null | cut -d'.' -f1)
5454
else
5555
echo "[INFO] openssl and rpm commands are not available, trying to detect OpenSSL version..."
5656
get_libssl_version
@@ -73,48 +73,59 @@ ls -la /checode/
7373
export MACHINE_EXEC_PORT=3333
7474
nohup /checode/bin/machine-exec --url "127.0.0.1:${MACHINE_EXEC_PORT}" &
7575

76-
# Start the checode component based on musl or libc
77-
78-
# detect if we're using alpine/musl
79-
libc=$(ldd /bin/ls | grep 'musl' | head -1 | cut -d ' ' -f1)
80-
if [ -n "$libc" ]; then
81-
echo "[INFO] Using linux-musl assembly..."
82-
cd /checode/checode-linux-musl || exit
83-
else
76+
runtime_ld_library_path=""
8477

78+
# detect if we're using alpine/musl (avoid grep dependency for micro images)
79+
ldd_output=$(ldd /bin/ls 2>/dev/null || true)
80+
case "$ldd_output" in
81+
*musl*)
82+
echo "[INFO] Using linux-musl assembly..."
83+
cd /checode/checode-linux-musl || exit
84+
;;
85+
*)
86+
8587
get_openssl_version
8688
echo "[INFO] OpenSSL major version is: $openssl_version."
8789

8890
case "${openssl_version}" in
8991
*"1"*)
90-
export LD_LIBRARY_PATH="/checode/checode-linux-libc/ubi8/ld_libs:$LD_LIBRARY_PATH"
91-
echo "[INFO] LD_LIBRARY_PATH is: $LD_LIBRARY_PATH"
92-
9392
echo "[INFO] Using linux-libc ubi8-based assembly..."
93+
runtime_ld_library_path="/checode/checode-linux-libc/ubi8/ld_libs"
9494
cd /checode/checode-linux-libc/ubi8 || exit
9595
;;
9696
*"3"*)
97-
export LD_LIBRARY_PATH="/checode/checode-linux-libc/ubi9/ld_libs:$LD_LIBRARY_PATH"
98-
echo "[INFO] LD_LIBRARY_PATH is: $LD_LIBRARY_PATH"
99-
10097
echo "[INFO] Using linux-libc ubi9-based assembly..."
98+
runtime_ld_library_path="/checode/checode-linux-libc/ubi9/ld_libs/core"
99+
if [ -d "/checode/checode-linux-libc/ubi9/ld_libs/openssl" ]; then
100+
runtime_ld_library_path="/checode/checode-linux-libc/ubi9/ld_libs/openssl:${runtime_ld_library_path}"
101+
fi
101102
cd /checode/checode-linux-libc/ubi9 || exit
102103
;;
103104
*)
104105
echo "[WARNING] Unsupported OpenSSL major version, linux-libc ubi9-based assembly will be used by default..."
105-
106-
export LD_LIBRARY_PATH="/checode/checode-linux-libc/ubi9/ld_libs:$LD_LIBRARY_PATH"
107-
echo "[INFO] LD_LIBRARY_PATH is: $LD_LIBRARY_PATH"
108-
106+
runtime_ld_library_path="/checode/checode-linux-libc/ubi9/ld_libs/core"
107+
if [ -d "/checode/checode-linux-libc/ubi9/ld_libs/openssl" ]; then
108+
runtime_ld_library_path="/checode/checode-linux-libc/ubi9/ld_libs/openssl:${runtime_ld_library_path}"
109+
fi
109110
cd /checode/checode-linux-libc/ubi9 || exit
110111
;;
111112
esac
113+
;;
114+
esac
115+
116+
if [ -n "$runtime_ld_library_path" ]; then
117+
export LD_LIBRARY_PATH="${runtime_ld_library_path}${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
118+
echo "[INFO] LD_LIBRARY_PATH is: $LD_LIBRARY_PATH"
112119
fi
113120

114121
# Set the default path to the serverDataFolderName
115122
# into a persistent volume
116123
export VSCODE_AGENT_FOLDER=/checode/remote
117124

125+
# Controls where LD_LIBRARY_PATH sanitization is applied.
126+
# Supported values: all | none | shellEnv | terminal
127+
export LD_SANITIZE_SCOPE="${LD_SANITIZE_SCOPE:-terminal}"
128+
118129
if [ -z "$VSCODE_NODEJS_RUNTIME_DIR" ]; then
119130
export VSCODE_NODEJS_RUNTIME_DIR="$(pwd)"
120131
fi

code/src/vs/platform/che/utils.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**********************************************************************
2+
* Copyright (c) 2024-2026 Red Hat, Inc.
3+
*
4+
* This program and the accompanying materials are made
5+
* available under the terms of the Eclipse Public License 2.0
6+
* which is available at https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
***********************************************************************/
10+
/* eslint-disable header/header */
11+
12+
import { delimiter } from '../../base/common/path.js';
13+
14+
/**
15+
* Merges the provided values. The first parameter is taken as the basis.
16+
* Items from the second parameter are appended to the main value, duplicates are filtered out.
17+
* For example:
18+
* - the first parameter is: "/checode/checode-linux-libc/ubi9/bin/remote-cli:/usr/local/bin:/usr/bin"
19+
* - the second parameter is: "/go/bin:/usr/bin"
20+
* - the function returns: "/checode/checode-linux-libc/ubi9/bin/remote-cli:/usr/local/bin:/usr/bin:/go/bin",
21+
* - note: "/usr/bin" is filtered out to avoud duplicates in the returned value
22+
*
23+
* @param currentPath current value of the PATH env variable
24+
* @param processEnvPath value of the process.env.PATH env variable
25+
* @returns
26+
* - merged value for the given parameters
27+
* - currentPath if processEnvPath is not provided (undefined or empty string)
28+
*/
29+
export function getResolvedPathEnvVar(currentPath: string, processEnvPath?: string): string {
30+
if (processEnvPath) {
31+
const currentPathArray: string[] = currentPath.split(delimiter);
32+
const processEnvPathArray: string[] = processEnvPath.split(delimiter);
33+
const processPathUniqueItems = processEnvPathArray.filter(path => !currentPathArray.includes(path));
34+
return processPathUniqueItems.length > 0 ? currentPath + delimiter + processPathUniqueItems.join(delimiter) : currentPath;
35+
}
36+
return currentPath;
37+
}
38+
39+
/*
40+
* The following logic was generated using AI assistance (Cursor AI)
41+
* and reviewed by the maintainers.
42+
*/
43+
export type LdSanitizeScope = 'all' | 'none' | 'shellEnv' | 'terminal';
44+
45+
const allLdLibPrefixes = new Set<string>([
46+
'/checode/checode-linux-libc/ubi8/ld_libs',
47+
'/checode/checode-linux-libc/ubi9/ld_libs/core',
48+
'/checode/checode-linux-libc/ubi9/ld_libs/openssl'
49+
]);
50+
51+
export function getLdSanitizeScope(): LdSanitizeScope {
52+
const scope = process.env['LD_SANITIZE_SCOPE'];
53+
if (scope === 'all' || scope === 'none' || scope === 'shellEnv' || scope === 'terminal') {
54+
return scope;
55+
}
56+
return 'terminal';
57+
}
58+
59+
export function shouldStripLdLibraryPathForShellEnv(): boolean {
60+
const scope = getLdSanitizeScope();
61+
return scope === 'all' || scope === 'shellEnv';
62+
}
63+
64+
export function shouldStripLdLibraryPathForTerminal(): boolean {
65+
const scope = getLdSanitizeScope();
66+
return scope === 'all' || scope === 'terminal';
67+
}
68+
69+
export function stripLdLibraryPath(value: string | undefined): string | undefined {
70+
if (!value) {
71+
return undefined;
72+
}
73+
74+
const filtered = value
75+
.split(':')
76+
.map(entry => entry.trim())
77+
.filter(entry => entry.length > 0 && !allLdLibPrefixes.has(entry));
78+
79+
return filtered.length > 0 ? filtered.join(':') : undefined;
80+
}
81+
82+
export function sanitizeLdLibraryPathInEnvironment(
83+
environment: NodeJS.ProcessEnv,
84+
logger?: (message: string) => void,
85+
source = 'unknown'
86+
): void {
87+
const before = environment['LD_LIBRARY_PATH'];
88+
const sanitizedLdLibraryPath = stripLdLibraryPath(environment['LD_LIBRARY_PATH']);
89+
if (sanitizedLdLibraryPath) {
90+
environment['LD_LIBRARY_PATH'] = sanitizedLdLibraryPath;
91+
} else {
92+
delete environment['LD_LIBRARY_PATH'];
93+
}
94+
logger?.(`[che-code][ld-sanitize][${source}] scope=${getLdSanitizeScope()} before=${before ?? '<unset>'} after=${environment['LD_LIBRARY_PATH'] ?? '<unset>'}`);
95+
}

code/src/vs/platform/shell/node/shellEnv.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { ILogService } from '../../log/common/log.js';
1818
import { Promises } from '../../../base/common/async.js';
1919
import { IConfigurationService } from '../../configuration/common/configuration.js';
2020
import { clamp } from '../../../base/common/numbers.js';
21+
import { sanitizeLdLibraryPathInEnvironment, shouldStripLdLibraryPathForShellEnv } from '../../che/utils.js';
2122

2223
let unixShellEnvPromise: Promise<typeof process.env> | undefined = undefined;
2324

@@ -109,7 +110,7 @@ async function doResolveUnixShellEnv(logService: ILogService, token: Cancellatio
109110
const mark = generateUuid().replace(/-/g, '').substr(0, 12);
110111
const regex = new RegExp(mark + '({.*})' + mark);
111112

112-
const env = {
113+
const env: NodeJS.ProcessEnv = {
113114
...process.env,
114115
ELECTRON_RUN_AS_NODE: '1',
115116
ELECTRON_NO_ATTACH_CONSOLE: '1',
@@ -207,6 +208,9 @@ async function doResolveUnixShellEnv(logService: ILogService, token: Cancellatio
207208
}
208209

209210
delete env['VSCODE_RESOLVING_ENVIRONMENT'];
211+
if (shouldStripLdLibraryPathForShellEnv()) {
212+
sanitizeLdLibraryPathInEnvironment(env, message => logService.trace(message), 'shellEnv');
213+
}
210214

211215
// https://github.com/microsoft/vscode/issues/22593#issuecomment-336050758
212216
delete env['XDG_RUNTIME_DIR'];

code/src/vs/server/node/che/utils.ts

Lines changed: 0 additions & 38 deletions
This file was deleted.

code/src/vs/server/node/extensionHostConnection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { IRemoteExtensionHostStartParams } from '../../platform/remote/common/re
1919
import { getResolvedShellEnv } from '../../platform/shell/node/shellEnv.js';
2020
import { IExtensionHostStatusService } from './extensionHostStatusService.js';
2121
import { getNLSConfiguration } from './remoteLanguagePacks.js';
22-
import { getResolvedPathEnvVar } from './che/utils.js';
22+
import { getResolvedPathEnvVar } from '../../platform/che/utils.js';
2323
import { IServerEnvironmentService } from './serverEnvironmentService.js';
2424
import { IPCExtHostConnection, SocketExtHostConnection, writeExtHostConnection } from '../../workbench/services/extensions/common/extensionHostEnv.js';
2525
import { IExtHostReadyMessage, IExtHostReduceGraceTimeMessage, IExtHostSocketMessage } from '../../workbench/services/extensions/common/extensionHostProtocol.js';

code/src/vs/server/node/remoteTerminalChannel.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { IConfigurationService } from '../../platform/configuration/common/confi
3333
import { ILogService } from '../../platform/log/common/log.js';
3434
import { promiseWithResolvers } from '../../base/common/async.js';
3535
import { shouldUseEnvironmentVariableCollection } from '../../platform/terminal/common/terminalEnvironment.js';
36+
import { sanitizeLdLibraryPathInEnvironment, shouldStripLdLibraryPathForTerminal } from '../../platform/che/utils.js';
3637

3738
class CustomVariableResolver extends AbstractVariableResolverService {
3839
constructor(
@@ -208,6 +209,9 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel<
208209

209210

210211
const baseEnv = await buildUserEnvironment(args.resolverEnv, !!args.shellLaunchConfig.useShellEnvironment, platform.language, this._environmentService, this._logService, this._configurationService);
212+
if (shouldStripLdLibraryPathForTerminal()) {
213+
sanitizeLdLibraryPathInEnvironment(baseEnv, message => this._logService.trace(message), 'remoteTerminalChannel:baseEnv');
214+
}
211215
this._logService.trace('baseEnv', baseEnv);
212216

213217
const reviveWorkspaceFolder = (workspaceData: IWorkspaceFolderData): IWorkspaceFolder => {
@@ -240,6 +244,9 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel<
240244
args.configuration['terminal.integrated.detectLocale'],
241245
baseEnv
242246
);
247+
if (shouldStripLdLibraryPathForTerminal()) {
248+
sanitizeLdLibraryPathInEnvironment(env, message => this._logService.trace(message), 'remoteTerminalChannel:terminalEnv');
249+
}
243250

244251
// Apply extension environment variable collections to the environment
245252
if (shouldUseEnvironmentVariableCollection(shellLaunchConfig)) {
@@ -328,7 +335,11 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel<
328335
}
329336

330337
private _getEnvironment(): platform.IProcessEnvironment {
331-
return { ...process.env };
338+
const env: platform.IProcessEnvironment = { ...process.env };
339+
if (shouldStripLdLibraryPathForTerminal()) {
340+
sanitizeLdLibraryPathInEnvironment(env, message => this._logService.trace(message), 'remoteTerminalChannel:getEnvironment');
341+
}
342+
return env;
332343
}
333344

334345
private _getWslPath(original: string, direction: 'unix-to-win' | 'win-to-unix'): Promise<string> {

0 commit comments

Comments
 (0)