Skip to content

Commit d26c7cc

Browse files
authored
Merge pull request #1 from BitGo/WP-4352-setup-enclaved-bitgo-express
feat: setup enclaved bitgo express with mTLS support
2 parents 1ea199c + 00bdafa commit d26c7cc

23 files changed

+5228
-2
lines changed

.eslintrc.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module.exports = {
2+
parser: '@typescript-eslint/parser',
3+
extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'],
4+
parserOptions: {
5+
ecmaVersion: 2018,
6+
sourceType: 'module',
7+
},
8+
rules: {
9+
'@typescript-eslint/explicit-function-return-type': 'off',
10+
'@typescript-eslint/no-explicit-any': 'off',
11+
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
12+
},
13+
ignorePatterns: ['dist/**/*', 'node_modules/**/*'],
14+
};
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
name: Build & Test (CI)
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
node-version:
7+
description: 'Node.js version to use'
8+
type: string
9+
default: '20.18.0'
10+
11+
jobs:
12+
build:
13+
name: Build
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v4
18+
19+
- name: Setup Node.js
20+
uses: actions/setup-node@v4
21+
with:
22+
node-version: ${{ inputs.node-version }}
23+
cache: 'yarn'
24+
25+
- name: Install dependencies
26+
run: yarn install
27+
28+
- name: Build
29+
run: yarn build
30+
31+
lint:
32+
name: Run lint
33+
runs-on: ubuntu-latest
34+
steps:
35+
- name: Checkout
36+
uses: actions/checkout@v4
37+
38+
- name: Setup Node.js
39+
uses: actions/setup-node@v4
40+
with:
41+
node-version: ${{ inputs.node-version }}
42+
cache: 'yarn'
43+
44+
- name: Install dependencies
45+
run: yarn install
46+
47+
- name: Lint
48+
run: yarn lint
49+
50+
test:
51+
name: Test
52+
runs-on: ubuntu-latest
53+
steps:
54+
- name: Checkout
55+
uses: actions/checkout@v4
56+
57+
- name: Setup Node.js
58+
uses: actions/setup-node@v4
59+
with:
60+
node-version: ${{ inputs.node-version }}
61+
cache: 'yarn'
62+
63+
- name: Install dependencies
64+
run: yarn install
65+
66+
- name: Generate test SSL certificates
67+
run: yarn generate-test-ssl
68+
69+
- name: Test
70+
run: yarn test
71+
env:
72+
MASTER_BITGO_EXPRESS_KEYPATH: ./test-ssl-key.pem
73+
MASTER_BITGO_EXPRESS_CRTPATH: ./test-ssl-cert.pem
74+
MTLS_ENABLED: true
75+
MTLS_REQUEST_CERT: true
76+
MTLS_REJECT_UNAUTHORIZED: false
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: on_pr
2+
run-name: "on_pr: ${{ github.event.pull_request.number }}"
3+
4+
on:
5+
pull_request:
6+
7+
permissions:
8+
actions: read # required by BitGo/build-system
9+
contents: read # required by BitGo/build-system
10+
id-token: write # required by BitGo/build-system
11+
pull-requests: write # required by Grype PR commenter
12+
packages: read # required for ArgoCD deploy
13+
14+
jobs:
15+
build-and-test:
16+
name: Build & Test (CI)
17+
uses: ./.github/workflows/build-and-test.yaml

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dist/
2+
node_modules/
3+
coverage/

.npmignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
!dist/
2+
.nyc_output/
3+
tsconfig.json
4+
src/
5+
test/
6+
*.tgz

.prettierrc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"singleQuote": true,
3+
"trailingComma": "all",
4+
"printWidth": 100,
5+
"tabWidth": 2,
6+
"semi": true
7+
}

README.md

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,120 @@
1-
# enclaved-bitgo-express
2-
Enclaved BitGo Express for Advanced Key Management
1+
# Enclaved Express
2+
3+
Enclaved Express is a secure signer implementation for cryptocurrency operations. It's designed to run in a secure enclave environment with flexible security options.
4+
5+
## Overview
6+
7+
This module provides a lightweight, dedicated signing server with these features:
8+
9+
- Focused on signing operations only - no BitGo API dependencies
10+
- Optional TLS security for secure connections
11+
- Client certificate validation when operating in mTLS mode
12+
- Simple configuration and deployment
13+
14+
## Supported Operations
15+
16+
Currently, the following operations are supported:
17+
18+
- `/ping` - Health check endpoint
19+
20+
## Configuration
21+
22+
Configuration is done via environment variables:
23+
24+
### Network Settings
25+
26+
- `PORT` - Port to listen on (default: 3080)
27+
- `BIND` - Address to bind to (default: localhost)
28+
- `TIMEOUT` - Request timeout in milliseconds (default: 305000)
29+
30+
### TLS Settings
31+
32+
- `MASTER_BITGO_EXPRESS_KEYPATH` - Path to server key file (required for TLS)
33+
- `MASTER_BITGO_EXPRESS_CRTPATH` - Path to server certificate file (required for TLS)
34+
- `MTLS_ENABLED` - Enable mTLS mode (default: false)
35+
- `MTLS_REQUEST_CERT` - Whether to request client certificates (default: false)
36+
- `MTLS_REJECT_UNAUTHORIZED` - Whether to reject unauthorized connections (default: false)
37+
- `MTLS_ALLOWED_CLIENT_FINGERPRINTS` - Comma-separated list of allowed client certificate fingerprints (optional)
38+
39+
### Other Settings
40+
41+
- `LOGFILE` - Path to log file (optional)
42+
- `DEBUG` - Debug namespaces to enable (e.g., 'enclaved:*')
43+
44+
## Running Enclaved Express
45+
46+
### Basic Setup (HTTP only)
47+
48+
```bash
49+
yarn start --port 3080
50+
```
51+
52+
### TLS Setup (with mTLS)
53+
54+
For testing purposes, you can use self-signed certificates with relaxed verification:
55+
56+
```bash
57+
MASTER_BITGO_EXPRESS_KEYPATH=./test-ssl-key.pem \
58+
MASTER_BITGO_EXPRESS_CRTPATH=./test-ssl-cert.pem \
59+
MTLS_ENABLED=true \
60+
MTLS_REQUEST_CERT=true \
61+
MTLS_REJECT_UNAUTHORIZED=false \
62+
yarn start --port 3080
63+
```
64+
65+
### Connecting from Regular Express
66+
67+
To connect to Enclaved Express from the regular Express server:
68+
69+
```bash
70+
yarn start --port 4000 \
71+
--enclavedExpressUrl='https://localhost:3080' \
72+
--enclavedExpressSSLCert='./test-ssl-cert.pem' \
73+
--disableproxy \
74+
--debug
75+
```
76+
77+
## Understanding mTLS Configuration
78+
79+
### Server Side (Enclaved Express)
80+
- Uses both certificate and key files
81+
- The key file (`test-ssl-key.pem`) is used to prove the server's identity
82+
- The certificate file (`test-ssl-cert.pem`) is what the server presents to clients
83+
84+
### Client Side (Regular Express)
85+
- For testing, only needs the server's certificate
86+
- `rejectUnauthorized: false` allows testing without strict certificate verification
87+
- In production, proper client certificates should be used
88+
89+
## Security Considerations
90+
91+
- The testing configuration (`MTLS_REJECT_UNAUTHORIZED=false`) should only be used in development
92+
- In production:
93+
- Use proper CA-signed certificates
94+
- Enable strict certificate verification
95+
- Use client certificate allowlisting
96+
- Keep private keys secure
97+
- Regularly rotate certificates
98+
99+
## Troubleshooting
100+
101+
### Common Issues
102+
103+
1. **Certificate Errors**
104+
- Ensure paths to certificate files are correct
105+
- Check file permissions on certificate files
106+
- Verify certificate format is correct
107+
108+
2. **Connection Issues**
109+
- Verify ports are not in use
110+
- Check firewall settings
111+
- Ensure URLs are correct (including https:// prefix)
112+
113+
3. **mTLS Errors**
114+
- Verify mTLS is enabled on both sides
115+
- Check certificate configuration
116+
- Ensure client certificate is trusted by server
117+
118+
## License
119+
120+
MIT

bin/enclaved-bitgo-express

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/usr/bin/env node
2+
3+
// TODO: Remove this unhandledRejection hook once BG-49996 is implemented.
4+
process.on('unhandledRejection', (reason, promise) => {
5+
console.error('----- Unhandled Rejection at -----');
6+
console.error(promise);
7+
console.error('----- Reason -----');
8+
console.error(reason);
9+
});
10+
11+
const { init } = require('../dist/src/enclavedApp');
12+
13+
if (require.main === module) {
14+
init().catch((err) => {
15+
console.log(`Fatal error: ${err.message}`);
16+
console.log(err.stack);
17+
});
18+
}

jest.config.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module.exports = {
2+
preset: 'ts-jest',
3+
testEnvironment: 'node',
4+
roots: ['<rootDir>/src'],
5+
testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
6+
transform: {
7+
'^.+\\.ts$': 'ts-jest',
8+
},
9+
moduleFileExtensions: ['ts', 'js', 'json', 'node'],
10+
collectCoverage: true,
11+
coverageDirectory: 'coverage',
12+
coverageReporters: ['text', 'lcov'],
13+
verbose: true,
14+
};

package.json

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"name": "@bitgo/enclaved-bitgo-express",
3+
"version": "1.0.0",
4+
"description": "BitGo Enclaved Express - Secure enclave for BitGo signing operations with mTLS",
5+
"main": "./dist/src/index.js",
6+
"types": "./dist/src/index.d.ts",
7+
"bin": {
8+
"enclaved-bitgo-express": "./bin/enclaved-bitgo-express"
9+
},
10+
"scripts": {
11+
"start": "node bin/enclaved-bitgo-express",
12+
"build": "yarn tsc --build --incremental --verbose . && cp package.json dist/",
13+
"test": "jest",
14+
"test:watch": "jest --watch",
15+
"test:coverage": "jest --coverage",
16+
"lint": "eslint --quiet .",
17+
"generate-test-ssl": "openssl req -x509 -newkey rsa:2048 -keyout test-ssl-key.pem -out test-ssl-cert.pem -days 365 -nodes -subj '/CN=localhost'"
18+
},
19+
"dependencies": {
20+
"body-parser": "^1.20.3",
21+
"connect-timeout": "^1.9.0",
22+
"debug": "^3.1.0",
23+
"express": "4.17.3",
24+
"lodash": "^4.17.20",
25+
"morgan": "^1.9.1"
26+
},
27+
"devDependencies": {
28+
"@types/body-parser": "^1.17.0",
29+
"@types/connect-timeout": "^1.9.0",
30+
"@types/debug": "^4.1.12",
31+
"@types/express": "4.17.13",
32+
"@types/jest": "^29.5.12",
33+
"@types/lodash": "^4.14.121",
34+
"@types/morgan": "^1.7.35",
35+
"@types/node": "^16.18.46",
36+
"@types/sinon": "^10.0.11",
37+
"@types/supertest": "^2.0.11",
38+
"@typescript-eslint/eslint-plugin": "^5.0.0",
39+
"@typescript-eslint/parser": "^5.0.0",
40+
"eslint": "^8.0.0",
41+
"eslint-config-prettier": "^8.0.0",
42+
"eslint-plugin-prettier": "^4.0.0",
43+
"jest": "^29.7.0",
44+
"nock": "^13.3.1",
45+
"nyc": "^15.0.0",
46+
"prettier": "^2.0.0",
47+
"should": "^13.2.3",
48+
"should-http": "^0.1.1",
49+
"should-sinon": "^0.0.6",
50+
"sinon": "^13.0.1",
51+
"supertest": "^4.0.2",
52+
"ts-jest": "^29.1.2",
53+
"typescript": "^4.2.4"
54+
},
55+
"engines": {
56+
"node": ">=14"
57+
}
58+
}

0 commit comments

Comments
 (0)