webssh2-server
Version:
A Websocket to SSH2 gateway using xterm.js, socket.io, ssh2
240 lines (239 loc) • 6.85 kB
JavaScript
// app/utils/config-builder.ts
// Configuration builder utility for creating and validating configs
import { ok, err } from '../utils/result.js';
import { validateConfigPure } from './config-validator.js';
import { DEFAULT_SSO_HEADERS } from '../constants/security.js';
import { validateSshHost, validateSshPort, validateCssColor } from '../validation/config.js';
/**
* Transform config to enhanced config with full validation
*/
export function enhanceConfig(config) {
// Use the existing validation pipeline
const validationResult = validateConfigPure(config);
if (validationResult.ok) {
// Validation passed, continue with additional checks
}
else {
// Convert validation error to our error format
return err([{
path: '',
message: validationResult.error.message,
value: config
}]);
}
// Additional branded type validations
const errors = [];
if (config.ssh.host != null) {
try {
validateSshHost(config.ssh.host);
}
catch (e) {
errors.push({
path: 'ssh.host',
message: e.message,
value: config.ssh.host,
});
}
}
try {
validateSshPort(config.ssh.port);
}
catch (e) {
errors.push({
path: 'ssh.port',
message: e.message,
value: config.ssh.port,
});
}
if (config.header.background !== '') {
try {
validateCssColor(config.header.background);
}
catch (e) {
errors.push({
path: 'header.background',
message: e.message,
value: config.header.background,
});
}
}
if (errors.length > 0) {
return err(errors);
}
return ok(config);
}
/**
* Configuration builder for creating validated configs
*/
export class ConfigBuilder {
config = {};
withSshHost(host) {
this.config.ssh = {
...this.config.ssh,
host,
};
return this;
}
withSshPort(port) {
this.config.ssh = {
...this.config.ssh,
port,
};
return this;
}
withSshLocalAddress(localAddress) {
this.config.ssh = {
...this.config.ssh,
localAddress,
};
return this;
}
withSshLocalPort(localPort) {
this.config.ssh = {
...this.config.ssh,
localPort,
};
return this;
}
withSshAlgorithms(algorithms) {
const currentAlgorithms = this.config.ssh?.algorithms;
this.config.ssh = {
...this.config.ssh,
algorithms: {
cipher: algorithms.cipher ?? currentAlgorithms?.cipher ?? [],
compress: algorithms.compress ?? currentAlgorithms?.compress ?? [],
hmac: algorithms.hmac ?? currentAlgorithms?.hmac ?? [],
kex: algorithms.kex ?? currentAlgorithms?.kex ?? [],
serverHostKey: algorithms.serverHostKey ?? currentAlgorithms?.serverHostKey ?? [],
},
};
return this;
}
withSshAllowedSubnets(subnets) {
this.config.ssh = {
...this.config.ssh,
allowedSubnets: subnets,
};
return this;
}
withHeader(text, background) {
this.config.header = { text, background };
return this;
}
withOptions(options) {
const defaultOptions = {
challengeButton: false,
autoLog: false,
allowReauth: false,
allowReconnect: false,
allowReplay: false,
replayCRLF: false,
};
this.config.options = {
...defaultOptions,
...this.config.options,
...options,
};
return this;
}
withSessionSecret(secret) {
this.config.session = {
name: this.config.session?.name ?? 'webssh2',
...this.config.session,
secret,
};
return this;
}
withSessionName(name) {
this.config.session = {
secret: this.config.session?.secret ?? '',
...this.config.session,
name,
};
return this;
}
withSsoConfig(sso) {
this.config.sso = {
...this.config.sso,
...sso,
};
return this;
}
withHttpOrigins(origins) {
this.config.http = { origins };
return this;
}
withListenConfig(ip, port) {
this.config.listen = { ip, port };
return this;
}
withUserCredentials(user) {
this.config.user = {
...this.config.user,
...user,
};
return this;
}
validate() {
// Set defaults for required fields
const fullConfig = {
listen: this.config.listen ?? { ip: '0.0.0.0', port: 2222 },
http: this.config.http ?? { origins: [] },
user: this.config.user ?? {
name: null,
password: null,
privateKey: null,
passphrase: null,
},
ssh: {
host: null,
port: 22,
term: 'xterm-256color',
readyTimeout: 20000,
keepaliveInterval: 60000,
keepaliveCountMax: 10,
allowedSubnets: [],
alwaysSendKeyboardInteractivePrompts: false,
disableInteractiveAuth: false,
algorithms: {
cipher: [],
compress: [],
hmac: [],
kex: [],
serverHostKey: [],
},
...this.config.ssh,
},
header: this.config.header ?? {
text: null,
background: 'green',
},
options: this.config.options ?? {
challengeButton: false,
autoLog: false,
allowReauth: false,
allowReconnect: false,
allowReplay: false,
replayCRLF: false,
},
session: this.config.session ?? {
secret: '',
name: 'webssh2',
},
sso: this.config.sso ?? {
enabled: false,
csrfProtection: false,
trustedProxies: [],
headerMapping: DEFAULT_SSO_HEADERS,
},
};
return enhanceConfig(fullConfig);
}
build() {
const result = this.validate();
if (result.ok) {
return result.value;
}
return null;
}
}