fix: load certificate files properly when running as pre-built binary

This commit is contained in:
Göran Sander
2025-07-31 09:48:14 +02:00
parent 67dd830330
commit e71b6b24b2
4 changed files with 205 additions and 317 deletions

View File

@@ -7,34 +7,32 @@ jest.unstable_mockModule('fs', () => ({
},
}));
jest.unstable_mockModule('../sea-wrapper.js', () => ({
default: {
getAsset: jest.fn(),
isSea: jest.fn(),
},
}));
jest.unstable_mockModule('../../globals.js', () => ({
default: {
logger: {
verbose: jest.fn(),
error: jest.fn(),
info: jest.fn(),
debug: jest.fn(),
},
isSea: false,
config: {
get: jest.fn(),
has: jest.fn(),
},
},
}));
// Import mocked modules
const fs = (await import('fs')).default;
const sea = (await import('../sea-wrapper.js')).default;
const globals = (await import('../../globals.js')).default;
// Import modules under test
const { getCertificates: getProxyCertificates } = await import('../proxysessionmetrics.js');
const { getCertificates: getHealthCertificates } = await import('../healthmetrics.js');
const { getCertificates: getCertificatesUtil, createCertificateOptions } = await import(
'../cert-utils.js'
);
describe('Certificate loading in SEA vs non-SEA modes', () => {
describe('Certificate loading', () => {
const mockCertificateOptions = {
Certificate: '/path/to/client.crt',
CertificateKey: '/path/to/client.key',
@@ -57,19 +55,14 @@ describe('Certificate loading in SEA vs non-SEA modes', () => {
globals.isSea = false;
});
describe('Non-SEA mode certificate loading', () => {
beforeEach(() => {
globals.isSea = false;
});
test('should load certificates from filesystem using fs.readFileSync - proxysessionmetrics', () => {
// Mock filesystem operations for this specific test
describe('Certificate utility function', () => {
test('should load certificates from filesystem', () => {
fs.readFileSync
.mockReturnValueOnce(mockCertData)
.mockReturnValueOnce(mockKeyData)
.mockReturnValueOnce(mockCaData);
const certificates = getProxyCertificates(mockCertificateOptions);
const certificates = getCertificatesUtil(mockCertificateOptions);
expect(fs.readFileSync).toHaveBeenCalledWith('/path/to/client.crt');
expect(fs.readFileSync).toHaveBeenCalledWith('/path/to/client.key');
@@ -82,77 +75,62 @@ describe('Certificate loading in SEA vs non-SEA modes', () => {
ca: mockCaData,
});
// Should not call SEA functions
expect(sea.getAsset).not.toHaveBeenCalled();
});
test('should load certificates from filesystem using fs.readFileSync - healthmetrics', () => {
// Mock filesystem operations for this specific test
fs.readFileSync
.mockReturnValueOnce(mockCertData)
.mockReturnValueOnce(mockKeyData)
.mockReturnValueOnce(mockCaData);
const certificates = getHealthCertificates(mockCertificateOptions);
expect(fs.readFileSync).toHaveBeenCalledWith('/path/to/client.crt');
expect(fs.readFileSync).toHaveBeenCalledWith('/path/to/client.key');
expect(fs.readFileSync).toHaveBeenCalledWith('/path/to/ca.crt');
expect(fs.readFileSync).toHaveBeenCalledTimes(3);
expect(certificates).toEqual({
cert: mockCertData,
key: mockKeyData,
ca: mockCaData,
});
// Should not call SEA functions
expect(sea.getAsset).not.toHaveBeenCalled();
});
test('should handle filesystem errors gracefully - proxysessionmetrics', () => {
fs.readFileSync.mockImplementation((path) => {
throw new Error(`ENOENT: no such file or directory, open '${path}'`);
});
expect(() => getProxyCertificates(mockCertificateOptions)).toThrow(
"ENOENT: no such file or directory, open '/path/to/client.crt'"
expect(globals.logger.debug).toHaveBeenCalledWith(
'Loading certificates from disk. SEA mode: false'
);
expect(globals.logger.debug).toHaveBeenCalledWith(
'Loading certificates from disk. cert=/path/to/client.crt'
);
expect(globals.logger.debug).toHaveBeenCalledWith(
'Loading certificates from disk. key=/path/to/client.key'
);
expect(globals.logger.debug).toHaveBeenCalledWith(
'Loading certificates from disk. ca=/path/to/ca.crt'
);
expect(fs.readFileSync).toHaveBeenCalledWith('/path/to/client.crt');
});
test('should handle filesystem errors gracefully - healthmetrics', () => {
test('should throw error when certificate paths are undefined', () => {
const invalidOptions = {
Certificate: undefined,
CertificateKey: '/path/to/client.key',
CertificateCA: '/path/to/ca.crt',
};
expect(() => getCertificatesUtil(invalidOptions)).toThrow(
'Certificate paths are not properly defined'
);
});
test('should throw error when filesystem read fails', () => {
fs.readFileSync.mockImplementation((path) => {
throw new Error(`ENOENT: no such file or directory, open '${path}'`);
});
expect(() => getHealthCertificates(mockCertificateOptions)).toThrow(
"ENOENT: no such file or directory, open '/path/to/client.crt'"
expect(() => getCertificatesUtil(mockCertificateOptions)).toThrow(
'Failed to load certificates from filesystem'
);
expect(fs.readFileSync).toHaveBeenCalledWith('/path/to/client.crt');
});
});
describe('SEA mode certificate loading', () => {
describe('SEA mode behavior', () => {
beforeEach(() => {
globals.isSea = true;
});
test('should load certificates from SEA assets using sea.getAsset - proxysessionmetrics', () => {
// Mock SEA asset operations for this specific test
sea.getAsset
test('should still load certificates from filesystem in SEA mode', () => {
fs.readFileSync
.mockReturnValueOnce(mockCertData)
.mockReturnValueOnce(mockKeyData)
.mockReturnValueOnce(mockCaData);
const certificates = getProxyCertificates(mockCertificateOptions);
const certificates = getCertificatesUtil(mockCertificateOptions);
expect(sea.getAsset).toHaveBeenCalledWith('/path/to/client.crt', 'utf8');
expect(sea.getAsset).toHaveBeenCalledWith('/path/to/client.key', 'utf8');
expect(sea.getAsset).toHaveBeenCalledWith('/path/to/ca.crt', 'utf8');
expect(sea.getAsset).toHaveBeenCalledTimes(3);
expect(fs.readFileSync).toHaveBeenCalledWith('/path/to/client.crt');
expect(fs.readFileSync).toHaveBeenCalledWith('/path/to/client.key');
expect(fs.readFileSync).toHaveBeenCalledWith('/path/to/ca.crt');
expect(fs.readFileSync).toHaveBeenCalledTimes(3);
expect(certificates).toEqual({
cert: mockCertData,
@@ -160,93 +138,75 @@ describe('Certificate loading in SEA vs non-SEA modes', () => {
ca: mockCaData,
});
// Should not call filesystem functions
expect(fs.readFileSync).not.toHaveBeenCalled();
expect(globals.logger.debug).toHaveBeenCalledWith(
'Loading certificates from disk. SEA mode: true'
);
});
test('should load certificates from SEA assets using sea.getAsset - healthmetrics', () => {
// Mock SEA asset operations for this specific test
sea.getAsset
.mockReturnValueOnce(mockCertData)
.mockReturnValueOnce(mockKeyData)
.mockReturnValueOnce(mockCaData);
const certificates = getHealthCertificates(mockCertificateOptions);
expect(sea.getAsset).toHaveBeenCalledWith('/path/to/client.crt', 'utf8');
expect(sea.getAsset).toHaveBeenCalledWith('/path/to/client.key', 'utf8');
expect(sea.getAsset).toHaveBeenCalledWith('/path/to/ca.crt', 'utf8');
expect(sea.getAsset).toHaveBeenCalledTimes(3);
expect(certificates).toEqual({
cert: mockCertData,
key: mockKeyData,
ca: mockCaData,
test('should handle filesystem errors in SEA mode', () => {
fs.readFileSync.mockImplementation((path) => {
throw new Error(`ENOENT: no such file or directory, open '${path}'`);
});
// Should not call filesystem functions
expect(fs.readFileSync).not.toHaveBeenCalled();
expect(() => getCertificatesUtil(mockCertificateOptions)).toThrow(
'Failed to load certificates from filesystem'
);
});
});
describe('Certificate options creation', () => {
test('should create certificate options from global configuration', () => {
// Mock the config get calls
globals.config.get
.mockReturnValueOnce('cert/client.crt')
.mockReturnValueOnce('cert/client.key')
.mockReturnValueOnce('cert/ca.crt');
globals.config.has.mockReturnValue(false);
const options = createCertificateOptions();
expect(globals.config.get).toHaveBeenCalledWith('Butler-SOS.cert.clientCert');
expect(globals.config.get).toHaveBeenCalledWith('Butler-SOS.cert.clientCertKey');
expect(globals.config.get).toHaveBeenCalledWith('Butler-SOS.cert.clientCertCA');
expect(globals.config.has).toHaveBeenCalledWith('Butler-SOS.cert.clientCertPassphrase');
// Verify the paths are resolved to absolute paths
expect(options.Certificate).toMatch(/cert\/client\.crt$/);
expect(options.CertificateKey).toMatch(/cert\/client\.key$/);
expect(options.CertificateCA).toMatch(/cert\/ca\.crt$/);
expect(options.CertificatePassphrase).toBeNull();
});
test('should handle missing SEA assets gracefully - proxysessionmetrics', () => {
sea.getAsset.mockReturnValue(undefined);
test('should include passphrase when configured', () => {
globals.config.get
.mockReturnValueOnce('cert/client.crt')
.mockReturnValueOnce('cert/client.key')
.mockReturnValueOnce('cert/ca.crt')
.mockReturnValue('my-passphrase'); // Any subsequent calls return the passphrase
globals.config.has.mockReturnValue(true);
const certificates = getProxyCertificates(mockCertificateOptions);
const options = createCertificateOptions();
expect(sea.getAsset).toHaveBeenCalledWith('/path/to/client.crt', 'utf8');
expect(certificates).toEqual({
cert: undefined,
key: undefined,
ca: undefined,
});
expect(globals.config.get).toHaveBeenCalledWith('Butler-SOS.cert.clientCertPassphrase');
expect(options.CertificatePassphrase).toBe('my-passphrase');
});
test('should handle missing SEA assets gracefully - healthmetrics', () => {
sea.getAsset.mockReturnValue(undefined);
test('should set passphrase to null when empty string configured', () => {
globals.config.get
.mockReturnValueOnce('cert/client.crt')
.mockReturnValueOnce('cert/client.key')
.mockReturnValueOnce('cert/ca.crt')
.mockReturnValueOnce('');
globals.config.has.mockReturnValue(true);
const certificates = getHealthCertificates(mockCertificateOptions);
const options = createCertificateOptions();
expect(sea.getAsset).toHaveBeenCalledWith('/path/to/client.crt', 'utf8');
expect(certificates).toEqual({
cert: undefined,
key: undefined,
ca: undefined,
});
});
test('should handle mixed success/failure scenarios - proxysessionmetrics', () => {
sea.getAsset
.mockReturnValueOnce(mockCertData) // cert succeeds
.mockReturnValueOnce(undefined) // key fails
.mockReturnValueOnce(mockCaData); // ca succeeds
const certificates = getProxyCertificates(mockCertificateOptions);
expect(certificates).toEqual({
cert: mockCertData,
key: undefined,
ca: mockCaData,
});
});
test('should handle mixed success/failure scenarios - healthmetrics', () => {
sea.getAsset
.mockReturnValueOnce(mockCertData) // cert succeeds
.mockReturnValueOnce(undefined) // key fails
.mockReturnValueOnce(mockCaData); // ca succeeds
const certificates = getHealthCertificates(mockCertificateOptions);
expect(certificates).toEqual({
cert: mockCertData,
key: undefined,
ca: mockCaData,
});
expect(options.CertificatePassphrase).toBeNull();
});
});
describe('Certificate format consistency', () => {
test('should handle various certificate formats in both modes', () => {
test('should handle various certificate formats', () => {
const formats = [
'-----BEGIN CERTIFICATE-----\nMIIBIjANBgkqhkiG9w0BAQEFA...\n-----END CERTIFICATE-----',
'-----BEGIN CERTIFICATE-----\nMIICIjANBgkqhkiG9w0BAQEFA...\n-----END CERTIFICATE-----\n',
@@ -254,31 +214,16 @@ describe('Certificate loading in SEA vs non-SEA modes', () => {
];
formats.forEach((certFormat, index) => {
// Test non-SEA mode
globals.isSea = false;
fs.readFileSync.mockClear();
fs.readFileSync.mockReturnValue(certFormat);
const nonSeaCerts = getProxyCertificates({
const certs = getCertificatesUtil({
Certificate: `/cert${index}.crt`,
CertificateKey: `/key${index}.key`,
CertificateCA: `/ca${index}.crt`,
});
expect(nonSeaCerts.cert).toBe(certFormat);
// Test SEA mode
globals.isSea = true;
sea.getAsset.mockClear();
sea.getAsset.mockReturnValue(certFormat);
const seaCerts = getProxyCertificates({
Certificate: `/cert${index}.crt`,
CertificateKey: `/key${index}.key`,
CertificateCA: `/ca${index}.crt`,
});
expect(seaCerts.cert).toBe(certFormat);
expect(certs.cert).toBe(certFormat);
});
});
});
@@ -308,91 +253,41 @@ describe('Certificate loading in SEA vs non-SEA modes', () => {
}, // Windows paths
];
testPaths.forEach((paths, index) => {
// Test non-SEA mode
globals.isSea = false;
testPaths.forEach((paths) => {
fs.readFileSync.mockClear();
fs.readFileSync.mockReturnValue('dummy-cert');
getProxyCertificates(paths);
getCertificatesUtil(paths);
expect(fs.readFileSync).toHaveBeenCalledWith(paths.Certificate);
expect(fs.readFileSync).toHaveBeenCalledWith(paths.CertificateKey);
expect(fs.readFileSync).toHaveBeenCalledWith(paths.CertificateCA);
// Test SEA mode
globals.isSea = true;
sea.getAsset.mockClear();
sea.getAsset.mockReturnValue('dummy-cert');
getHealthCertificates(paths);
expect(sea.getAsset).toHaveBeenCalledWith(paths.Certificate, 'utf8');
expect(sea.getAsset).toHaveBeenCalledWith(paths.CertificateKey, 'utf8');
expect(sea.getAsset).toHaveBeenCalledWith(paths.CertificateCA, 'utf8');
});
});
});
describe('Error handling consistency', () => {
test('should provide consistent error handling patterns across modes', () => {
// Non-SEA mode error
globals.isSea = false;
fs.readFileSync.mockImplementation(() => {
throw new Error('Permission denied');
});
expect(() => getProxyCertificates(mockCertificateOptions)).toThrow('Permission denied');
// SEA mode should not throw for missing assets, but return undefined
globals.isSea = true;
sea.getAsset.mockClear();
sea.getAsset.mockReturnValue(undefined);
const certificates = getHealthCertificates(mockCertificateOptions);
expect(certificates.cert).toBeUndefined();
expect(certificates.key).toBeUndefined();
expect(certificates.ca).toBeUndefined();
});
});
describe('Integration with TLS configuration', () => {
test('should produce certificates compatible with HTTPS agent configuration', () => {
const mockHttpsAgentOptions = {};
// Test non-SEA mode
globals.isSea = false;
fs.readFileSync.mockClear();
fs.readFileSync
.mockReturnValueOnce(mockCertData)
.mockReturnValueOnce(mockKeyData)
.mockReturnValueOnce(mockCaData);
const nonSeaCerts = getProxyCertificates(mockCertificateOptions);
const certificates = getCertificatesUtil(mockCertificateOptions);
// Verify that certificate structure is suitable for HTTPS agent
expect(typeof nonSeaCerts.cert).toBe('string');
expect(typeof nonSeaCerts.key).toBe('string');
expect(typeof nonSeaCerts.ca).toBe('string');
expect(typeof certificates.cert).toBe('string');
expect(typeof certificates.key).toBe('string');
expect(typeof certificates.ca).toBe('string');
// These should be assignable to HTTPS agent options
Object.assign(mockHttpsAgentOptions, nonSeaCerts);
Object.assign(mockHttpsAgentOptions, certificates);
expect(mockHttpsAgentOptions.cert).toBe(mockCertData);
expect(mockHttpsAgentOptions.key).toBe(mockKeyData);
expect(mockHttpsAgentOptions.ca).toBe(mockCaData);
// Test SEA mode
globals.isSea = true;
sea.getAsset.mockClear();
sea.getAsset
.mockReturnValueOnce(mockCertData)
.mockReturnValueOnce(mockKeyData)
.mockReturnValueOnce(mockCaData);
const seaCerts = getHealthCertificates(mockCertificateOptions);
// Should produce identical structure
expect(seaCerts).toEqual(nonSeaCerts);
});
});
});

91
src/lib/cert-utils.js Normal file
View File

@@ -0,0 +1,91 @@
/**
* Certificate utilities for loading TLS certificates from the filesystem
*/
import fs from 'fs';
import path from 'path';
import globals from '../globals.js';
/**
* Creates certificate options object from global configuration.
*
* This function reads certificate paths from the Butler-SOS configuration
* and creates an options object suitable for use with getCertificates().
*
* @property {string} Certificate - Absolute path to client certificate
* @property {string} CertificateKey - Absolute path to client certificate key
* @property {string} CertificateCA - Absolute path to certificate authority
* @property {string|null} CertificatePassphrase - Certificate passphrase or null
* @returns {object} Certificate options object with resolved paths
*/
export function createCertificateOptions() {
const options = {};
options.Certificate = path.resolve(
process.cwd(),
globals.config.get('Butler-SOS.cert.clientCert')
);
options.CertificateKey = path.resolve(
process.cwd(),
globals.config.get('Butler-SOS.cert.clientCertKey')
);
options.CertificateCA = path.resolve(
process.cwd(),
globals.config.get('Butler-SOS.cert.clientCertCA')
);
if (
globals.config.has('Butler-SOS.cert.clientCertPassphrase') === true &&
globals.config.get('Butler-SOS.cert.clientCertPassphrase')?.length > 0
) {
options.CertificatePassphrase = globals.config.get('Butler-SOS.cert.clientCertPassphrase');
} else {
options.CertificatePassphrase = null;
}
return options;
}
/**
* Loads TLS certificates from the filesystem.
*
* Certificates are always loaded from disk files, regardless of whether the
* application is running in SEA (Single Executable Application) mode or not.
* This ensures security and flexibility by keeping certificates as external files.
*
* @param {object} options - Certificate options
* @param {string} options.Certificate - Path to the client certificate file
* @param {string} options.CertificateKey - Path to the client certificate key file
* @param {string} options.CertificateCA - Path to the certificate authority file
* @param {string} [options.CertificatePassphrase] - Optional passphrase for the certificate
* @returns {object} Object containing cert, key, and ca properties with certificate contents
* @throws {Error} If any certificate file cannot be read
*/
export function getCertificates(options) {
const certificate = {};
globals.logger.debug(`Loading certificates from disk. SEA mode: ${globals.isSea}`);
globals.logger.debug(`Loading certificates from disk. cert=${options.Certificate}`);
globals.logger.debug(`Loading certificates from disk. key=${options.CertificateKey}`);
globals.logger.debug(`Loading certificates from disk. ca=${options.CertificateCA}`);
if (!options.Certificate || !options.CertificateKey || !options.CertificateCA) {
throw new Error(
'Certificate paths are not properly defined. Please check your configuration.'
);
}
try {
certificate.cert = fs.readFileSync(options.Certificate);
certificate.key = fs.readFileSync(options.CertificateKey);
certificate.ca = fs.readFileSync(options.CertificateCA);
} catch (error) {
throw new Error(
`Failed to load certificates from filesystem. ` +
`Error: ${error.message}. ` +
`Certificate paths: cert=${options.Certificate}, key=${options.CertificateKey}, ca=${options.CertificateCA}`
);
}
return certificate;
}

View File

@@ -4,9 +4,7 @@
import path from 'path';
import https from 'https';
import fs from 'fs';
import axios from 'axios';
import sea from './sea-wrapper.js';
import globals from '../globals.js';
import { postHealthMetricsToInfluxdb } from './post-to-influxdb.js';
@@ -15,34 +13,7 @@ import { postHealthToMQTT } from './post-to-mqtt.js';
import { getServerHeaders } from './serverheaders.js';
import { getServerTags } from './servertags.js';
import { saveHealthMetricsToPrometheus } from './prom-client.js';
/**
* Loads TLS certificates from the filesystem or SEA assets based on the provided options.
*
* @param {object} options - Certificate options
* @param {string} options.Certificate - Path to the client certificate file
* @param {string} options.CertificateKey - Path to the client certificate key file
* @param {string} options.CertificateCA - Path to the certificate authority file
* @param {string} [options.CertificatePassphrase] - Optional passphrase for the certificate
* @returns {object} Object containing cert, key, and ca properties with certificate contents
*/
export function getCertificates(options) {
const certificate = {};
if (globals.isSea) {
// In SEA mode, get certificates from embedded assets
certificate.cert = sea.getAsset(options.Certificate, 'utf8');
certificate.key = sea.getAsset(options.CertificateKey, 'utf8');
certificate.ca = sea.getAsset(options.CertificateCA, 'utf8');
} else {
// In non-SEA mode, read certificates from filesystem
certificate.cert = fs.readFileSync(options.Certificate);
certificate.key = fs.readFileSync(options.CertificateKey);
certificate.ca = fs.readFileSync(options.CertificateCA);
}
return certificate;
}
import { getCertificates, createCertificateOptions } from './cert-utils.js';
/**
* Retrieves health statistics from Qlik Sense server via the engine healthcheck API.
@@ -59,28 +30,8 @@ export function getCertificates(options) {
export function getHealthStatsFromSense(serverName, host, tags, headers) {
globals.logger.debug(`HEALTH: URL=https://${host}/engine/healthcheck`);
const options = {};
options.Certificate = path.resolve(
process.cwd(),
globals.config.get('Butler-SOS.cert.clientCert')
);
options.CertificateKey = path.resolve(
process.cwd(),
globals.config.get('Butler-SOS.cert.clientCertKey')
);
options.CertificateCA = path.resolve(
process.cwd(),
globals.config.get('Butler-SOS.cert.clientCertCA')
);
if (
globals.config.has('Butler-SOS.cert.clientCertPassphrase') === true &&
globals.config.get('Butler-SOS.cert.clientCertPassphrase')?.length > 0
) {
options.CertificatePassphrase = globals.config.get('Butler-SOS.cert.clientCertPassphrase');
} else {
options.CertificatePassphrase = null;
}
// Get certificate configuration options
const options = createCertificateOptions();
// Get certificates used to authenticate with Sense proxy service
const cert = getCertificates(options);

View File

@@ -3,11 +3,9 @@
*/
import https from 'https';
import fs from 'fs';
import path from 'path';
import axios from 'axios';
import { Point } from '@influxdata/influxdb-client';
import sea from './sea-wrapper.js';
import globals from '../globals.js';
import { postProxySessionsToInfluxdb } from './post-to-influxdb.js';
@@ -15,33 +13,7 @@ import { postProxySessionsToNewRelic } from './post-to-new-relic.js';
import { postUserSessionsToMQTT } from './post-to-mqtt.js';
import { getServerTags } from './servertags.js';
import { saveUserSessionMetricsToPrometheus } from './prom-client.js';
/**
* Loads TLS certificates from the filesystem or SEA assets based on the provided options.
*
* @param {object} options - Certificate options
* @param {string} options.Certificate - Path to the client certificate file
* @param {string} options.CertificateKey - Path to the client certificate key file
* @param {string} options.CertificateCA - Path to the certificate authority file
* @returns {object} Object containing cert, key, and ca properties with certificate contents
*/
export function getCertificates(options) {
const certificate = {};
if (globals.isSea) {
// In SEA mode, get certificates from embedded assets
certificate.cert = sea.getAsset(options.Certificate, 'utf8');
certificate.key = sea.getAsset(options.CertificateKey, 'utf8');
certificate.ca = sea.getAsset(options.CertificateCA, 'utf8');
} else {
// In non-SEA mode, read certificates from filesystem
certificate.cert = fs.readFileSync(options.Certificate);
certificate.key = fs.readFileSync(options.CertificateKey);
certificate.ca = fs.readFileSync(options.CertificateCA);
}
return certificate;
}
import { getCertificates, createCertificateOptions } from './cert-utils.js';
/**
* Prepares user session metrics data for storage/forwarding to various destinations.
@@ -243,29 +215,8 @@ export async function getProxySessionStatsFromSense(serverName, host, virtualPro
// Current user sessions are retrived using this API:
// https://help.qlik.com/en-US/sense-developer/February2021/Subsystems/ProxyServiceAPI/Content/Sense_ProxyServiceAPI/ProxyServiceAPI-Proxy-API.htm
const options = {};
options.Certificate = path.resolve(
process.cwd(),
globals.config.get('Butler-SOS.cert.clientCert')
);
options.CertificateKey = path.resolve(
process.cwd(),
globals.config.get('Butler-SOS.cert.clientCertKey')
);
options.CertificateCA = path.resolve(
process.cwd(),
globals.config.get('Butler-SOS.cert.clientCertCA')
);
if (
globals.config.has('Butler-SOS.cert.clientCertPassphrase') === true &&
globals.config.get('Butler-SOS.cert.clientCertPassphrase')?.length > 0
) {
options.CertificatePassphrase = globals.config.get('Butler-SOS.cert.clientCertPassphrase');
} else {
options.CertificatePassphrase = null;
}
// Get certificate configuration options
const options = createCertificateOptions();
// Get certificates used to authenticate with Sense proxy service
const cert = getCertificates(options);