Skip to content

Commit 873a97b

Browse files
authored
fix: add TLS secure context to migrate-mongo config for CDP environments (#494)
1 parent c414770 commit 873a97b

9 files changed

Lines changed: 51 additions & 41 deletions

File tree

Dockerfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ USER node
3333

3434
COPY --from=development /home/node/package*.json ./
3535
COPY --from=development /home/node/.server ./.server/
36-
COPY --from=development /home/node/migrate-mongo-config.cjs ./
36+
COPY --from=development /home/node/migrate-mongo-config.js ./
37+
# config/index.js and secure-context.js are dependencies for migrate-mongo-config.js
38+
COPY --from=development /home/node/src/secure-context.js ./src/secure-context.js
39+
COPY --from=development /home/node/src/config/index.js ./src/config/index.js
3740
COPY --from=development /home/node/migrations ./migrations/
3841
COPY --from=development /home/node/scripts ./scripts/
3942

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ npm run migrate:up
261261
npm run migrate:down
262262

263263
# Create a new migration
264-
npx migrate-mongo create <migration-name> -f migrate-mongo-config.cjs
264+
npx migrate-mongo create <migration-name> -f migrate-mongo-config.js
265265
```
266266

267267
**Important**: When running migrations manually, ensure your `.env` file contains the correct `MONGO_URI` and `MONGO_DATABASE` values that match your local MongoDB instance.
Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
/* eslint-disable */
2-
// In this file you can configure migrate-mongo
3-
require('dotenv/config')
1+
import 'dotenv/config'
42

5-
const config = {
6-
mongodb: {
7-
// Use environment variables to match the app's configuration
8-
url: process.env.MONGO_URI || 'mongodb://127.0.0.1:27017/',
3+
import { config as appConfig } from './src/config/index.js'
4+
import { prepareSecureContext } from './src/secure-context.js'
95

10-
// Use the same database name as the app
11-
databaseName: process.env.MONGO_DATABASE || 'forms-submission-api',
6+
// Prepare secure context for MongoDB TLS connections
7+
if (appConfig.get('isProduction')) {
8+
prepareSecureContext(console.log) // eslint-disable-line no-console
9+
}
1210

11+
const config = {
12+
mongodb: {
13+
url: appConfig.get('mongo.uri'),
14+
databaseName: appConfig.get('mongo.databaseName'),
1315
options: {
1416
// connectTimeoutMS: 3600000, // increase connection timeout to 1 hour
1517
// socketTimeoutMS: 3600000, // increase socket timeout to 1 hour
@@ -29,14 +31,14 @@ const config = {
2931
lockTtl: 0,
3032

3133
// The file extension to create migrations and search for in migration dir
32-
migrationFileExtension: '.cjs',
34+
migrationFileExtension: '.js',
3335

3436
// Enable the algorithm to create a checksum of the file contents and use that in the comparison to determine
3537
// if the file should be run. Requires that scripts are coded to be run multiple times.
3638
useFileHash: false,
3739

3840
// Don't change this, unless you know what you're doing
39-
moduleSystem: 'commonjs'
41+
moduleSystem: 'esm'
4042
}
4143

42-
module.exports = config
44+
export default config

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@
3535
"prestart": "npm run build",
3636
"start": "NODE_ENV=${NODE_ENV:-production} node --enable-source-maps ./.server",
3737
"setup:husky": "node -e \"try { (await import('husky')).default() } catch (e) { if (e.code !== 'ERR_MODULE_NOT_FOUND') throw e }\" --input-type module",
38-
"migrate:up": "migrate-mongo up -f migrate-mongo-config.cjs",
39-
"migrate:down": "migrate-mongo down -f migrate-mongo-config.cjs",
40-
"migrate:status": "migrate-mongo status -f migrate-mongo-config.cjs"
38+
"migrate:up": "migrate-mongo up -f migrate-mongo-config.js",
39+
"migrate:down": "migrate-mongo down -f migrate-mongo-config.js",
40+
"migrate:status": "migrate-mongo status -f migrate-mongo-config.js"
4141
},
4242
"dependencies": {
4343
"@aws-sdk/client-s3": "^3.679.0",

scripts/run-migrations-and-start.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ set -e
55

66
echo "Running database migrations..."
77

8-
# Run migrations with the config file (.cjs extension required for ES module projects)
9-
npx migrate-mongo up -f migrate-mongo-config.cjs
8+
# Run migrations with the config file
9+
npx migrate-mongo up -f migrate-mongo-config.js
1010

1111
echo "Migrations complete. Starting application..."
1212

src/api/server.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ export async function createServer() {
8686
])
8787

8888
if (isProduction) {
89-
prepareSecureContext(server)
89+
prepareSecureContext((message) => {
90+
server.logger.info(message)
91+
})
9092
}
9193

9294
await prepareDb(server.logger)

src/helpers/secure-context/get-trust-store-certs.js

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

src/helpers/secure-context/get-trust-store-certs.test.js renamed to src/helpers/secure-context/secure-context.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getTrustStoreCerts } from '~/src/helpers/secure-context/get-trust-store-certs.js'
1+
import { getTrustStoreCerts } from '~/src/secure-context.js'
22

33
describe('#getTrustStoreCerts', () => {
44
const mockProcessEnvWithCerts = {

src/secure-context.js

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
1+
/*
2+
WARNING: this file is imported by migrate-mongo which does not use babel. Avoid importing assets from the wider project as they'll use tilde imports.
3+
This file is copied verbatim into the production Docker image at src/secure-context.js. Imports will not be copied.
4+
If this grows in complexity and needs external dependencies, it will need to be folded into babel.
5+
*/
16
import tls from 'node:tls'
27

3-
import { getTrustStoreCerts } from '~/src/helpers/secure-context/get-trust-store-certs.js'
4-
58
/**
69
* @type {SecureContext}
710
*/
811
export let secureContext
912

1013
/**
1114
* Prepares the TLS secure context
12-
* @param {Server} server
1315
* @returns
16+
* @param {(arg: string) => void} loggerFn
1417
*/
15-
export function prepareSecureContext(server) {
18+
export function prepareSecureContext(loggerFn) {
1619
const originalCreateSecureContext = tls.createSecureContext
1720

1821
tls.createSecureContext = function (options = {}) {
1922
const trustStoreCerts = getTrustStoreCerts(process.env)
2023

2124
if (!trustStoreCerts.length) {
22-
server.logger.info('Could not find any TRUSTSTORE_ certificates')
25+
loggerFn('Could not find any TRUSTSTORE_ certificates')
2326
}
2427

2528
const originalSecureContext = originalCreateSecureContext(options)
@@ -37,6 +40,20 @@ export function prepareSecureContext(server) {
3740
}
3841

3942
/**
40-
* @import { Server } from '@hapi/hapi'
43+
* Get base64 certs from all environment variables starting with TRUSTSTORE_
44+
* @param {NodeJS.ProcessEnv} envs
45+
* @returns {string[]}
46+
*/
47+
export function getTrustStoreCerts(envs) {
48+
return Object.entries(envs)
49+
.map(([key, value]) => key.startsWith('TRUSTSTORE_') && value)
50+
.filter(
51+
/** @returns {envValue is string} */
52+
(envValue) => Boolean(envValue)
53+
)
54+
.map((envValue) => Buffer.from(envValue, 'base64').toString().trim())
55+
}
56+
57+
/**
4158
* @import { SecureContext } from 'node:tls'
4259
*/

0 commit comments

Comments
 (0)