Server: Support passphrase for SSL private key (#4488) * SSL: support passphrase for private key * Recommend CLI argument or environment variable for key passphrase * Fix SSL passphrase handling to ensure it is always a string

e871886b1358d385b2fee177e9db0e1f7c85d3d0

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
3 files changed, +16 -0Ignore whitespace
default/config.yaml+6 -0
@@ -40,9 +40,15 @@ browserLaunch:
40port: 800040port: 8000
41# -- SSL options --41# -- SSL options --
42ssl:42ssl:
43 # Enable SSL/TLS encryption
43 enabled: false44 enabled: false
45 # Path to certificate (relative to server root)
44 certPath: "./certs/cert.pem"46 certPath: "./certs/cert.pem"
47 # Path to private key (relative to server root)
45 keyPath: "./certs/privkey.pem"48 keyPath: "./certs/privkey.pem"
49 # Private key passphrase (leave empty if not needed)
50 # For better security, use a CLI argument or an environment variable (SILLYTAVERN_SSL_KEYPASSPHRASE)
51 keyPassphrase: ""
46# -- SECURITY CONFIGURATION --52# -- SECURITY CONFIGURATION --
47# Toggle whitelist mode53# Toggle whitelist mode
48whitelistMode: true54whitelistMode: true
src/command-line.js+8 -0
@@ -27,6 +27,7 @@ import { initConfig } from './config-init.js';
27 * @property {boolean} ssl If enable SSL27 * @property {boolean} ssl If enable SSL
28 * @property {string} certPath Path to certificate28 * @property {string} certPath Path to certificate
29 * @property {string} keyPath Path to private key29 * @property {string} keyPath Path to private key
30 * @property {string} keyPassphrase SSL private key passphrase
30 * @property {boolean} whitelistMode If enable whitelist mode31 * @property {boolean} whitelistMode If enable whitelist mode
31 * @property {boolean} basicAuthMode If enable basic authentication32 * @property {boolean} basicAuthMode If enable basic authentication
32 * @property {boolean} requestProxyEnabled If enable outgoing request proxy33 * @property {boolean} requestProxyEnabled If enable outgoing request proxy
@@ -70,6 +71,7 @@ export class CommandLineParser {
70 ssl: false,71 ssl: false,
71 certPath: 'certs/cert.pem',72 certPath: 'certs/cert.pem',
72 keyPath: 'certs/privkey.pem',73 keyPath: 'certs/privkey.pem',
74 keyPassphrase: '',
73 whitelistMode: true,75 whitelistMode: true,
74 basicAuthMode: false,76 basicAuthMode: false,
75 requestProxyEnabled: false,77 requestProxyEnabled: false,
@@ -193,6 +195,11 @@ export class CommandLineParser {
193 default: null,195 default: null,
194 describe: 'Path to SSL private key file',196 describe: 'Path to SSL private key file',
195 })197 })
198 .option('keyPassphrase', {
199 type: 'string',
200 default: null,
201 describe: 'Passphrase for the SSL private key',
202 })
196 .option('whitelist', {203 .option('whitelist', {
197 type: 'boolean',204 type: 'boolean',
198 default: null,205 default: null,
@@ -291,6 +298,7 @@ export class CommandLineParser {
291 ssl: cliArguments.ssl ?? getConfigValue('ssl.enabled', defaultConfig.ssl, 'boolean'),298 ssl: cliArguments.ssl ?? getConfigValue('ssl.enabled', defaultConfig.ssl, 'boolean'),
292 certPath: cliArguments.certPath ?? getConfigValue('ssl.certPath', defaultConfig.certPath),299 certPath: cliArguments.certPath ?? getConfigValue('ssl.certPath', defaultConfig.certPath),
293 keyPath: cliArguments.keyPath ?? getConfigValue('ssl.keyPath', defaultConfig.keyPath),300 keyPath: cliArguments.keyPath ?? getConfigValue('ssl.keyPath', defaultConfig.keyPath),
301 keyPassphrase: cliArguments.keyPassphrase ?? getConfigValue('ssl.keyPassphrase', defaultConfig.keyPassphrase),
294 whitelistMode: cliArguments.whitelist ?? getConfigValue('whitelistMode', defaultConfig.whitelistMode, 'boolean'),302 whitelistMode: cliArguments.whitelist ?? getConfigValue('whitelistMode', defaultConfig.whitelistMode, 'boolean'),
295 basicAuthMode: cliArguments.basicAuthMode ?? getConfigValue('basicAuthMode', defaultConfig.basicAuthMode, 'boolean'),303 basicAuthMode: cliArguments.basicAuthMode ?? getConfigValue('basicAuthMode', defaultConfig.basicAuthMode, 'boolean'),
296 requestProxyEnabled: cliArguments.requestProxyEnabled ?? getConfigValue('requestProxy.enabled', defaultConfig.requestProxyEnabled, 'boolean'),304 requestProxyEnabled: cliArguments.requestProxyEnabled ?? getConfigValue('requestProxy.enabled', defaultConfig.requestProxyEnabled, 'boolean'),
src/server-startup.js+2 -0
@@ -233,9 +233,11 @@ export class ServerStartup {
233 #createHttpsServer(url, ipVersion) {233 #createHttpsServer(url, ipVersion) {
234 this.#verifySslOptions();234 this.#verifySslOptions();
235 return new Promise((resolve, reject) => {235 return new Promise((resolve, reject) => {
236 /** @type {import('https').ServerOptions} */
236 const sslOptions = {237 const sslOptions = {
237 cert: fs.readFileSync(this.cliArgs.certPath),238 cert: fs.readFileSync(this.cliArgs.certPath),
238 key: fs.readFileSync(this.cliArgs.keyPath),239 key: fs.readFileSync(this.cliArgs.keyPath),
240 passphrase: String(this.cliArgs.keyPassphrase ?? ''),
239 };241 };
240 const server = https.createServer(sslOptions, this.app);242 const server = https.createServer(sslOptions, this.app);
241 server.on('error', reject);243 server.on('error', reject);