246 lines
8.2 KiB
TypeScript
246 lines
8.2 KiB
TypeScript
import { useTranslation } from 'react-i18next';
|
|
import { HelpText } from './HelpText';
|
|
import { FieldLabel, FieldInput } from './formUtils';
|
|
import type { SectionFormProps } from './types';
|
|
|
|
/**
|
|
* SSH global config editor (`ssh.*` + nested `ssh.console.*`).
|
|
*
|
|
* Edits the `ssh.*` block of config.yaml via the same draft / save-bar flow as
|
|
* other ConfigFormInner sections. Admin sub-tools (global connections /
|
|
* grants / rotation / audit) hit their own per-row endpoints and live in
|
|
* sibling subtabs of `SshForm`.
|
|
*/
|
|
export function SshConfigForm({ config, onChange, overriddenByEnv: _overriddenByEnv }: SectionFormProps) {
|
|
const { t } = useTranslation('settings');
|
|
const ssh = config.ssh ?? {};
|
|
const console_ = ssh.console ?? {};
|
|
|
|
return (
|
|
<div className="space-y-5">
|
|
<div>
|
|
<label className="flex items-center gap-2 text-sm text-slate-700">
|
|
<input
|
|
type="checkbox"
|
|
checked={ssh.enabled === true}
|
|
onChange={e => onChange('ssh.enabled', e.target.checked)}
|
|
className="rounded"
|
|
/>
|
|
{t('ssh.config.enableLabel')}
|
|
</label>
|
|
<HelpText>{t('ssh.config.enableHelp')}</HelpText>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="flex items-center gap-2 text-sm text-slate-700">
|
|
<input
|
|
type="checkbox"
|
|
checked={ssh.allowPrivateAddresses === true}
|
|
onChange={e => onChange('ssh.allowPrivateAddresses', e.target.checked)}
|
|
className="rounded"
|
|
/>
|
|
{t('ssh.config.allowPrivateLabel')}
|
|
</label>
|
|
<HelpText>{t('ssh.config.allowPrivateHelp')}</HelpText>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="flex items-center gap-2 text-sm text-slate-700">
|
|
<input
|
|
type="checkbox"
|
|
checked={ssh.adminBypassesGrants === true}
|
|
onChange={e => onChange('ssh.adminBypassesGrants', e.target.checked)}
|
|
className="rounded"
|
|
/>
|
|
{t('ssh.config.adminBypassLabel')}
|
|
</label>
|
|
<HelpText>{t('ssh.config.adminBypassHelp')}</HelpText>
|
|
</div>
|
|
|
|
<h3 className="text-sm font-medium text-slate-600 mt-6 pt-3 border-t border-slate-200">
|
|
Limits / Timeouts
|
|
</h3>
|
|
|
|
<div>
|
|
<FieldLabel>{t('ssh.config.callTimeout')}</FieldLabel>
|
|
<FieldInput
|
|
type="number"
|
|
value={ssh.callTimeoutSeconds ?? 30}
|
|
onChange={v => onChange('ssh.callTimeoutSeconds', Number(v))}
|
|
/>
|
|
<HelpText>{t('ssh.config.callTimeoutHelp')}</HelpText>
|
|
</div>
|
|
|
|
<div>
|
|
<FieldLabel>Max output bytes</FieldLabel>
|
|
<FieldInput
|
|
type="number"
|
|
value={ssh.maxOutputBytes ?? 32768}
|
|
onChange={v => onChange('ssh.maxOutputBytes', Number(v))}
|
|
/>
|
|
<HelpText>{t('ssh.config.maxOutputHelp')}</HelpText>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<div>
|
|
<FieldLabel>Max upload size (MB)</FieldLabel>
|
|
<FieldInput
|
|
type="number"
|
|
value={ssh.maxUploadSizeMb ?? 100}
|
|
onChange={v => onChange('ssh.maxUploadSizeMb', Number(v))}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<FieldLabel>Max download size (MB)</FieldLabel>
|
|
<FieldInput
|
|
type="number"
|
|
value={ssh.maxDownloadSizeMb ?? 100}
|
|
onChange={v => onChange('ssh.maxDownloadSizeMb', Number(v))}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<HelpText>{t('ssh.config.transferSizeHelp')}</HelpText>
|
|
|
|
<div>
|
|
<FieldLabel>{t('ssh.config.auditRetention')}</FieldLabel>
|
|
<FieldInput
|
|
type="number"
|
|
value={ssh.auditRetentionDays ?? 90}
|
|
onChange={v => onChange('ssh.auditRetentionDays', Number(v))}
|
|
/>
|
|
<HelpText>{t('ssh.config.auditRetentionHelp')}</HelpText>
|
|
</div>
|
|
|
|
<h3 className="text-sm font-medium text-slate-600 mt-6 pt-3 border-t border-slate-200">
|
|
Abuse detection
|
|
</h3>
|
|
<HelpText>{t('ssh.config.abuseHelp')}</HelpText>
|
|
|
|
<div className="grid grid-cols-3 gap-3">
|
|
<div>
|
|
<FieldLabel>{t('ssh.config.abuseWindow')}</FieldLabel>
|
|
<FieldInput
|
|
type="number"
|
|
value={ssh.abuseWindowMinutes ?? 10}
|
|
onChange={v => onChange('ssh.abuseWindowMinutes', Number(v))}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<FieldLabel>Failure threshold</FieldLabel>
|
|
<FieldInput
|
|
type="number"
|
|
value={ssh.abuseFailureThreshold ?? 5}
|
|
onChange={v => onChange('ssh.abuseFailureThreshold', Number(v))}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<FieldLabel>{t('ssh.config.abuseLock')}</FieldLabel>
|
|
<FieldInput
|
|
type="number"
|
|
value={ssh.abuseLockMinutes ?? 30}
|
|
onChange={v => onChange('ssh.abuseLockMinutes', Number(v))}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<h3 className="text-sm font-medium text-slate-600 mt-6 pt-3 border-t border-slate-200">
|
|
{t('ssh.config.consoleSectionTitle')}
|
|
</h3>
|
|
|
|
<div>
|
|
<label className="flex items-center gap-2 text-sm text-slate-700">
|
|
<input
|
|
type="checkbox"
|
|
checked={console_.enabled === true}
|
|
onChange={e => onChange('ssh.console.enabled', e.target.checked)}
|
|
className="rounded"
|
|
/>
|
|
{t('ssh.config.consoleEnableLabel')}
|
|
</label>
|
|
<HelpText>{t('ssh.config.consoleEnableHelp')}</HelpText>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<div>
|
|
<FieldLabel>{t('ssh.config.idleTimeout')}</FieldLabel>
|
|
<FieldInput
|
|
type="number"
|
|
value={console_.idleTimeoutSeconds ?? 1800}
|
|
onChange={v => onChange('ssh.console.idleTimeoutSeconds', Number(v))}
|
|
/>
|
|
<HelpText>{t('ssh.config.idleTimeoutHelp')}</HelpText>
|
|
</div>
|
|
<div>
|
|
<FieldLabel>{t('ssh.config.maxSessionDuration')}</FieldLabel>
|
|
<FieldInput
|
|
type="number"
|
|
value={console_.maxSessionDurationSeconds ?? 14400}
|
|
onChange={v => onChange('ssh.console.maxSessionDurationSeconds', Number(v))}
|
|
/>
|
|
<HelpText>{t('ssh.config.maxDurationHelp')}</HelpText>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<FieldLabel>Scrollback bytes</FieldLabel>
|
|
<FieldInput
|
|
type="number"
|
|
value={console_.scrollbackBytes ?? 524288}
|
|
onChange={v => onChange('ssh.console.scrollbackBytes', Number(v))}
|
|
/>
|
|
<HelpText>{t('ssh.config.scrollbackHelp')}</HelpText>
|
|
</div>
|
|
|
|
<div>
|
|
<FieldLabel>Max sessions per connection</FieldLabel>
|
|
<FieldInput
|
|
type="number"
|
|
value={console_.maxSessionsPerConnection ?? 3}
|
|
onChange={v => onChange('ssh.console.maxSessionsPerConnection', Number(v))}
|
|
/>
|
|
<HelpText>{t('ssh.config.maxSessionsHelp')}</HelpText>
|
|
</div>
|
|
|
|
<div>
|
|
<FieldLabel>Max input bytes per Send</FieldLabel>
|
|
<FieldInput
|
|
type="number"
|
|
value={console_.maxInputBytesPerSend ?? 16384}
|
|
onChange={v => onChange('ssh.console.maxInputBytesPerSend', Number(v))}
|
|
/>
|
|
<HelpText>{t('ssh.config.maxInputHelp')}</HelpText>
|
|
</div>
|
|
|
|
<div>
|
|
<FieldLabel>Auto-inject screen lines</FieldLabel>
|
|
<FieldInput
|
|
type="number"
|
|
value={console_.autoInjectScreenLines ?? 24}
|
|
onChange={v => onChange('ssh.console.autoInjectScreenLines', Number(v))}
|
|
/>
|
|
<HelpText>{t('ssh.config.autoInjectHelp')}</HelpText>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<div>
|
|
<FieldLabel>Default cols</FieldLabel>
|
|
<FieldInput
|
|
type="number"
|
|
value={console_.defaultCols ?? 120}
|
|
onChange={v => onChange('ssh.console.defaultCols', Number(v))}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<FieldLabel>Default rows</FieldLabel>
|
|
<FieldInput
|
|
type="number"
|
|
value={console_.defaultRows ?? 32}
|
|
onChange={v => onChange('ssh.console.defaultRows', Number(v))}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<HelpText>{t('ssh.config.ptySizeHelp')}</HelpText>
|
|
</div>
|
|
);
|
|
}
|