173 lines
5.6 KiB
JavaScript
173 lines
5.6 KiB
JavaScript
// server
|
|
// app/config.js
|
|
|
|
import path from 'path'
|
|
import fs from 'fs'
|
|
import readConfig from 'read-config-ng'
|
|
import { deepMerge, validateConfig } from './utils.js'
|
|
import { generateSecureSecret } from './crypto-utils.js'
|
|
import { createNamespacedDebug } from './logger.js'
|
|
import { ConfigError, handleError } from './errors.js'
|
|
import { DEFAULTS } from './constants.js'
|
|
|
|
const debug = createNamespacedDebug('config')
|
|
|
|
const defaultConfig = {
|
|
listen: {
|
|
ip: '0.0.0.0',
|
|
port: DEFAULTS.LISTEN_PORT,
|
|
},
|
|
http: {
|
|
origins: ['*:*'],
|
|
},
|
|
user: {
|
|
name: null,
|
|
password: null,
|
|
privateKey: null,
|
|
passphrase: null,
|
|
},
|
|
ssh: {
|
|
host: null,
|
|
port: DEFAULTS.SSH_PORT,
|
|
term: DEFAULTS.SSH_TERM,
|
|
readyTimeout: 20000,
|
|
keepaliveInterval: 120000,
|
|
keepaliveCountMax: 10,
|
|
alwaysSendKeyboardInteractivePrompts: false,
|
|
disableInteractiveAuth: false,
|
|
algorithms: {
|
|
cipher: [
|
|
'aes128-ctr',
|
|
'aes192-ctr',
|
|
'aes256-ctr',
|
|
'aes128-gcm',
|
|
'aes128-gcm@openssh.com',
|
|
'aes256-gcm',
|
|
'aes256-gcm@openssh.com',
|
|
'aes256-cbc',
|
|
],
|
|
compress: ['none', 'zlib@openssh.com', 'zlib'],
|
|
hmac: ['hmac-sha2-256', 'hmac-sha2-512', 'hmac-sha1'],
|
|
kex: [
|
|
'ecdh-sha2-nistp256',
|
|
'ecdh-sha2-nistp384',
|
|
'ecdh-sha2-nistp521',
|
|
'diffie-hellman-group-exchange-sha256',
|
|
'diffie-hellman-group14-sha1',
|
|
],
|
|
serverHostKey: [
|
|
'ecdsa-sha2-nistp256',
|
|
'ecdsa-sha2-nistp384',
|
|
'ecdsa-sha2-nistp521',
|
|
'ssh-rsa',
|
|
],
|
|
},
|
|
},
|
|
header: {
|
|
text: null,
|
|
background: 'green',
|
|
},
|
|
options: {
|
|
challengeButton: true,
|
|
autoLog: false,
|
|
allowReauth: true,
|
|
allowReconnect: true,
|
|
allowReplay: true,
|
|
},
|
|
session: {
|
|
secret: process.env.WEBSSH_SESSION_SECRET || generateSecureSecret(),
|
|
name: 'webssh2.sid',
|
|
},
|
|
}
|
|
|
|
import { fileURLToPath } from 'url'
|
|
import { dirname } from 'path'
|
|
|
|
const __filename = fileURLToPath(import.meta.url)
|
|
const __dirname = dirname(__filename)
|
|
|
|
function getConfigPath() {
|
|
return path.join(__dirname, '..', 'config.json')
|
|
}
|
|
|
|
function loadConfig() {
|
|
const configPath = getConfigPath()
|
|
|
|
try {
|
|
if (fs.existsSync(configPath)) {
|
|
const providedConfig = readConfig.sync(configPath)
|
|
const mergedConfig = deepMerge(JSON.parse(JSON.stringify(defaultConfig)), providedConfig)
|
|
|
|
if (process.env.PORT) {
|
|
mergedConfig.listen.port = parseInt(process.env.PORT, 10)
|
|
debug('Using PORT from environment: %s', mergedConfig.listen.port)
|
|
}
|
|
|
|
const validatedConfig = validateConfig(mergedConfig)
|
|
debug('Merged and validated configuration')
|
|
return validatedConfig
|
|
}
|
|
debug('Missing config.json for webssh. Using default config')
|
|
return defaultConfig
|
|
} catch (err) {
|
|
const error = new ConfigError(`Problem loading config.json for webssh: ${err.message}`)
|
|
handleError(error)
|
|
return defaultConfig
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads and validates the WebSSH2 configuration.
|
|
* Merges the default configuration with user-provided config.json if it exists.
|
|
* Falls back to default configuration if config.json is missing or invalid.
|
|
* Overrides listen.port with PORT environment variable if provided.
|
|
* @returns {Object} Configuration object with the following structure:
|
|
* @returns {Object} .listen - Server listening settings
|
|
* @returns {string} .listen.ip - IP address to listen on (default: "0.0.0.0")
|
|
* @returns {number} .listen.port - Port number to listen on
|
|
* @returns {Object} .http - HTTP server settings
|
|
* @returns {string[]} .http.origins - Allowed CORS origins (default: ["*:*"])
|
|
* @returns {Object} .user - Default user credentials
|
|
* @returns {string|null} .user.name - Default username
|
|
* @returns {string|null} .user.password - Default password
|
|
* @returns {Object} .ssh - SSH connection settings
|
|
* @returns {string|null} .ssh.host - SSH server hostname
|
|
* @returns {number} .ssh.port - SSH server port
|
|
* @returns {string} .ssh.term - Terminal type
|
|
* @returns {number} .ssh.readyTimeout - Connection timeout in ms
|
|
* @returns {number} .ssh.keepaliveInterval - Keepalive interval in ms
|
|
* @returns {number} .ssh.keepaliveCountMax - Max keepalive count
|
|
* @returns {boolean} .ssh.alwaysSendKeyboardInteractivePrompts - Force keyboard-interactive
|
|
* @returns {Object} .ssh.algorithms - Supported SSH algorithms
|
|
* @returns {string[]} .ssh.algorithms.cipher - Supported ciphers
|
|
* @returns {string[]} .ssh.algorithms.compress - Supported compression
|
|
* @returns {string[]} .ssh.algorithms.hmac - Supported HMAC algorithms
|
|
* @returns {string[]} .ssh.algorithms.kex - Supported key exchange
|
|
* @returns {string[]} .ssh.algorithms.serverHostKey - Supported host key types
|
|
* @returns {Object} .header - UI header settings
|
|
* @returns {string|null} .header.text - Header text
|
|
* @returns {string} .header.background - Header background color
|
|
* @returns {Object} .options - Feature flags and options
|
|
* @returns {boolean} .options.challengeButton - Show challenge button
|
|
* @returns {boolean} .options.autoLog - Enable automatic logging
|
|
* @returns {boolean} .options.allowReauth - Allow reauthentication
|
|
* @returns {boolean} .options.allowReconnect - Allow reconnection
|
|
* @returns {boolean} .options.allowReplay - Allow session replay
|
|
* @returns {Object} .session - Session configuration
|
|
* @returns {string} .session.secret - Session secret key
|
|
* @returns {string} .session.name - Session cookie name
|
|
*/
|
|
const config = loadConfig()
|
|
|
|
function getCorsConfig() {
|
|
return {
|
|
origin: config.http.origins,
|
|
methods: ['GET', 'POST'],
|
|
credentials: true,
|
|
}
|
|
}
|
|
|
|
// Add getCorsConfig to the config object
|
|
config.getCorsConfig = getCorsConfig
|
|
|
|
export default config
|