@porosys/pss
Version:
Porosys Server Setup (pss): General-purpose server setup and automation tool (including Netdata management)
170 lines (155 loc) • 5.5 kB
text/typescript
import path from 'path';
import { input, select, confirm } from '@inquirer/prompts';
import chalk from 'chalk';
import { loadConfig, saveConfig } from '../config/config.helper';
import { setupUfwAlert } from '../alerts/setup-ufw-alert';
import { setupSshConfigAlert } from '../alerts/setup-ssh-config-alert';
import { setupMongodServiceDeactiveAlert } from '../alerts/setup-mongod-service-deactive-alert';
import { setupMockServiceDeactiveAlert } from '../alerts/setup-mock-service-deactive-alert';
import { setupPasswdAlert } from '../alerts/setup-passwd-alert';
import { setupShadowAlert } from '../alerts/setup-shadow-alert';
import { setupSudoersAlert } from '../alerts/setup-sudoers-alert';
import { setupSshBruteforceAlert } from '../alerts/setup-ssh-bruteforce-alert';
export const netdataAddAlertCommand = async () => {
console.log(chalk.green.bold('\n📦 Updating Netdata Alarm Config'));
const config = await loadConfig();
if (!config) {
console.log(
chalk.yellow('⚠️ No config found. Run `pss netdata install` first.'),
);
return;
}
if (!config.healthdPath) {
const possiblePaths = [
'/opt/netdata/etc/netdata/health.d',
'/etc/netdata/health.d',
];
let detectedPath = '';
for (const p of possiblePaths) {
try {
await import('fs')
.then((fs) => fs.promises.access(p))
.then(() => {
detectedPath = p;
});
if (detectedPath) break;
} catch {}
}
if (detectedPath) {
console.log(chalk.gray(`Detected health.d path: ${detectedPath}`));
config.healthdPath = detectedPath;
} else {
const healthdPath = await input({
message: 'Enter the path to your Netdata health.d folder: ',
});
config.healthdPath = healthdPath;
}
}
// Compute and store healthScriptsPath as sibling to healthdPath
const computedHealthScriptsPath = path.join(
path.dirname(config.healthdPath),
'health-scripts',
);
if (!config.healthScriptsPath) {
config.healthScriptsPath = computedHealthScriptsPath;
await saveConfig(config);
}
let done = false;
while (!done) {
const alertCategory = await select({
message: 'Which alert category would you like to set up?',
choices: [
{ name: 'Sensitive Change', value: 'sensitive-change' },
{ name: 'Service Deactivation', value: 'service-deactivation' },
{ name: 'Attack Detection', value: 'attack-detection' },
{ name: 'Exit', value: 'exit' },
],
});
if (alertCategory === 'exit') return;
let alertType;
let backToCategory = false;
while (!alertType && !backToCategory) {
if (alertCategory === 'sensitive-change') {
alertType = await select({
message: 'Which sensitive change would you like to monitor?',
choices: [
{ name: 'ufw', value: 'ufw-change' },
{ name: 'sshd-config', value: 'sshd-config-change' },
{ name: 'passwd', value: 'passwd-change' },
{ name: 'sudoers', value: 'sudoers-change' },
{ name: 'shadow', value: 'shadow-change' },
{ name: '⬅️ Back', value: 'back' },
],
});
} else if (alertCategory === 'service-deactivation') {
alertType = await select({
message: 'Which service deactivation would you like to monitor?',
choices: [
{ name: 'mongod', value: 'mongod-service-deactive' },
{ name: 'mock-service', value: 'mock-service-deactive' },
{ name: '⬅️ Back', value: 'back' },
],
});
} else if (alertCategory === 'attack-detection') {
alertType = await select({
message: 'Which attack detection would you like to monitor?',
choices: [{ name: 'ssh-bruteforce', value: 'ssh-bruteforce-attack' }],
});
}
if (alertType === 'back') {
backToCategory = true;
break;
}
}
if (backToCategory) continue;
switch (alertType) {
case 'ufw-change':
await setupUfwAlert();
break;
case 'sshd-config-change':
await setupSshConfigAlert();
break;
case 'passwd-change':
await setupPasswdAlert();
break;
case 'sudoers-change':
await setupSudoersAlert();
break;
case 'shadow-change':
await setupShadowAlert();
break;
case 'mongod-service-deactive':
await setupMongodServiceDeactiveAlert();
break;
case 'mock-service-deactive':
await setupMockServiceDeactiveAlert();
break;
case 'ssh-bruteforce-attack':
await setupSshBruteforceAlert();
break;
default:
console.log(chalk.yellow('⚠️ Alert type not found.'));
}
// Ask if user wants to add another alert or apply changes
const addAnother = await confirm({
message: 'Would you like to add another alert?',
default: false,
});
if (!addAnother) {
done = true;
}
}
// Reload Netdata health after all alerts are set up
const { execa } = await import('execa');
try {
await execa('/opt/netdata/bin/netdatacli', ['reload-health'], {
stdio: 'inherit',
});
console.log(chalk.green('✅ Netdata health reloaded.'));
} catch (err) {
console.log(chalk.red('❌ Failed to reload Netdata health.'));
if (err instanceof Error) {
console.error(err.message);
}
}
};