A secure, mTLS-enabled cryptocurrency signing server with two operational modes: Enclaved Express (dedicated signer) and Master Express (API gateway with integrated signing capabilities).
This application provides secure cryptocurrency operations with mutual TLS (mTLS) authentication:
- Enclaved Mode: Lightweight signing server for secure key operations
- Master Express Mode: Full BitGo Express functionality with integrated signing
- mTLS Security: Client certificate validation for secure communications
- Flexible Configuration: Environment-based setup with file or variable-based certificates
- Enclaved Express (Port 3080): Focused signing operations with KMS integration
- Master Express (Port 3081): Full BitGo API functionality with secure communication to Enclaved Express
Configuration is managed through environment variables:
APP_MODE- Application mode (required: "enclaved" or "master-express")
BIND- Address to bind to (default: localhost)TIMEOUT- Request timeout in milliseconds (default: 305000)KEEP_ALIVE_TIMEOUT- Keep-alive timeout (optional)HEADERS_TIMEOUT- Headers timeout (optional)
ENCLAVED_EXPRESS_PORT- Port to listen on (default: 3080)KMS_URL- KMS service URL (required)
MASTER_EXPRESS_PORT- Port to listen on (default: 3081)BITGO_ENV- BitGo environment (default: test)BITGO_DISABLE_ENV_CHECK- Disable environment check (default: true)BITGO_AUTH_VERSION- Authentication version (default: 2)BITGO_CUSTOM_ROOT_URI- Custom BitGo API root URI (optional)BITGO_CUSTOM_BITCOIN_NETWORK- Custom Bitcoin network (optional)ENCLAVED_EXPRESS_URL- Enclaved Express server URL (required)ENCLAVED_EXPRESS_CERT- Path to Enclaved Express server certificate (required)
Both modes use the same TLS configuration variables:
TLS_MODE- Set to either "mtls" or "disabled" (defaults to "mtls" if not set)
Option 1: Certificate Files
TLS_KEY_PATH- Path to private key fileTLS_CERT_PATH- Path to certificate file
Option 2: Environment Variables
TLS_KEY- Private key content (PEM format)TLS_CERT- Certificate content (PEM format)
MTLS_REQUEST_CERT- Request client certificates (default: true)ALLOW_SELF_SIGNED- Allow self-signed certificates (default: false)MTLS_ALLOWED_CLIENT_FINGERPRINTS- Comma-separated list of allowed client certificate fingerprints (optional)
HTTP_LOGFILE- Path to HTTP request log file (optional, used by Morgan for HTTP access logs)
First, create self-signed certificates for testing:
# Generate private key
openssl genrsa -out server.key 2048
# Generate certificate
openssl req -new -x509 -key server.key -out server.crt -days 365 -subj "/CN=localhost"export APP_MODE=enclaved
export KMS_URL=https://your-kms-service
export TLS_KEY_PATH=./server.key
export TLS_CERT_PATH=./server.crt
export MTLS_REQUEST_CERT=true
export ALLOW_SELF_SIGNED=true
npm startIn a separate terminal:
export APP_MODE=master-express
export BITGO_ENV=test
export TLS_KEY_PATH=./server.key
export TLS_CERT_PATH=./server.crt
export ENCLAVED_EXPRESS_URL=https://localhost:3080
export ENCLAVED_EXPRESS_CERT=./server.crt
export MTLS_REQUEST_CERT=false
export ALLOW_SELF_SIGNED=true
npm startTest that Master Express can communicate with Enclaved Express:
curl -k -X POST https://localhost:3081/ping/enclavedExpress- Use CA-signed certificates instead of self-signed
- Set
ALLOW_SELF_SIGNED=falsein production - Configure client certificate allowlisting with
MTLS_ALLOWED_CLIENT_FINGERPRINTS - Use separate certificates for each service
- Regularly rotate certificates
- Secure private key storage
export APP_MODE=enclaved
export KMS_URL=https://production-kms.example.com
export TLS_KEY_PATH=/secure/path/enclaved.key
export TLS_CERT_PATH=/secure/path/enclaved.crt
export MTLS_REQUEST_CERT=true
export ALLOW_SELF_SIGNED=false
export MTLS_ALLOWED_CLIENT_FINGERPRINTS=ABC123...,DEF456...
npm startexport APP_MODE=master-express
export BITGO_ENV=prod
export TLS_KEY_PATH=/secure/path/master.key
export TLS_CERT_PATH=/secure/path/master.crt
export ENCLAVED_EXPRESS_URL=https://enclaved.internal.example.com:3080
export ENCLAVED_EXPRESS_CERT=/secure/path/enclaved.crt
export MTLS_REQUEST_CERT=true
export ALLOW_SELF_SIGNED=false
npm startFirst, build the container image:
# For Master Express (default port 3081)
npm run container:build
# For Enclaved Express (port 3080)
npm run container:build --build-arg PORT=3080For local development, you'll need to run both the Enclaved Express and Master Express containers:
# Start Enclaved Express container
podman run -d \
-p 3080:3080 \
-v $(pwd)/certs:/app/certs:Z \
-e APP_MODE=enclaved \
-e BIND=0.0.0.0 \
-e TLS_MODE=mtls \
-e TLS_KEY_PATH=/app/certs/enclaved-express-key.pem \
-e TLS_CERT_PATH=/app/certs/enclaved-express-cert.pem \
-e KMS_URL=host.containers.internal:3000 \
-e NODE_ENV=development \
-e ALLOW_SELF_SIGNED=true \
bitgo-onprem-express
# View logs
podman logs -f <container_id>
# Test the endpoint (note: using https)
curl -k -X POST https://localhost:3080/ping
# Start Master Express container
podman run -d \
-p 3081:3081 \
-v $(pwd)/certs:/app/certs:Z \
-e APP_MODE=master-express \
-e BIND=0.0.0.0 \
-e TLS_MODE=mtls \
-e TLS_KEY_PATH=/app/certs/test-ssl-key.pem \
-e TLS_CERT_PATH=/app/certs/test-ssl-cert.pem \
-e ENCLAVED_EXPRESS_URL=https://host.containers.internal:3080 \
-e ENCLAVED_EXPRESS_CERT=/app/certs/enclaved-express-cert.pem \
-e ALLOW_SELF_SIGNED=true \
bitgo-onprem-express
# View logs
podman logs -f <container_id>
# Test the endpoints (note: using https and mTLS)
# For Enclaved Express
curl -k --cert certs/test-ssl-cert.pem --key certs/enclaved-express-key.pem -X POST https://localhost:3080/ping
# For Master Express
curl -k --cert certs/test-ssl-cert.pem --key certs/test-ssl-key.pem -X POST https://localhost:3081/ping
# Test the connection
curl -k -X POST https://localhost:3081/ping/enclavedExpressNotes:
host.containers.internalis a special DNS name that resolves to the host machine from inside containers- The
:Zoption in volume mounts is specific to SELinux-enabled systems and ensures proper volume labeling - The logs directory will be created with appropriate permissions if it doesn't exist
POST /ping- Health checkGET /version- Version informationPOST /:coin/key/independent- Generate independent keychain
POST /ping- Health checkGET /version- Version informationPOST /ping/enclavedExpress- Test connection to Enclaved ExpressPOST /api/:coin/wallet/generate- Generate wallet (with Enclaved Express integration)
# Check certificate file paths and permissions
ls -la /path/to/certificates/
# Verify certificate format
openssl x509 -in certificate.crt -text -noout- Verify client certificates are provided
- Check
ALLOW_SELF_SIGNEDsetting matches certificate type - Confirm client certificate fingerprints are in allowlist
- Ensure both services use compatible TLS settings
- Verify both services are running on correct ports
- Check firewall settings
- Confirm URLs use
https://prefix - Test basic connectivity with curl
# Check that required variables are set
env | grep -E "(APP_MODE|KMS_URL|ENCLAVED_EXPRESS|TLS_)"Enable debug logging for detailed troubleshooting:
DEBUG_NAMESPACE=enclaved:*,master:* npm run startMIT