Skip to content

Commit 538c2ed

Browse files
authored
Merge pull request #11 from BitGo/WP-4676-fix-tls-mode
fix: both tls modes working with http/https
2 parents 11e3ba1 + a03fea9 commit 538c2ed

File tree

4 files changed

+64
-32
lines changed

4 files changed

+64
-32
lines changed

src/config.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -196,17 +196,19 @@ const defaultMasterExpressConfig: MasterExpressConfig = {
196196
allowSelfSigned: false,
197197
};
198198

199-
function forceSecureUrl(url: string): string {
199+
function determineProtocol(url: string, tlsMode: TlsMode, isBitGo = false): string {
200200
const regex = new RegExp(/(^\w+:|^)\/\//);
201+
const protocol = isBitGo ? 'https' : tlsMode === TlsMode.DISABLED ? 'http' : 'https';
201202
if (regex.test(url)) {
202-
return url.replace(/(^\w+:|^)\/\//, 'https://');
203+
return url.replace(/(^\w+:|^)\/\//, `${protocol}://`);
203204
}
204-
return `https://${url}`;
205+
return `${protocol}://${url}`;
205206
}
206207

207208
function masterExpressEnvConfig(): Partial<MasterExpressConfig> {
208209
const enclavedExpressUrl = readEnvVar('ENCLAVED_EXPRESS_URL');
209210
const enclavedExpressCert = readEnvVar('ENCLAVED_EXPRESS_CERT');
211+
const tlsMode = determineTlsMode();
210212

211213
if (!enclavedExpressUrl) {
212214
throw new Error('ENCLAVED_EXPRESS_URL environment variable is required and cannot be empty');
@@ -245,7 +247,7 @@ function masterExpressEnvConfig(): Partial<MasterExpressConfig> {
245247
crtPath: readEnvVar('TLS_CERT_PATH'),
246248
tlsKey: readEnvVar('TLS_KEY'),
247249
tlsCert: readEnvVar('TLS_CERT'),
248-
tlsMode: determineTlsMode(),
250+
tlsMode,
249251
mtlsRequestCert,
250252
mtlsAllowedClientFingerprints: readEnvVar('MTLS_ALLOWED_CLIENT_FINGERPRINTS')?.split(','),
251253
allowSelfSigned,
@@ -295,13 +297,17 @@ export function configureMasterExpressMode(): MasterExpressConfig {
295297
const env = masterExpressEnvConfig();
296298
let config = mergeMasterExpressConfigs(env);
297299

298-
// Post-process URLs to ensure they use HTTPS
300+
// Post-process URLs to ensure they use the correct protocol based on TLS mode
299301
const updates: Partial<MasterExpressConfig> = {};
300302
if (config.customRootUri) {
301-
updates.customRootUri = forceSecureUrl(config.customRootUri);
303+
updates.customRootUri = determineProtocol(config.customRootUri, config.tlsMode, true);
302304
}
303305
if (config.enclavedExpressUrl) {
304-
updates.enclavedExpressUrl = forceSecureUrl(config.enclavedExpressUrl);
306+
updates.enclavedExpressUrl = determineProtocol(
307+
config.enclavedExpressUrl,
308+
config.tlsMode,
309+
false,
310+
);
305311
}
306312
config = { ...config, ...updates };
307313

src/enclavedApp.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export async function createServer(
7979

8080
export function createBaseUri(config: EnclavedConfig): string {
8181
const { bind, port } = config;
82-
const tls = isTLS(config);
82+
const tls = config.tlsMode === TlsMode.MTLS;
8383
const isStandardPort = (port === 80 && !tls) || (port === 443 && tls);
8484
return `http${tls ? 's' : ''}://${bind}${!isStandardPort ? ':' + port : ''}`;
8585
}

src/masterBitgoExpress/enclavedExpressClient.ts

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import superagent from 'superagent';
22
import https from 'https';
33
import debug from 'debug';
44
import { MasterExpressConfig } from '../types';
5+
import { TlsMode } from '../types';
56

67
const debugLogger = debug('bitgo:express:enclavedExpressClient');
78

@@ -24,16 +25,17 @@ export interface IndependentKeychainResponse {
2425
export class EnclavedExpressClient {
2526
private readonly baseUrl: string;
2627
private readonly enclavedExpressCert: string;
27-
private readonly tlsKey: string;
28-
private readonly tlsCert: string;
28+
private readonly tlsKey?: string;
29+
private readonly tlsCert?: string;
2930
private readonly allowSelfSigned: boolean;
3031
private readonly coin?: string;
32+
private readonly tlsMode: TlsMode;
3133

3234
constructor(cfg: MasterExpressConfig, coin?: string) {
3335
if (!cfg.enclavedExpressUrl || !cfg.enclavedExpressCert) {
3436
throw new Error('enclavedExpressUrl and enclavedExpressCert are required');
3537
}
36-
if (!cfg.tlsKey || !cfg.tlsCert) {
38+
if (cfg.tlsMode === TlsMode.MTLS && (!cfg.tlsKey || !cfg.tlsCert)) {
3739
throw new Error('tlsKey and tlsCert are required for mTLS communication');
3840
}
3941

@@ -43,10 +45,14 @@ export class EnclavedExpressClient {
4345
this.tlsCert = cfg.tlsCert;
4446
this.allowSelfSigned = cfg.allowSelfSigned ?? false;
4547
this.coin = coin;
48+
this.tlsMode = cfg.tlsMode;
4649
debugLogger('EnclavedExpressClient initialized with URL: %s', this.baseUrl);
4750
}
4851

4952
private createHttpsAgent(): https.Agent {
53+
if (!this.tlsKey || !this.tlsCert) {
54+
throw new Error('TLS key and certificate are required for HTTPS agent');
55+
}
5056
return new https.Agent({
5157
rejectUnauthorized: !this.allowSelfSigned,
5258
ca: this.enclavedExpressCert,
@@ -59,7 +65,12 @@ export class EnclavedExpressClient {
5965
async ping(): Promise<void> {
6066
try {
6167
debugLogger('Pinging enclaved express at %s', this.baseUrl);
62-
await superagent.get(`${this.baseUrl}/ping`).agent(this.createHttpsAgent()).send();
68+
if (this.tlsMode === TlsMode.MTLS) {
69+
await superagent.get(`${this.baseUrl}/ping`).agent(this.createHttpsAgent()).send();
70+
} else {
71+
// When TLS is disabled, use plain HTTP without any TLS configuration
72+
await superagent.get(`${this.baseUrl}/ping`).send();
73+
}
6374
} catch (error) {
6475
const err = error as Error;
6576
debugLogger('Failed to ping enclaved express: %s', err.message);
@@ -79,13 +90,22 @@ export class EnclavedExpressClient {
7990

8091
try {
8192
debugLogger('Creating independent keychain for coin: %s', this.coin);
82-
const { body: keychain } = await superagent
83-
.post(`${this.baseUrl}/api/${this.coin}/key/independent`)
84-
.agent(this.createHttpsAgent())
85-
.type('json')
86-
.send(params);
93+
let response;
94+
if (this.tlsMode === TlsMode.MTLS) {
95+
response = await superagent
96+
.post(`${this.baseUrl}/api/${this.coin}/key/independent`)
97+
.agent(this.createHttpsAgent())
98+
.type('json')
99+
.send(params);
100+
} else {
101+
// When TLS is disabled, use plain HTTP without any TLS configuration
102+
response = await superagent
103+
.post(`${this.baseUrl}/api/${this.coin}/key/independent`)
104+
.type('json')
105+
.send(params);
106+
}
87107

88-
return keychain;
108+
return response.body;
89109
} catch (error) {
90110
const err = error as Error;
91111
debugLogger('Failed to create independent keychain: %s', err.message);

src/masterExpressApp.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -157,20 +157,26 @@ function setupMasterExpressRoutes(app: express.Application, cfg: MasterExpressCo
157157
try {
158158
logger.debug('Pinging enclaved express');
159159

160-
// Use Master Express's own certificate as client cert when connecting to Enclaved Express
161-
const httpsAgent = new https.Agent({
162-
rejectUnauthorized: cfg.tlsMode === TlsMode.MTLS && !cfg.allowSelfSigned,
163-
ca: cfg.enclavedExpressCert,
164-
// Provide client certificate for mTLS
165-
key: cfg.tlsKey,
166-
cert: cfg.tlsCert,
167-
});
168-
169-
const response = await superagent
170-
.post(`${cfg.enclavedExpressUrl}/ping`)
171-
.ca(cfg.enclavedExpressCert)
172-
.agent(httpsAgent)
173-
.send();
160+
let response;
161+
if (cfg.tlsMode === TlsMode.MTLS) {
162+
// Use Master Express's own certificate as client cert when connecting to Enclaved Express
163+
const httpsAgent = new https.Agent({
164+
rejectUnauthorized: !cfg.allowSelfSigned,
165+
ca: cfg.enclavedExpressCert,
166+
// Provide client certificate for mTLS
167+
key: cfg.tlsKey,
168+
cert: cfg.tlsCert,
169+
});
170+
171+
response = await superagent
172+
.post(`${cfg.enclavedExpressUrl}/ping`)
173+
.ca(cfg.enclavedExpressCert)
174+
.agent(httpsAgent)
175+
.send();
176+
} else {
177+
// When TLS is disabled, use plain HTTP without any TLS configuration
178+
response = await superagent.post(`${cfg.enclavedExpressUrl}/ping`).send();
179+
}
174180

175181
res.json({
176182
status: 'Successfully pinged enclaved express',

0 commit comments

Comments
 (0)