webssh2-server
Version:
A Websocket to SSH2 gateway using xterm.js, socket.io, ssh2
137 lines (136 loc) • 6.15 kB
JavaScript
// app/envConfig.ts
// Pure functions for loading configuration from environment variables
import { ENV_VAR_MAPPING } from './config/env-mapper.js';
import { parseEnvValue } from './config/env-parser.js';
import { getAlgorithmPreset } from './config/algorithm-presets.js';
import { safePathToKeys, safeSetNested } from './utils/safe-property-access.js';
/**
* Load configuration from environment variables
* @pure
*/
export function loadEnvironmentConfig() {
const config = {};
// Special handling for PORT env variable
if (process.env['PORT'] !== undefined && process.env['WEBSSH2_LISTEN_PORT'] === undefined) {
const value = parseEnvValue(process.env['PORT'], 'number');
if (value !== null) {
const keys = safePathToKeys('listen.port');
safeSetNested(config, keys, value);
}
}
// Process all WEBSSH2_ environment variables
for (const [envVar, mapping] of Object.entries(ENV_VAR_MAPPING)) {
// Get value safely using Object.getOwnPropertyDescriptor
const descriptor = Object.getOwnPropertyDescriptor(process.env, envVar);
if (descriptor === undefined || descriptor.value === undefined) {
continue;
}
const envValue = String(descriptor.value);
// Special handling for algorithm presets
if (mapping.type === 'preset' && envVar === 'WEBSSH2_SSH_ALGORITHMS_PRESET') {
const preset = getAlgorithmPreset(envValue);
if (preset !== undefined) {
const keys = safePathToKeys(mapping.path);
safeSetNested(config, keys, preset);
}
continue;
}
// Parse the value based on type
const parsedValue = parseEnvValue(envValue, mapping.type);
// parseEnvValue never returns undefined, always store the result
const keys = safePathToKeys(mapping.path);
safeSetNested(config, keys, parsedValue);
}
return config;
}
/**
* Get the complete environment variable mapping with descriptions
* @pure
*/
export function getEnvironmentVariableMap() {
const map = {};
// Add PORT variable
map['PORT'] = {
path: 'listen.port',
type: 'number',
description: 'Port to listen on (overridden by WEBSSH2_LISTEN_PORT)'
};
// Add all WEBSSH2_ variables with descriptions
for (const [envVar, mapping] of Object.entries(ENV_VAR_MAPPING)) {
// Create a safe key to avoid object injection
const safeKey = String(envVar);
Object.defineProperty(map, safeKey, {
value: {
path: mapping.path,
type: mapping.type,
description: getEnvironmentVariableDescription(safeKey)
},
enumerable: true,
configurable: true,
writable: true
});
}
return map;
}
/**
* Get all available algorithm presets
* @pure
*/
export function getAlgorithmPresets() {
return {
modern: getAlgorithmPreset('modern'),
legacy: getAlgorithmPreset('legacy'),
strict: getAlgorithmPreset('strict')
};
}
/**
* Get description for environment variable
* @pure
*/
function getEnvironmentVariableDescription(envVar) {
const descriptions = {
WEBSSH2_LISTEN_IP: 'IP address to listen on',
WEBSSH2_LISTEN_PORT: 'Port number to listen on',
WEBSSH2_HTTP_ORIGINS: 'Allowed HTTP origins (comma-separated)',
WEBSSH2_USER_NAME: 'Default SSH username',
WEBSSH2_USER_PASSWORD: 'Default SSH password', //NOSONAR
WEBSSH2_USER_PRIVATE_KEY: 'SSH private key (base64 encoded)',
WEBSSH2_USER_PASSPHRASE: 'SSH private key passphrase', //NOSONAR
WEBSSH2_SSH_HOST: 'Default SSH host',
WEBSSH2_SSH_PORT: 'Default SSH port',
WEBSSH2_SSH_LOCAL_ADDRESS: 'Local address for SSH connection',
WEBSSH2_SSH_LOCAL_PORT: 'Local port for SSH connection',
WEBSSH2_SSH_TERM: 'Terminal type for SSH',
WEBSSH2_SSH_ENV_ALLOWLIST: 'Environment variables to pass through (comma-separated)',
WEBSSH2_SSH_READY_TIMEOUT: 'SSH ready timeout in milliseconds',
WEBSSH2_SSH_KEEPALIVE_INTERVAL: 'SSH keepalive interval in milliseconds',
WEBSSH2_SSH_KEEPALIVE_COUNT_MAX: 'Maximum SSH keepalive count',
WEBSSH2_SSH_ALLOWED_SUBNETS: 'Allowed subnets for SSH connections (comma-separated)',
WEBSSH2_SSH_ALWAYS_SEND_KEYBOARD_INTERACTIVE: 'Always send keyboard interactive prompts',
WEBSSH2_SSH_DISABLE_INTERACTIVE_AUTH: 'Disable interactive authentication',
WEBSSH2_SSH_ALGORITHMS_CIPHER: 'SSH cipher algorithms (comma-separated)',
WEBSSH2_SSH_ALGORITHMS_KEX: 'SSH key exchange algorithms (comma-separated)',
WEBSSH2_SSH_ALGORITHMS_HMAC: 'SSH HMAC algorithms (comma-separated)',
WEBSSH2_SSH_ALGORITHMS_COMPRESS: 'SSH compression algorithms (comma-separated)',
WEBSSH2_SSH_ALGORITHMS_SERVER_HOST_KEY: 'SSH server host key algorithms (comma-separated)',
WEBSSH2_SSH_ALGORITHMS_PRESET: 'SSH algorithm preset (modern, legacy, or strict)',
WEBSSH2_HEADER_TEXT: 'Header text to display',
WEBSSH2_HEADER_BACKGROUND: 'Header background color',
WEBSSH2_OPTIONS_CHALLENGE_BUTTON: 'Show challenge button',
WEBSSH2_OPTIONS_AUTO_LOG: 'Enable automatic logging',
WEBSSH2_OPTIONS_ALLOW_REAUTH: 'Allow reauthentication',
WEBSSH2_OPTIONS_ALLOW_RECONNECT: 'Allow reconnection',
WEBSSH2_OPTIONS_ALLOW_REPLAY: 'Allow replay',
WEBSSH2_SESSION_SECRET: 'Session secret for cookies',
WEBSSH2_SESSION_NAME: 'Session cookie name',
WEBSSH2_SSO_ENABLED: 'Enable SSO',
WEBSSH2_SSO_CSRF_PROTECTION: 'Enable CSRF protection for SSO',
WEBSSH2_SSO_TRUSTED_PROXIES: 'Trusted proxy addresses (comma-separated)'
};
// Get value safely using Object.getOwnPropertyDescriptor
const descriptor = Object.getOwnPropertyDescriptor(descriptions, envVar);
if (descriptor !== undefined && descriptor.value !== undefined) {
return String(descriptor.value);
}
return 'Configuration option';
}