From 9c2b9327e5cdce9583ef6cfe5072102b150cf7cb Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 13 Nov 2023 16:44:22 +0100 Subject: [PATCH] Load local certificates (microsoft/vscode-remote-release#9176) --- package.json | 2 +- remote/package.json | 2 +- remote/yarn.lock | 8 ++-- src/vs/platform/native/common/native.ts | 1 + .../electron-main/nativeHostMainService.ts | 5 +++ .../request/browser/requestService.ts | 4 ++ src/vs/platform/request/common/request.ts | 2 + src/vs/platform/request/common/requestIpc.ts | 4 ++ .../platform/request/node/requestService.ts | 5 +++ .../test/common/userDataSyncClient.ts | 1 + .../common/userDataSyncStoreService.test.ts | 3 +- .../api/browser/mainThreadWorkspace.ts | 4 ++ .../workbench/api/common/extHost.protocol.ts | 1 + .../workbench/api/common/extHostWorkspace.ts | 5 +++ src/vs/workbench/api/node/proxyResolver.ts | 37 +++++++++---------- .../electron-sandbox/requestService.ts | 4 ++ .../electron-sandbox/workbenchTestServices.ts | 1 + yarn.lock | 8 ++-- 18 files changed, 66 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index 35a85347013..96eb6ddf07d 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "@parcel/watcher": "2.1.0", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/policy-watcher": "^1.1.4", - "@vscode/proxy-agent": "^0.17.5", + "@vscode/proxy-agent": "^0.18.0", "@vscode/ripgrep": "^1.15.6", "@vscode/spdlog": "^0.13.12", "@vscode/sqlite3": "5.1.6-vscode", diff --git a/remote/package.json b/remote/package.json index 2a4dcf858e1..960a573a1f6 100644 --- a/remote/package.json +++ b/remote/package.json @@ -7,7 +7,7 @@ "@microsoft/1ds-post-js": "^3.2.13", "@parcel/watcher": "2.1.0", "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/proxy-agent": "^0.17.5", + "@vscode/proxy-agent": "^0.18.0", "@vscode/ripgrep": "^1.15.6", "@vscode/spdlog": "^0.13.12", "@vscode/vscode-languagedetection": "1.0.21", diff --git a/remote/yarn.lock b/remote/yarn.lock index c83c555aa64..8e6a3bb9e50 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -58,10 +58,10 @@ resolved "https://registry.yarnpkg.com/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz#d2f1e0664ee6036408f9743fee264ea0699b0e48" integrity sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg== -"@vscode/proxy-agent@^0.17.5": - version "0.17.5" - resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.17.5.tgz#a59f6087a39795425b2601c9ee95bcb0338154e6" - integrity sha512-plKfR1i9ce09aro1/yvK3Ckiu84Cj5ViuLqJ/7VRT6E9w5xP2YUPcgrCy+u7FGorKZmJb+wQ1L6f/cdJ7axulw== +"@vscode/proxy-agent@^0.18.0": + version "0.18.0" + resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.18.0.tgz#08c1adc4707844788738e87814a425c1f553d695" + integrity sha512-lOBA4Ns6PqJtX6LCUpKiwxkx3uhPoOdChtSSvO0hujON1sPBdSjyAwECoEWlUAk8FTJ3040ClPoLsDn9gUw2lw== dependencies: "@tootallnate/once" "^3.0.0" agent-base "^7.0.1" diff --git a/src/vs/platform/native/common/native.ts b/src/vs/platform/native/common/native.ts index b2243653d81..12a2fd9d742 100644 --- a/src/vs/platform/native/common/native.ts +++ b/src/vs/platform/native/common/native.ts @@ -180,6 +180,7 @@ export interface ICommonNativeHostService { // Connectivity resolveProxy(url: string): Promise; + loadCertificates(): Promise; findFreePort(startPort: number, giveUpAfter: number, timeout: number, stride?: number): Promise; // Registry (windows only) diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 29132727162..1cd311f7df6 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -43,6 +43,7 @@ import { WindowProfiler } from 'vs/platform/profiling/electron-main/windowProfil import { IV8Profile } from 'vs/platform/profiling/common/profiling'; import { IAuxiliaryWindowsMainService, isAuxiliaryWindow } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows'; import { IAuxiliaryWindow } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow'; +import { loadSystemCertificates } from '@vscode/proxy-agent'; export interface INativeHostMainService extends AddFirstParameterToFunctions /* only methods, not events */, number | undefined /* window ID */> { } @@ -756,6 +757,10 @@ export class NativeHostMainService extends Disposable implements INativeHostMain return session?.resolveProxy(url); } + async loadCertificates(_windowId: number | undefined): Promise { + return loadSystemCertificates({ log: this.logService }); + } + findFreePort(windowId: number | undefined, startPort: number, giveUpAfter: number, timeout: number, stride = 1): Promise { return findFreePort(startPort, giveUpAfter, timeout, stride); } diff --git a/src/vs/platform/request/browser/requestService.ts b/src/vs/platform/request/browser/requestService.ts index f2edce7bc38..f0fc3d68790 100644 --- a/src/vs/platform/request/browser/requestService.ts +++ b/src/vs/platform/request/browser/requestService.ts @@ -35,4 +35,8 @@ export class RequestService extends AbstractRequestService implements IRequestSe async resolveProxy(url: string): Promise { return undefined; // not implemented in the web } + + async loadCertificates(): Promise { + return []; // not implemented in the web + } } diff --git a/src/vs/platform/request/common/request.ts b/src/vs/platform/request/common/request.ts index 13edce38a5d..289ad6740e0 100644 --- a/src/vs/platform/request/common/request.ts +++ b/src/vs/platform/request/common/request.ts @@ -22,6 +22,7 @@ export interface IRequestService { request(options: IRequestOptions, token: CancellationToken): Promise; resolveProxy(url: string): Promise; + loadCertificates(): Promise; } class LoggableHeaders { @@ -79,6 +80,7 @@ export abstract class AbstractRequestService extends Disposable implements IRequ abstract request(options: IRequestOptions, token: CancellationToken): Promise; abstract resolveProxy(url: string): Promise; + abstract loadCertificates(): Promise; } export function isSuccess(context: IRequestContext): boolean { diff --git a/src/vs/platform/request/common/requestIpc.ts b/src/vs/platform/request/common/requestIpc.ts index b529e5c5d34..421106ebff6 100644 --- a/src/vs/platform/request/common/requestIpc.ts +++ b/src/vs/platform/request/common/requestIpc.ts @@ -34,6 +34,7 @@ export class RequestChannel implements IServerChannel { return [{ statusCode: res.statusCode, headers: res.headers }, buffer]; }); case 'resolveProxy': return this.service.resolveProxy(args[0]); + case 'loadCertificates': return this.service.loadCertificates(); } throw new Error('Invalid call'); } @@ -54,4 +55,7 @@ export class RequestChannelClient implements IRequestService { return this.channel.call('resolveProxy', [url]); } + async loadCertificates(): Promise { + return this.channel.call('loadCertificates'); + } } diff --git a/src/vs/platform/request/node/requestService.ts b/src/vs/platform/request/node/requestService.ts index 111d9fb246b..07318efce27 100644 --- a/src/vs/platform/request/node/requestService.ts +++ b/src/vs/platform/request/node/requestService.ts @@ -20,6 +20,7 @@ import { ILogService, ILoggerService } from 'vs/platform/log/common/log'; import { AbstractRequestService, IRequestService } from 'vs/platform/request/common/request'; import { Agent, getProxyAgent } from 'vs/platform/request/node/proxy'; import { createGunzip } from 'zlib'; +import { loadSystemCertificates } from '@vscode/proxy-agent'; interface IHTTPConfiguration { proxy?: string; @@ -109,6 +110,10 @@ export class RequestService extends AbstractRequestService implements IRequestSe async resolveProxy(url: string): Promise { return undefined; // currently not implemented in node } + + async loadCertificates(): Promise { + return loadSystemCertificates({ log: this.logService }); + } } async function getNodeRequest(options: IRequestOptions): Promise { diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index a1ba702c728..4d1eaa4afb2 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -188,6 +188,7 @@ export class UserDataSyncTestServer implements IRequestService { constructor(private readonly rateLimit = Number.MAX_SAFE_INTEGER, private readonly retryAfter?: number) { } async resolveProxy(url: string): Promise { return url; } + async loadCertificates(): Promise { return []; } async request(options: IRequestOptions, token: CancellationToken): Promise { if (this._requests.length === this.rateLimit) { diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts index 42f3abd7e56..bc90fb28b24 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts @@ -413,7 +413,8 @@ suite('UserDataSyncRequestsSession', () => { const requestService: IRequestService = { _serviceBrand: undefined, async request() { return { res: { headers: {} }, stream: newWriteableBufferStream() }; }, - async resolveProxy() { return undefined; } + async resolveProxy() { return undefined; }, + async loadCertificates() { return []; } }; test('too many requests are thrown when limit exceeded', async () => { diff --git a/src/vs/workbench/api/browser/mainThreadWorkspace.ts b/src/vs/workbench/api/browser/mainThreadWorkspace.ts index 8e97e6a954c..e04ee2837d9 100644 --- a/src/vs/workbench/api/browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/browser/mainThreadWorkspace.ts @@ -230,6 +230,10 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { return this._requestService.resolveProxy(url); } + $loadCertificates(): Promise { + return this._requestService.loadCertificates(); + } + // --- trust --- $requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 422cee576d8..a01697db5a6 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1311,6 +1311,7 @@ export interface MainThreadWorkspaceShape extends IDisposable { $saveAll(includeUntitled?: boolean): Promise; $updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents; name?: string }[]): Promise; $resolveProxy(url: string): Promise; + $loadCertificates(): Promise; $requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise; $registerEditSessionIdentityProvider(handle: number, scheme: string): void; $unregisterEditSessionIdentityProvider(handle: number): void; diff --git a/src/vs/workbench/api/common/extHostWorkspace.ts b/src/vs/workbench/api/common/extHostWorkspace.ts index 19ba0baec9f..95a60bcba8b 100644 --- a/src/vs/workbench/api/common/extHostWorkspace.ts +++ b/src/vs/workbench/api/common/extHostWorkspace.ts @@ -39,6 +39,7 @@ export interface IExtHostWorkspaceProvider { resolveWorkspaceFolder(uri: vscode.Uri): Promise; getWorkspaceFolders2(): Promise; resolveProxy(url: string): Promise; + loadCertificates(): Promise; } function isFolderEqual(folderA: URI, folderB: URI, extHostFileSystemInfo: IExtHostFileSystemInfo): boolean { @@ -578,6 +579,10 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac return this._proxy.$resolveProxy(url); } + loadCertificates(): Promise { + return this._proxy.$loadCertificates(); + } + // --- trust --- get trusted(): boolean { diff --git a/src/vs/workbench/api/node/proxyResolver.ts b/src/vs/workbench/api/node/proxyResolver.ts index 1f59cbf5ca3..519924eec13 100644 --- a/src/vs/workbench/api/node/proxyResolver.ts +++ b/src/vs/workbench/api/node/proxyResolver.ts @@ -16,7 +16,7 @@ import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionS import { URI } from 'vs/base/common/uri'; import { ILogService, LogLevel as LogServiceLevel } from 'vs/platform/log/common/log'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { LogLevel, createHttpPatch, createProxyResolver, createTlsPatch, ProxySupportSetting, ProxyAgentParams, createNetPatch } from '@vscode/proxy-agent'; +import { LogLevel, createHttpPatch, createProxyResolver, createTlsPatch, ProxySupportSetting, ProxyAgentParams, createNetPatch, loadSystemCertificates } from '@vscode/proxy-agent'; const systemCertificatesV2Default = false; @@ -35,24 +35,9 @@ export function connectProxyResolver( lookupProxyAuthorization: lookupProxyAuthorization.bind(undefined, extHostLogService, mainThreadTelemetry, configProvider, {}, initData.remote.isRemote), getProxyURL: () => configProvider.getConfiguration('http').get('proxy'), getProxySupport: () => configProvider.getConfiguration('http').get('proxySupport') || 'off', - getSystemCertificatesV1: () => certSettingV1(configProvider), - getSystemCertificatesV2: () => certSettingV2(configProvider), - log: (level, message, ...args) => { - switch (level) { - case LogLevel.Trace: extHostLogService.trace(message, ...args); break; - case LogLevel.Debug: extHostLogService.debug(message, ...args); break; - case LogLevel.Info: extHostLogService.info(message, ...args); break; - case LogLevel.Warning: extHostLogService.warn(message, ...args); break; - case LogLevel.Error: extHostLogService.error(message, ...args); break; - case LogLevel.Critical: extHostLogService.error(message, ...args); break; - case LogLevel.Off: break; - default: never(level, message, args); break; - } - function never(level: never, message: string, ...args: any[]) { - extHostLogService.error('Unknown log level', level); - extHostLogService.error(message, ...args); - } - }, + addCertificatesV1: () => certSettingV1(configProvider), + addCertificatesV2: () => certSettingV2(configProvider), + log: extHostLogService, getLogLevel: () => { const level = extHostLogService.getLevel(); switch (level) { @@ -71,7 +56,19 @@ export function connectProxyResolver( }, proxyResolveTelemetry: () => { }, useHostProxy: doUseHostProxy, - addCertificates: [], + loadAdditionalCertificates: async () => { + const promises: Promise[] = []; + if (initData.remote.isRemote) { + promises.push(loadSystemCertificates({ log: extHostLogService })); + } + if (doUseHostProxy) { + extHostLogService.trace('ProxyResolver#loadAdditionalCertificates: Loading certificates from main process'); + const certs = extHostWorkspace.loadCertificates(); // Loading from main process to share cache. + certs.then(certs => extHostLogService.trace('ProxyResolver#loadAdditionalCertificates: Loaded certificates from main process', certs.length)); + promises.push(certs); + } + return (await Promise.all(promises)).flat(); + }, env: process.env, }; const resolveProxy = createProxyResolver(params); diff --git a/src/vs/workbench/services/request/electron-sandbox/requestService.ts b/src/vs/workbench/services/request/electron-sandbox/requestService.ts index 3a673a25e64..0a249b6eaaa 100644 --- a/src/vs/workbench/services/request/electron-sandbox/requestService.ts +++ b/src/vs/workbench/services/request/electron-sandbox/requestService.ts @@ -23,6 +23,10 @@ export class NativeRequestService extends RequestService { override async resolveProxy(url: string): Promise { return this.nativeHostService.resolveProxy(url); } + + override async loadCertificates(): Promise { + return this.nativeHostService.loadCertificates(); + } } registerSingleton(IRequestService, NativeRequestService, InstantiationType.Delayed); diff --git a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts index 0e432c4cecf..c97ea8ad9c3 100644 --- a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts @@ -141,6 +141,7 @@ export class TestNativeHostService implements INativeHostService { async openDevTools(options?: Electron.OpenDevToolsOptions | undefined): Promise { } async toggleDevTools(): Promise { } async resolveProxy(url: string): Promise { return undefined; } + async loadCertificates(): Promise { return []; } async findFreePort(startPort: number, giveUpAfter: number, timeout: number, stride?: number): Promise { return -1; } async readClipboardText(type?: 'selection' | 'clipboard' | undefined): Promise { return ''; } async writeClipboardText(text: string, type?: 'selection' | 'clipboard' | undefined): Promise { } diff --git a/yarn.lock b/yarn.lock index 0d50d37117a..efd492bf7aa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1392,10 +1392,10 @@ bindings "^1.5.0" node-addon-api "^6.0.0" -"@vscode/proxy-agent@^0.17.5": - version "0.17.5" - resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.17.5.tgz#a59f6087a39795425b2601c9ee95bcb0338154e6" - integrity sha512-plKfR1i9ce09aro1/yvK3Ckiu84Cj5ViuLqJ/7VRT6E9w5xP2YUPcgrCy+u7FGorKZmJb+wQ1L6f/cdJ7axulw== +"@vscode/proxy-agent@^0.18.0": + version "0.18.0" + resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.18.0.tgz#08c1adc4707844788738e87814a425c1f553d695" + integrity sha512-lOBA4Ns6PqJtX6LCUpKiwxkx3uhPoOdChtSSvO0hujON1sPBdSjyAwECoEWlUAk8FTJ3040ClPoLsDn9gUw2lw== dependencies: "@tootallnate/once" "^3.0.0" agent-base "^7.0.1"