Skip to content

Commit 8ed9ae6

Browse files
committed
chore: log cert validation at app initiations
Ticket: WP-4663
1 parent e293c70 commit 8ed9ae6

File tree

6 files changed

+103
-72
lines changed

6 files changed

+103
-72
lines changed

src/app.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
import { config, isEnclavedConfig, isMasterExpressConfig } from './config';
1+
import { determineAppMode, AppMode } from './config';
22
import * as enclavedApp from './enclavedApp';
33
import * as masterExpressApp from './masterExpressApp';
44

55
/**
66
* Main application entry point that determines the mode and starts the appropriate app
77
*/
88
export async function init(): Promise<void> {
9-
const cfg = config();
9+
const appMode = determineAppMode();
1010

11-
if (isEnclavedConfig(cfg)) {
11+
if (appMode === AppMode.ENCLAVED) {
1212
console.log('Starting in Enclaved mode...');
1313
await enclavedApp.init();
14-
} else if (isMasterExpressConfig(cfg)) {
14+
} else if (appMode === AppMode.MASTER_EXPRESS) {
1515
console.log('Starting in Master Express mode...');
1616
await masterExpressApp.init();
1717
} else {
18-
throw new Error(`Unknown app mode: ${(cfg as any).appMode}`);
18+
throw new Error(`Unknown app mode: ${appMode}`);
1919
}
2020
}
2121

src/config.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
EnvironmentName,
99
} from './types';
1010
import logger from './logger';
11+
import { validateTlsCertificates, validateMasterExpressConfig } from './shared/appUtils';
1112

1213
export { Config, EnclavedConfig, MasterExpressConfig, TlsMode, AppMode, EnvironmentName };
1314

@@ -37,6 +38,8 @@ function determineAppMode(): AppMode {
3738
throw new Error(`Invalid APP_MODE: ${mode}. Must be either "enclaved" or "master-express"`);
3839
}
3940

41+
export { determineAppMode };
42+
4043
// ============================================================================
4144
// ENCLAVED MODE CONFIGURATION
4245
// ============================================================================
@@ -130,20 +133,30 @@ function configureEnclavedMode(): EnclavedConfig {
130133
if (!config.tlsKey && config.keyPath) {
131134
try {
132135
config = { ...config, tlsKey: fs.readFileSync(config.keyPath, 'utf-8') };
136+
logger.info(`Successfully loaded TLS private key from file: ${config.keyPath}`);
133137
} catch (e) {
134138
const err = e instanceof Error ? e : new Error(String(e));
135139
throw new Error(`Failed to read TLS key from keyPath: ${err.message}`);
136140
}
141+
} else if (config.tlsKey) {
142+
logger.debug('Using TLS private key from environment variable');
137143
}
144+
138145
if (!config.tlsCert && config.crtPath) {
139146
try {
140147
config = { ...config, tlsCert: fs.readFileSync(config.crtPath, 'utf-8') };
148+
logger.info(`Successfully loaded TLS certificate from file: ${config.crtPath}`);
141149
} catch (e) {
142150
const err = e instanceof Error ? e : new Error(String(e));
143151
throw new Error(`Failed to read TLS certificate from crtPath: ${err.message}`);
144152
}
153+
} else if (config.tlsCert) {
154+
logger.debug('Using TLS certificate from environment variable');
145155
}
146156

157+
// Validate that certificates are properly loaded when TLS is enabled
158+
validateTlsCertificates(config);
159+
147160
return config;
148161
}
149162

@@ -280,18 +293,25 @@ export function configureMasterExpressMode(): MasterExpressConfig {
280293
if (!config.tlsKey && config.keyPath) {
281294
try {
282295
config = { ...config, tlsKey: fs.readFileSync(config.keyPath, 'utf-8') };
296+
logger.info(`Successfully loaded TLS private key from file: ${config.keyPath}`);
283297
} catch (e) {
284298
const err = e instanceof Error ? e : new Error(String(e));
285299
throw new Error(`Failed to read TLS key from keyPath: ${err.message}`);
286300
}
301+
} else if (config.tlsKey) {
302+
logger.debug('Using TLS private key from environment variable');
287303
}
304+
288305
if (!config.tlsCert && config.crtPath) {
289306
try {
290307
config = { ...config, tlsCert: fs.readFileSync(config.crtPath, 'utf-8') };
308+
logger.info(`Successfully loaded TLS certificate from file: ${config.crtPath}`);
291309
} catch (e) {
292310
const err = e instanceof Error ? e : new Error(String(e));
293311
throw new Error(`Failed to read TLS certificate from crtPath: ${err.message}`);
294312
}
313+
} else if (config.tlsCert) {
314+
logger.debug('Using TLS certificate from environment variable');
295315
}
296316

297317
// Handle cert loading
@@ -302,6 +322,12 @@ export function configureMasterExpressMode(): MasterExpressConfig {
302322
...config,
303323
enclavedExpressCert: fs.readFileSync(config.enclavedExpressCert, 'utf-8'),
304324
};
325+
logger.info(
326+
`Successfully loaded Enclaved Express certificate from file: ${config.enclavedExpressCert.substring(
327+
0,
328+
50,
329+
)}...`,
330+
);
305331
} else {
306332
throw new Error(`Certificate file not found: ${config.enclavedExpressCert}`);
307333
}
@@ -311,6 +337,12 @@ export function configureMasterExpressMode(): MasterExpressConfig {
311337
}
312338
}
313339

340+
// Validate that certificates are properly loaded when TLS is enabled
341+
validateTlsCertificates(config);
342+
343+
// Validate Master Express configuration
344+
validateMasterExpressConfig(config);
345+
314346
return config;
315347
}
316348

src/enclavedApp.ts

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,11 @@ import { EnclavedConfig, config, TlsMode, isEnclavedConfig } from './config';
88
import * as routes from './routes';
99
import {
1010
setupLogging,
11-
setupDebugNamespaces,
1211
setupCommonMiddleware,
1312
createErrorHandler,
1413
createHttpServer,
1514
configureServerTimeouts,
1615
prepareIpc,
17-
readCertificates,
1816
createMtlsMiddleware,
1917
} from './shared/appUtils';
2018
import logger from './logger';
@@ -56,27 +54,16 @@ async function createHttpsServer(
5654
app: express.Application,
5755
config: EnclavedConfig,
5856
): Promise<https.Server> {
59-
const { keyPath, crtPath, tlsKey, tlsCert, tlsMode, mtlsRequestCert } = config;
60-
let key: string;
61-
let cert: string;
62-
63-
if (tlsKey && tlsCert) {
64-
key = tlsKey;
65-
cert = tlsCert;
66-
logger.info('Using TLS key and cert from environment variables');
67-
} else if (keyPath && crtPath) {
68-
const certificates = await readCertificates(keyPath, crtPath);
69-
key = certificates.key;
70-
cert = certificates.cert;
71-
logger.info(`Using TLS key and cert from files: ${keyPath}, ${crtPath}`);
72-
} else {
73-
throw new Error('Failed to get TLS key and certificate');
57+
const { tlsKey, tlsCert, tlsMode, mtlsRequestCert } = config;
58+
59+
if (!tlsKey || !tlsCert) {
60+
throw new Error('TLS key and certificate must be provided for HTTPS server');
7461
}
7562

7663
const httpsOptions: https.ServerOptions = {
7764
secureOptions: SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1,
78-
key,
79-
cert,
65+
key: tlsKey,
66+
cert: tlsCert,
8067
// Only request cert if mTLS is enabled AND we want to request certs
8168
// This prevents TLS handshake failures when no cert is provided
8269
requestCert: tlsMode === TlsMode.MTLS && mtlsRequestCert,
@@ -120,7 +107,6 @@ export function app(cfg: EnclavedConfig): express.Application {
120107
return (req as any).clientCert ? (req as any).clientCert.subject.CN : 'unknown';
121108
});
122109

123-
setupDebugNamespaces(cfg.debugNamespace);
124110
setupCommonMiddleware(app, cfg);
125111

126112
// Add mTLS middleware before routes if in mTLS mode

src/masterBitgoExpress/enclavedExpressClient.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import superagent from 'superagent';
22
import https from 'https';
33
import debug from 'debug';
4-
import { MasterExpressConfig, TlsMode } from '../types';
4+
import { MasterExpressConfig } from '../types';
55

66
const debugLogger = debug('bitgo:express:enclavedExpressClient');
77

@@ -23,26 +23,43 @@ export interface IndependentKeychainResponse {
2323

2424
export class EnclavedExpressClient {
2525
private readonly baseUrl: string;
26-
private readonly sslCert: string;
27-
private readonly tlsMode: TlsMode;
26+
private readonly enclavedExpressCert: string;
27+
private readonly tlsKey: string;
28+
private readonly tlsCert: string;
29+
private readonly allowSelfSigned: boolean;
2830
private readonly coin?: string;
2931

3032
constructor(cfg: MasterExpressConfig, coin?: string) {
3133
if (!cfg.enclavedExpressUrl || !cfg.enclavedExpressCert) {
3234
throw new Error('enclavedExpressUrl and enclavedExpressCert are required');
3335
}
36+
if (!cfg.tlsKey || !cfg.tlsCert) {
37+
throw new Error('tlsKey and tlsCert are required for mTLS communication');
38+
}
3439

3540
this.baseUrl = cfg.enclavedExpressUrl;
36-
this.sslCert = cfg.enclavedExpressCert;
37-
this.tlsMode = cfg.tlsMode;
41+
this.enclavedExpressCert = cfg.enclavedExpressCert;
42+
this.tlsKey = cfg.tlsKey;
43+
this.tlsCert = cfg.tlsCert;
44+
this.allowSelfSigned = cfg.allowSelfSigned ?? false;
3845
this.coin = coin;
3946
debugLogger('EnclavedExpressClient initialized with URL: %s', this.baseUrl);
4047
}
4148

49+
private createHttpsAgent(): https.Agent {
50+
return new https.Agent({
51+
rejectUnauthorized: !this.allowSelfSigned,
52+
ca: this.enclavedExpressCert,
53+
// Use Master Express's own certificate as client cert when connecting to Enclaved Express
54+
key: this.tlsKey,
55+
cert: this.tlsCert,
56+
});
57+
}
58+
4259
async ping(): Promise<void> {
4360
try {
4461
debugLogger('Pinging enclaved express at %s', this.baseUrl);
45-
await superagent.get(`${this.baseUrl}/ping`).ca(this.sslCert).send();
62+
await superagent.get(`${this.baseUrl}/ping`).agent(this.createHttpsAgent()).send();
4663
} catch (error) {
4764
const err = error as Error;
4865
debugLogger('Failed to ping enclaved express: %s', err.message);
@@ -64,13 +81,7 @@ export class EnclavedExpressClient {
6481
debugLogger('Creating independent keychain for coin: %s', this.coin);
6582
const { body: keychain } = await superagent
6683
.post(`${this.baseUrl}/api/${this.coin}/key/independent`)
67-
.ca(this.sslCert)
68-
.agent(
69-
new https.Agent({
70-
rejectUnauthorized: this.tlsMode === TlsMode.MTLS,
71-
ca: this.sslCert,
72-
}),
73-
)
84+
.agent(this.createHttpsAgent())
7485
.type('json')
7586
.send(params);
7687

src/masterExpressApp.ts

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@ import { MasterExpressConfig, config, isMasterExpressConfig, TlsMode } from './c
1111
import { BitGoRequest } from './types/request';
1212
import {
1313
setupLogging,
14-
setupDebugNamespaces,
1514
setupCommonMiddleware,
1615
createErrorHandler,
1716
createHttpServer,
1817
configureServerTimeouts,
1918
prepareIpc,
20-
readCertificates,
2119
setupHealthCheckRoutes,
2220
createMtlsMiddleware,
2321
} from './shared/appUtils';
@@ -117,27 +115,16 @@ async function createHttpsServer(
117115
app: express.Application,
118116
config: MasterExpressConfig,
119117
): Promise<https.Server> {
120-
const { keyPath, crtPath, tlsKey, tlsCert, tlsMode, mtlsRequestCert } = config;
121-
let key: string;
122-
let cert: string;
123-
124-
if (tlsKey && tlsCert) {
125-
key = tlsKey;
126-
cert = tlsCert;
127-
logger.info('Using TLS key and cert from environment variables');
128-
} else if (keyPath && crtPath) {
129-
const certificates = await readCertificates(keyPath, crtPath);
130-
key = certificates.key;
131-
cert = certificates.cert;
132-
logger.info(`Using TLS key and cert from files: ${keyPath}, ${crtPath}`);
133-
} else {
134-
throw new Error('Failed to get TLS key and certificate');
118+
const { tlsKey, tlsCert, tlsMode, mtlsRequestCert } = config;
119+
120+
if (!tlsKey || !tlsCert) {
121+
throw new Error('TLS key and certificate must be provided for HTTPS server');
135122
}
136123

137124
const httpsOptions: https.ServerOptions = {
138125
secureOptions: SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1,
139-
key,
140-
cert,
126+
key: tlsKey,
127+
cert: tlsCert,
141128
// Only request cert if mTLS is enabled AND we want to request certs
142129
// This prevents TLS handshake failures when no cert is provided
143130
requestCert: tlsMode === TlsMode.MTLS && mtlsRequestCert,
@@ -241,7 +228,6 @@ export function app(cfg: MasterExpressConfig): express.Application {
241228
setupLogging(app, cfg);
242229
logger.debug('logging setup');
243230

244-
setupDebugNamespaces(cfg.debugNamespace);
245231
setupCommonMiddleware(app, cfg);
246232

247233
// Add mTLS middleware before routes if in mTLS mode

src/shared/appUtils.ts

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import morgan from 'morgan';
66
import fs from 'fs';
77
import timeout from 'connect-timeout';
88
import bodyParser from 'body-parser';
9-
import _ from 'lodash';
109
import pjson from '../../package.json';
1110
import logger from '../logger';
1211

@@ -32,19 +31,6 @@ export function setupLogging(app: express.Application, config: Config): void {
3231
app.use(middleware);
3332
}
3433

35-
/**
36-
* Setup debug namespaces
37-
*/
38-
export function setupDebugNamespaces(debugNamespace?: string[]): void {
39-
if (_.isArray(debugNamespace)) {
40-
for (const ns of debugNamespace) {
41-
if (ns) {
42-
logger.debug(`Enabling debug namespace: ${ns}`);
43-
}
44-
}
45-
}
46-
}
47-
4834
/**
4935
* Create common Express middleware
5036
*/
@@ -215,3 +201,33 @@ export function createMtlsMiddleware(config: {
215201
next();
216202
};
217203
}
204+
205+
/**
206+
* Validate that TLS certificates are properly loaded when TLS is enabled
207+
*/
208+
export function validateTlsCertificates(config: {
209+
tlsMode: TlsMode;
210+
tlsKey?: string;
211+
tlsCert?: string;
212+
}): void {
213+
if (config.tlsMode !== TlsMode.DISABLED) {
214+
if (!config.tlsKey || !config.tlsCert) {
215+
throw new Error('TLS is enabled but certificates are not properly loaded');
216+
}
217+
}
218+
}
219+
220+
/**
221+
* Validate Master Express configuration
222+
*/
223+
export function validateMasterExpressConfig(config: {
224+
enclavedExpressUrl: string;
225+
enclavedExpressCert: string;
226+
}): void {
227+
if (!config.enclavedExpressUrl) {
228+
throw new Error('ENCLAVED_EXPRESS_URL is required for Master Express mode');
229+
}
230+
if (!config.enclavedExpressCert) {
231+
throw new Error('ENCLAVED_EXPRESS_CERT is required for Master Express mode');
232+
}
233+
}

0 commit comments

Comments
 (0)