diff --git a/config.schema.json b/config.schema.json index 771e83d0c..4e9622ca0 100644 --- a/config.schema.json +++ b/config.schema.json @@ -78,6 +78,16 @@ "type": "object" } } + }, + "tls": { + "description": "TLS configuration for secure connections", + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "key": { "type": "string" }, + "cert": { "type": "string" } + }, + "required": ["enabled", "key", "cert"] } }, "definitions": { diff --git a/proxy.config.json b/proxy.config.json index 14d016e4d..fdb32a0d0 100644 --- a/proxy.config.json +++ b/proxy.config.json @@ -97,5 +97,10 @@ "urlShortener": "", "contactEmail": "", "csrfProtection": true, - "plugins": [] + "plugins": [], + "tls": { + "enabled": true, + "key": "certs/key.pem", + "cert": "certs/cert.pem" + } } diff --git a/src/config/index.ts b/src/config/index.ts index a1779ed9d..782c75564 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -2,8 +2,13 @@ import { existsSync, readFileSync } from 'fs'; import defaultSettings from '../../proxy.config.json'; import { configFile } from './file'; -import { Authentication, AuthorisedRepo, Database, TempPasswordConfig, UserSettings } from './types'; - +import { + Authentication, + AuthorisedRepo, + Database, + TempPasswordConfig, + UserSettings, +} from './types'; let _userSettings: UserSettings | null = null; if (existsSync(configFile)) { @@ -26,8 +31,9 @@ let _contactEmail: string = defaultSettings.contactEmail; let _csrfProtection: boolean = defaultSettings.csrfProtection; let _domains: Record = defaultSettings.domains; // These are not always present in the default config file, so casting is required -let _sslKeyPath: string = (defaultSettings as any).sslKeyPemPath; -let _sslCertPath: string = (defaultSettings as any).sslCertPemPath; +let _tlsEnabled = defaultSettings.tls.enabled; +let _tlsKeyPemPath = defaultSettings.tls.key; +let _tlsCertPemPath = defaultSettings.tls.cert; // Get configured proxy URL export const getProxyUrl = () => { @@ -170,26 +176,39 @@ export const getPlugins = () => { _plugins = _userSettings.plugins; } return _plugins; -} +}; -export const getSSLKeyPath = () => { +export const getTLSKeyPemPath = () => { if (_userSettings && _userSettings.sslKeyPemPath) { - _sslKeyPath = _userSettings.sslKeyPemPath; + console.log( + 'Warning: sslKeyPemPath setting is replaced with tls.key setting in proxy.config.json & will be deprecated in a future release', + ); + _tlsKeyPemPath = _userSettings.sslKeyPemPath; } - if (!_sslKeyPath) { - return '../../certs/key.pem'; + if (_userSettings?.tls && _userSettings?.tls?.key) { + _tlsKeyPemPath = _userSettings.tls.key; } - return _sslKeyPath; + return _tlsKeyPemPath; }; -export const getSSLCertPath = () => { +export const getTLSCertPemPath = () => { if (_userSettings && _userSettings.sslCertPemPath) { - _sslCertPath = _userSettings.sslCertPemPath; + console.log( + 'Warning: sslCertPemPath setting is replaced with tls.cert setting in proxy.config.json & will be deprecated in a future release', + ); + _tlsCertPemPath = _userSettings.sslCertPemPath; } - if (!_sslCertPath) { - return '../../certs/cert.pem'; + if (_userSettings?.tls && _userSettings?.tls?.cert) { + _tlsCertPemPath = _userSettings.tls.cert; + } + return _tlsCertPemPath; +}; + +export const getTLSEnabled = () => { + if (_userSettings && _userSettings.tls && _userSettings.tls.enabled) { + _tlsEnabled = _userSettings.tls.enabled; } - return _sslCertPath; + return _tlsEnabled; }; export const getDomains = () => { diff --git a/src/config/types.ts b/src/config/types.ts index 7364f6201..30428f232 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -7,8 +7,9 @@ export interface UserSettings { api: Record; cookieSecret: string; sessionMaxAgeHours: number; - sslKeyPemPath?: string; // Optional (not in config.schema.json) - sslCertPemPath?: string; // Optional (not in config.schema.json) + tls?: TLSConfig; + sslCertPemPath?: string; // deprecated + sslKeyPemPath?: string; // deprecated plugins: any[]; commitConfig: Record; attestationConfig: Record; @@ -19,6 +20,12 @@ export interface UserSettings { domains: Record; } +export interface TLSConfig { + enabled?: boolean; + cert?: string; + key?: string; +} + export interface AuthorisedRepo { project: string; name: string; diff --git a/src/proxy/index.ts b/src/proxy/index.ts index 89a0977b3..04fce52d7 100644 --- a/src/proxy/index.ts +++ b/src/proxy/index.ts @@ -3,20 +3,15 @@ import bodyParser from 'body-parser'; import http from 'http'; import https from 'https'; import fs from 'fs'; -import path from 'path'; import { router } from './routes'; import { getAuthorisedList, getPlugins, - getSSLCertPath, - getSSLKeyPath + getTLSKeyPemPath, + getTLSCertPemPath, + getTLSEnabled, } from '../config'; -import { - addUserCanAuthorise, - addUserCanPush, - createRepo, - getRepos -} from '../db'; +import { addUserCanAuthorise, addUserCanPush, createRepo, getRepos } from '../db'; import { PluginLoader } from '../plugin'; import chain from './chain'; import { Repo } from '../db/types'; @@ -28,8 +23,8 @@ const options = { inflate: true, limit: '100000kb', type: '*/*', - key: fs.readFileSync(path.join(__dirname, getSSLKeyPath())), - cert: fs.readFileSync(path.join(__dirname, getSSLCertPath())), + key: getTLSEnabled() ? fs.readFileSync(getTLSKeyPemPath()) : undefined, + cert: getTLSEnabled() ? fs.readFileSync(getTLSCertPemPath()) : undefined, }; const proxyPreparations = async () => { @@ -66,15 +61,17 @@ const start = async () => { http.createServer(options as any, app).listen(proxyHttpPort, () => { console.log(`HTTP Proxy Listening on ${proxyHttpPort}`); }); - https.createServer(options, app).listen(proxyHttpsPort, () => { - console.log(`HTTPS Proxy Listening on ${proxyHttpsPort}`); - }); - + // Start HTTPS server only if TLS is enabled + if (getTLSEnabled()) { + https.createServer(options, app).listen(proxyHttpsPort, () => { + console.log(`HTTPS Proxy Listening on ${proxyHttpsPort}`); + }); + } return app; }; export default { proxyPreparations, createApp, - start + start, }; diff --git a/test/testConfig.test.js b/test/testConfig.test.js index 125ee7b44..9f5f45419 100644 --- a/test/testConfig.test.js +++ b/test/testConfig.test.js @@ -16,8 +16,8 @@ describe('default configuration', function () { expect(config.getDatabase()).to.be.eql(defaultSettings.sink[0]); expect(config.getTempPasswordConfig()).to.be.eql(defaultSettings.tempPassword); expect(config.getAuthorisedList()).to.be.eql(defaultSettings.authorisedList); - expect(config.getSSLKeyPath()).to.be.eql("../../certs/key.pem"); - expect(config.getSSLCertPath()).to.be.eql("../../certs/cert.pem"); + expect(config.getTLSKeyPemPath()).to.be.eql(defaultSettings.tls.key); + expect(config.getTLSCertPemPath()).to.be.eql(defaultSettings.tls.cert); }); after(function () { delete require.cache[require.resolve('../src/config')]; @@ -94,15 +94,17 @@ describe('user configuration', function () { it('should override default settings for SSL certificate', function () { const user = { - sslKeyPemPath: "my-key.pem", - sslCertPemPath: "my-cert.pem" + tls: { + key: 'my-key.pem', + cert: 'my-cert.pem', + }, }; fs.writeFileSync(tempUserFile, JSON.stringify(user)); const config = require('../src/config'); - expect(config.getSSLKeyPath()).to.be.eql(user.sslKeyPemPath); - expect(config.getSSLCertPath()).to.be.eql(user.sslCertPemPath); + expect(config.getTLSKeyPemPath()).to.be.eql(user.tls.key); + expect(config.getTLSCertPemPath()).to.be.eql(user.tls.cert); }); afterEach(function () { @@ -116,21 +118,14 @@ describe('validate config files', function () { const config = require('../src/config/file'); it('all valid config files should pass validation', function () { - const validConfigFiles = [ - 'proxy.config.valid-1.json', - 'proxy.config.valid-2.json', - ]; + const validConfigFiles = ['proxy.config.valid-1.json', 'proxy.config.valid-2.json']; for (const testConfigFile of validConfigFiles) { - expect(config.validate(path.join(__dirname, fixtures, testConfigFile))).to - .be.true; + expect(config.validate(path.join(__dirname, fixtures, testConfigFile))).to.be.true; } }); it('all invalid config files should fail validation', function () { - const invalidConfigFiles = [ - 'proxy.config.invalid-1.json', - 'proxy.config.invalid-2.json', - ]; + const invalidConfigFiles = ['proxy.config.invalid-1.json', 'proxy.config.invalid-2.json']; for (const testConfigFile of invalidConfigFiles) { const test = function () { config.validate(path.join(__dirname, fixtures, testConfigFile));