Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 36 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ Currently, the following operations are supported:

Configuration is done via environment variables:

### Required Settings

- `APP_MODE` - Application mode (required, must be either "enclaved" or "master-express")

### Network Settings

- `PORT` - Port to listen on (default: 3080)
Expand All @@ -36,10 +40,20 @@ Configuration is done via environment variables:
- `MTLS_REJECT_UNAUTHORIZED` - Whether to reject unauthorized connections (default: false)
- `MTLS_ALLOWED_CLIENT_FINGERPRINTS` - Comma-separated list of allowed client certificate fingerprints (optional)

### Master Express Settings

- `BITGO_PORT` - Port to listen on (default: 3080)
- `BITGO_BIND` - Address to bind to (default: localhost)
- `BITGO_ENV` - Environment name (default: test)
- `BITGO_ENABLE_SSL` - Enable SSL and certificate verification (default: true)
- `BITGO_ENABLE_PROXY` - Enable proxy (default: true)
- `ENCLAVED_EXPRESS_URL` - URL of the enclaved express server (required)
- `ENCLAVED_EXPRESS_SSL_CERT` - Path to the enclaved express server's SSL certificate (required)

### Other Settings

- `LOGFILE` - Path to log file (optional)
- `DEBUG` - Debug namespaces to enable (e.g., 'enclaved:*')
- `DEBUG` - Debug namespaces to enable (e.g., 'enclaved:\*')

## Running Enclaved Express

Expand All @@ -54,34 +68,44 @@ yarn start --port 3080
For testing purposes, you can use self-signed certificates with relaxed verification:

```bash
APP_MODE=enclaved \
MASTER_BITGO_EXPRESS_PORT=3080 \
MASTER_BITGO_EXPRESS_BIND=localhost \
MASTER_BITGO_EXPRESS_KEYPATH=./test-ssl-key.pem \
MASTER_BITGO_EXPRESS_CRTPATH=./test-ssl-cert.pem \
MTLS_ENABLED=true \
MTLS_REQUEST_CERT=true \
MTLS_REJECT_UNAUTHORIZED=false \
yarn start --port 3080
yarn start
```

### Connecting from Regular Express
### Connecting from Master Express

To connect to Enclaved Express from the regular Express server:
To connect to Enclaved Express from the Master Express server:

```bash
yarn start --port 4000 \
--enclavedExpressUrl='https://localhost:3080' \
--enclavedExpressSSLCert='./test-ssl-cert.pem' \
--disableproxy \
--debug
APP_MODE=master-express \
BITGO_PORT=3080 \
BITGO_BIND=localhost \
BITGO_ENV=test \
BITGO_KEYPATH=./test-ssl-key.pem \
BITGO_CRTPATH=./test-ssl-cert.pem \
ENCLAVED_EXPRESS_URL=https://localhost:4000 \
ENCLAVED_EXPRESS_SSL_CERT=./enclaved-express-cert.pem \
BITGO_ENABLE_SSL=false \
yarn start
```

## Understanding mTLS Configuration

### Server Side (Enclaved Express)

- Uses both certificate and key files
- The key file (`test-ssl-key.pem`) is used to prove the server's identity
- The certificate file (`test-ssl-cert.pem`) is what the server presents to clients

### Client Side (Regular Express)

- For testing, only needs the server's certificate
- `rejectUnauthorized: false` allows testing without strict certificate verification
- In production, proper client certificates should be used
Expand All @@ -101,11 +125,13 @@ yarn start --port 4000 \
### Common Issues

1. **Certificate Errors**

- Ensure paths to certificate files are correct
- Check file permissions on certificate files
- Verify certificate format is correct

2. **Connection Issues**

- Verify ports are not in use
- Check firewall settings
- Ensure URLs are correct (including https:// prefix)
Expand All @@ -117,4 +143,4 @@ yarn start --port 4000 \

## License

MIT
MIT
2 changes: 1 addition & 1 deletion bin/enclaved-bitgo-express
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ process.on('unhandledRejection', (reason, promise) => {
console.error(reason);
});

const { init } = require('../dist/src/enclavedApp');
const { init } = require('../dist/src/app');

if (require.main === module) {
init().catch((err) => {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"debug": "^3.1.0",
"express": "4.17.3",
"lodash": "^4.17.20",
"morgan": "^1.9.1"
"morgan": "^1.9.1",
"superagent": "^8.0.9"
},
"devDependencies": {
"@types/body-parser": "^1.17.0",
Expand Down
98 changes: 64 additions & 34 deletions src/__tests__/config.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { config, TlsMode } from '../config';
import { config, isEnclavedConfig, TlsMode } from '../config';

describe('Configuration', () => {
const originalEnv = process.env;
Expand All @@ -15,46 +15,76 @@ describe('Configuration', () => {
process.env = originalEnv;
});

it('should use default configuration when no environment variables are set', () => {
const cfg = config();
expect(cfg.port).toBe(3080);
expect(cfg.bind).toBe('localhost');
expect(cfg.tlsMode).toBe(TlsMode.ENABLED);
expect(cfg.timeout).toBe(305 * 1000);
it('should throw error when APP_MODE is not set', () => {
expect(() => config()).toThrow('APP_MODE environment variable is required');
});

it('should read port from environment variable', () => {
process.env.MASTER_BITGO_EXPRESS_PORT = '4000';
const cfg = config();
expect(cfg.port).toBe(4000);
it('should throw error when APP_MODE is invalid', () => {
process.env.APP_MODE = 'invalid';
expect(() => config()).toThrow('Invalid APP_MODE: invalid');
});

it('should read TLS mode from environment variables', () => {
process.env.MASTER_BITGO_EXPRESS_DISABLE_TLS = 'true';
let cfg = config();
expect(cfg.tlsMode).toBe(TlsMode.DISABLED);
describe('Enclaved Mode', () => {
beforeEach(() => {
process.env.APP_MODE = 'enclaved';
});

process.env.MASTER_BITGO_EXPRESS_DISABLE_TLS = 'false';
process.env.MTLS_ENABLED = 'true';
cfg = config();
expect(cfg.tlsMode).toBe(TlsMode.MTLS);
});
it('should use default configuration when no environment variables are set', () => {
const cfg = config();
expect(isEnclavedConfig(cfg)).toBe(true);
if (isEnclavedConfig(cfg)) {
expect(cfg.port).toBe(3080);
expect(cfg.bind).toBe('localhost');
expect(cfg.tlsMode).toBe(TlsMode.ENABLED);
expect(cfg.timeout).toBe(305 * 1000);
}
});

it('should throw error when both TLS disabled and mTLS enabled', () => {
process.env.MASTER_BITGO_EXPRESS_DISABLE_TLS = 'true';
process.env.MTLS_ENABLED = 'true';
expect(() => config()).toThrow('Cannot have both TLS disabled and mTLS enabled');
});
it('should read port from environment variable', () => {
process.env.MASTER_BITGO_EXPRESS_PORT = '4000';
const cfg = config();
expect(isEnclavedConfig(cfg)).toBe(true);
if (isEnclavedConfig(cfg)) {
expect(cfg.port).toBe(4000);
}
});

it('should read TLS mode from environment variables', () => {
process.env.MASTER_BITGO_EXPRESS_DISABLE_TLS = 'true';
let cfg = config();
expect(isEnclavedConfig(cfg)).toBe(true);
if (isEnclavedConfig(cfg)) {
expect(cfg.tlsMode).toBe(TlsMode.DISABLED);
}

process.env.MASTER_BITGO_EXPRESS_DISABLE_TLS = 'false';
process.env.MTLS_ENABLED = 'true';
cfg = config();
expect(isEnclavedConfig(cfg)).toBe(true);
if (isEnclavedConfig(cfg)) {
expect(cfg.tlsMode).toBe(TlsMode.MTLS);
}
});

it('should throw error when both TLS disabled and mTLS enabled', () => {
process.env.MASTER_BITGO_EXPRESS_DISABLE_TLS = 'true';
process.env.MTLS_ENABLED = 'true';
expect(() => config()).toThrow('Cannot have both TLS disabled and mTLS enabled');
});

it('should read mTLS settings from environment variables', () => {
process.env.MTLS_ENABLED = 'true';
process.env.MTLS_REQUEST_CERT = 'true';
process.env.MTLS_REJECT_UNAUTHORIZED = 'true';
process.env.MTLS_ALLOWED_CLIENT_FINGERPRINTS = 'ABC123,DEF456';
it('should read mTLS settings from environment variables', () => {
process.env.MTLS_ENABLED = 'true';
process.env.MTLS_REQUEST_CERT = 'true';
process.env.MTLS_REJECT_UNAUTHORIZED = 'true';
process.env.MTLS_ALLOWED_CLIENT_FINGERPRINTS = 'ABC123,DEF456';

const cfg = config();
expect(cfg.mtlsRequestCert).toBe(true);
expect(cfg.mtlsRejectUnauthorized).toBe(true);
expect(cfg.mtlsAllowedClientFingerprints).toEqual(['ABC123', 'DEF456']);
const cfg = config();
expect(isEnclavedConfig(cfg)).toBe(true);
if (isEnclavedConfig(cfg)) {
expect(cfg.mtlsRequestCert).toBe(true);
expect(cfg.mtlsRejectUnauthorized).toBe(true);
expect(cfg.mtlsAllowedClientFingerprints).toEqual(['ABC123', 'DEF456']);
}
});
});
});
26 changes: 26 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* @prettier
*/
import { config, isEnclavedConfig, isMasterExpressConfig } from './config';
import * as enclavedApp from './enclavedApp';
import * as masterExpressApp from './masterExpressApp';

/**
* Main application entry point that determines the mode and starts the appropriate app
*/
export async function init(): Promise<void> {
const cfg = config();

if (isEnclavedConfig(cfg)) {
console.log('Starting in Enclaved mode...');
await enclavedApp.init();
} else if (isMasterExpressConfig(cfg)) {
console.log('Starting in Master Express mode...');
await masterExpressApp.init();
} else {
throw new Error(`Unknown app mode: ${(cfg as any).appMode}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't need this if config() returns Config

}
}

// Export the individual app modules for direct access if needed
export { enclavedApp, masterExpressApp };
Loading