Browse Source

Fixes GitLab issues w/ self-signed certs

This is an ugly hack, but node-fetch doesn't seem to be able to pass `rejectUnauthorized` through on the request options properly even though we are creating a HTTPS agent with it set
main
Eric Amodio 2 years ago
parent
commit
eadd4b9bbc
4 changed files with 82 additions and 31 deletions
  1. +2
    -2
      package.json
  2. +2
    -2
      src/config.ts
  3. +7
    -4
      src/env/node/fetch.ts
  4. +71
    -23
      src/plus/gitlab/gitlab.ts

+ 2
- 2
package.json View File

@ -2329,10 +2329,10 @@
"default": "https", "default": "https",
"description": "Specifies an optional url protocol for the custom remote service" "description": "Specifies an optional url protocol for the custom remote service"
}, },
"ignoreCertErrors": {
"ignoreSSLErrors": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "When connecting to the remote API, true will ignore any invalid certificate errors"
"description": "Specifies whether to ignore invalid SSL certificate errors when connecting to the remote service"
}, },
"urls": { "urls": {
"type": "object", "type": "object",

+ 2
- 2
src/config.ts View File

@ -485,7 +485,7 @@ export type RemotesConfig =
protocol?: string; protocol?: string;
type: CustomRemoteType; type: CustomRemoteType;
urls?: RemotesUrlsConfig; urls?: RemotesUrlsConfig;
ignoreCertErrors?: boolean;
ignoreSSLErrors?: boolean | 'force';
} }
| { | {
domain: null; domain: null;
@ -494,7 +494,7 @@ export type RemotesConfig =
protocol?: string; protocol?: string;
type: CustomRemoteType; type: CustomRemoteType;
urls?: RemotesUrlsConfig; urls?: RemotesUrlsConfig;
ignoreCertErrors?: boolean;
ignoreSSLErrors?: boolean | 'force';
}; };
export interface RemotesUrlsConfig { export interface RemotesUrlsConfig {

+ 7
- 4
src/env/node/fetch.ts View File

@ -19,10 +19,13 @@ export function getProxyAgent(strictSSL?: boolean): HttpsProxyAgent | undefined
undefined, undefined,
'override', 'override',
); );
if (proxySupport === 'off') return undefined;
strictSSL = strictSSL ?? configuration.getAny<boolean>('http.proxyStrictSSL', undefined, true);
proxyUrl = configuration.getAny<string>('http.proxy') || process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
if (proxySupport === 'off') {
strictSSL = strictSSL ?? true;
} else {
strictSSL = strictSSL ?? configuration.getAny<boolean>('http.proxyStrictSSL', undefined, true);
proxyUrl = configuration.getAny<string>('http.proxy') || process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
}
} }
if (proxyUrl) { if (proxyUrl) {
@ -32,7 +35,7 @@ export function getProxyAgent(strictSSL?: boolean): HttpsProxyAgent | undefined
}); });
} }
if (!strictSSL) {
if (strictSSL === false) {
return new HttpsProxyAgent({ return new HttpsProxyAgent({
rejectUnauthorized: false, rejectUnauthorized: false,
}); });

+ 71
- 23
src/plus/gitlab/gitlab.ts View File

@ -2,7 +2,7 @@ import type { HttpsProxyAgent } from 'https-proxy-agent';
import { Disposable, Event, EventEmitter, Uri, window } from 'vscode'; import { Disposable, Event, EventEmitter, Uri, window } from 'vscode';
import { fetch, getProxyAgent, RequestInit, Response } from '@env/fetch'; import { fetch, getProxyAgent, RequestInit, Response } from '@env/fetch';
import { isWeb } from '@env/platform'; import { isWeb } from '@env/platform';
import { configuration } from '../../configuration';
import { configuration, CustomRemoteType } from '../../configuration';
import { import {
AuthenticationError, AuthenticationError,
AuthenticationErrorReason, AuthenticationErrorReason,
@ -38,17 +38,16 @@ export class GitLabApi implements Disposable {
constructor() { constructor() {
this._disposable = Disposable.from( this._disposable = Disposable.from(
configuration.onDidChange(e => { configuration.onDidChange(e => {
if (configuration.changed(e, 'proxy')) {
this._proxyAgent = null;
this._projectIds.clear();
} else if (configuration.changed(e, 'outputLevel')) {
if (configuration.changed(e, 'proxy') || configuration.changed(e, 'remotes')) {
this._projectIds.clear(); this._projectIds.clear();
this._proxyAgents.clear();
this._ignoreSSLErrors.clear();
} }
}), }),
configuration.onDidChangeAny(e => { configuration.onDidChangeAny(e => {
if (e.affectsConfiguration('http.proxy') || e.affectsConfiguration('http.proxyStrictSSL')) { if (e.affectsConfiguration('http.proxy') || e.affectsConfiguration('http.proxyStrictSSL')) {
this._proxyAgent = null;
this._projectIds.clear(); this._projectIds.clear();
this._proxyAgents.clear();
} }
}), }),
); );
@ -58,18 +57,34 @@ export class GitLabApi implements Disposable {
this._disposable?.dispose(); this._disposable?.dispose();
} }
private _proxyAgent: HttpsProxyAgent | null | undefined = null;
private get proxyAgent(): HttpsProxyAgent | undefined {
private _proxyAgents = new Map<string, HttpsProxyAgent | null | undefined>();
private getProxyAgent(provider: RichRemoteProvider): HttpsProxyAgent | undefined {
if (isWeb) return undefined; if (isWeb) return undefined;
if (this._proxyAgent === null) {
// TODO@eamodio figure out how to best handle this -- we can't really just find the first GitLab remote here
// const config = configuration.get('remotes')?.find(remote => remote.type === CustomRemoteType.GitLab);
// this._agent = getProxyAgent(config?.ignoreCertErrors);
let proxyAgent = this._proxyAgents.get(provider.id);
if (proxyAgent === undefined) {
const ignoreSSLErrors = this.getIgnoreSSLErrors(provider);
proxyAgent = getProxyAgent(ignoreSSLErrors === true || ignoreSSLErrors === 'force' ? false : undefined);
this._proxyAgents.set(provider.id, proxyAgent ?? null);
}
return proxyAgent ?? undefined;
}
this._proxyAgent = getProxyAgent();
private _ignoreSSLErrors = new Map<string, boolean | 'force'>();
private getIgnoreSSLErrors(provider: RichRemoteProvider): boolean | 'force' {
if (isWeb) return false;
let ignoreSSLErrors = this._ignoreSSLErrors.get(provider.id);
if (ignoreSSLErrors === undefined) {
const cfg = configuration
.get('remotes')
?.find(remote => remote.type === CustomRemoteType.GitLab && remote.domain === provider.domain);
ignoreSSLErrors = cfg?.ignoreSSLErrors ?? false;
this._ignoreSSLErrors.set(provider.id, ignoreSSLErrors);
} }
return this._proxyAgent;
return ignoreSSLErrors;
} }
@debug<GitLabApi['getAccountForCommit']>({ args: { 0: p => p.name, 1: '<token>' } }) @debug<GitLabApi['getAccountForCommit']>({ args: { 0: p => p.name, 1: '<token>' } })
@ -91,6 +106,7 @@ export class GitLabApi implements Disposable {
try { try {
const commit = await this.request<GitLabCommit>( const commit = await this.request<GitLabCommit>(
provider,
token, token,
options?.baseUrl, options?.baseUrl,
`v4/projects/${projectId}/repository/commits/${ref}?stats=false`, `v4/projects/${projectId}/repository/commits/${ref}?stats=false`,
@ -199,7 +215,7 @@ export class GitLabApi implements Disposable {
} }
}`; }`;
const rsp = await this.graphql<QueryResult>(token, options?.baseUrl, query, {
const rsp = await this.graphql<QueryResult>(provider, token, options?.baseUrl, query, {
fullPath: `${owner}/${repo}`, fullPath: `${owner}/${repo}`,
}); });
@ -278,7 +294,7 @@ export class GitLabApi implements Disposable {
} }
}`; }`;
const rsp = await this.graphql<QueryResult>(token, options?.baseUrl, query, {
const rsp = await this.graphql<QueryResult>(provider, token, options?.baseUrl, query, {
fullPath: `${owner}/${repo}`, fullPath: `${owner}/${repo}`,
iid: String(number), iid: String(number),
}); });
@ -408,7 +424,7 @@ export class GitLabApi implements Disposable {
} }
}`; }`;
const rsp = await this.graphql<QueryResult>(token, options?.baseUrl, query, {
const rsp = await this.graphql<QueryResult>(provider, token, options?.baseUrl, query, {
fullPath: `${owner}/${repo}`, fullPath: `${owner}/${repo}`,
branches: [branch], branches: [branch],
state: options?.include, state: options?.include,
@ -479,6 +495,8 @@ export class GitLabApi implements Disposable {
try { try {
const mrs = await this.request<GitLabMergeRequestREST[]>( const mrs = await this.request<GitLabMergeRequestREST[]>(
provider,
token, token,
options?.baseUrl, options?.baseUrl,
`v4/projects/${projectId}/repository/commits/${ref}/merge_requests`, `v4/projects/${projectId}/repository/commits/${ref}/merge_requests`,
@ -507,7 +525,7 @@ export class GitLabApi implements Disposable {
} }
private async findUser( private async findUser(
_provider: RichRemoteProvider,
provider: RichRemoteProvider,
token: string, token: string,
search: string, search: string,
options?: { options?: {
@ -549,7 +567,7 @@ $search: String!
} }
} }
}`; }`;
const rsp = await this.graphql<QueryResult>(token, options?.baseUrl, query, {
const rsp = await this.graphql<QueryResult>(provider, token, options?.baseUrl, query, {
search: search, search: search,
}); });
@ -601,7 +619,7 @@ $search: String!
} }
private async getProjectIdCore( private async getProjectIdCore(
_provider: RichRemoteProvider,
provider: RichRemoteProvider,
token: string, token: string,
group: string, group: string,
repo: string, repo: string,
@ -621,7 +639,7 @@ $search: String!
id id
} }
}`; }`;
const rsp = await this.graphql<QueryResult>(token, baseUrl, query, {
const rsp = await this.graphql<QueryResult>(provider, token, baseUrl, query, {
fullPath: `${group}/${repo}`, fullPath: `${group}/${repo}`,
}); });
@ -646,6 +664,7 @@ $search: String!
} }
private async graphql<T>( private async graphql<T>(
provider: RichRemoteProvider,
token: string, token: string,
baseUrl: string | undefined, baseUrl: string | undefined,
query: string, query: string,
@ -657,11 +676,21 @@ $search: String!
Logger.logLevel === LogLevel.Debug || Logger.isDebugging Logger.logLevel === LogLevel.Debug || Logger.isDebugging
? new Stopwatch(`[GITLAB] POST ${baseUrl}`, { log: false }) ? new Stopwatch(`[GITLAB] POST ${baseUrl}`, { log: false })
: undefined; : undefined;
const agent = this.getProxyAgent(provider);
const ignoreSSLErrors = this.getIgnoreSSLErrors(provider);
let previousRejectUnauthorized;
try { try {
if (ignoreSSLErrors === 'force') {
previousRejectUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED;
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
}
rsp = await fetch(`${baseUrl ?? 'https://gitlab.com/api'}/graphql`, { rsp = await fetch(`${baseUrl ?? 'https://gitlab.com/api'}/graphql`, {
method: 'POST', method: 'POST',
headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' }, headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },
agent: this.proxyAgent as any,
agent: agent as any,
body: JSON.stringify({ query: query, variables: variables }), body: JSON.stringify({ query: query, variables: variables }),
}); });
@ -674,6 +703,10 @@ $search: String!
throw new ProviderFetchError('GitLab', rsp); throw new ProviderFetchError('GitLab', rsp);
} finally { } finally {
if (ignoreSSLErrors === 'force') {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = previousRejectUnauthorized;
}
const match = /(^[^({\n]+)/.exec(query); const match = /(^[^({\n]+)/.exec(query);
const message = ` ${match?.[1].trim() ?? query}`; const message = ` ${match?.[1].trim() ?? query}`;
@ -691,6 +724,7 @@ $search: String!
} }
private async request<T>( private async request<T>(
provider: RichRemoteProvider,
token: string, token: string,
baseUrl: string | undefined, baseUrl: string | undefined,
route: string, route: string,
@ -704,10 +738,20 @@ $search: String!
Logger.logLevel === LogLevel.Debug || Logger.isDebugging Logger.logLevel === LogLevel.Debug || Logger.isDebugging
? new Stopwatch(`[GITLAB] ${options?.method ?? 'GET'} ${url}`, { log: false }) ? new Stopwatch(`[GITLAB] ${options?.method ?? 'GET'} ${url}`, { log: false })
: undefined; : undefined;
const agent = this.getProxyAgent(provider);
const ignoreSSLErrors = this.getIgnoreSSLErrors(provider);
let previousRejectUnauthorized;
try { try {
if (ignoreSSLErrors === 'force') {
previousRejectUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED;
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
}
rsp = await fetch(url, { rsp = await fetch(url, {
headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' }, headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },
agent: this.proxyAgent as any,
agent: agent as any,
...options, ...options,
}); });
@ -718,6 +762,10 @@ $search: String!
throw new ProviderFetchError('GitLab', rsp); throw new ProviderFetchError('GitLab', rsp);
} finally { } finally {
if (ignoreSSLErrors === 'force') {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = previousRejectUnauthorized;
}
stopwatch?.stop(); stopwatch?.stop();
} }
} catch (ex) { } catch (ex) {

Loading…
Cancel
Save