mirror of
https://github.com/valitydev/wazuh-kibana-app.git
synced 2024-11-07 02:15:24 +00:00
680 lines
19 KiB
JavaScript
680 lines
19 KiB
JavaScript
/*
|
|
* Wazuh app - Module for agent info fetching functions
|
|
* Copyright (C) 2018 Wazuh, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Find more information about this on the LICENSE file.
|
|
*/
|
|
import cron from 'node-cron';
|
|
import needle from 'needle';
|
|
import { getPath } from '../util/get-path';
|
|
import colors from 'ansicolors';
|
|
import { log } from './logger';
|
|
import { ElasticWrapper } from './lib/elastic-wrapper';
|
|
import { monitoringTemplate } from './integration-files/monitoring-template';
|
|
import packageJSON from '../package.json';
|
|
import { getConfiguration } from './lib/get-configuration';
|
|
import { parseCron } from './lib/parse-cron';
|
|
import { BuildBody } from './lib/replicas-shards-helper';
|
|
|
|
export function Monitoring(server, options) {
|
|
const blueWazuh = colors.blue('wazuh');
|
|
|
|
let ENABLED = true;
|
|
let FREQUENCY = 3600;
|
|
let CRON_FREQ = '0 1 * * * *';
|
|
try {
|
|
const configFile = getConfiguration();
|
|
|
|
ENABLED =
|
|
configFile &&
|
|
typeof configFile['wazuh.monitoring.enabled'] !== 'undefined'
|
|
? configFile['wazuh.monitoring.enabled'] &&
|
|
configFile['wazuh.monitoring.enabled'] !== 'worker'
|
|
: ENABLED;
|
|
FREQUENCY =
|
|
configFile &&
|
|
typeof configFile['wazuh.monitoring.frequency'] !== 'undefined'
|
|
? configFile['wazuh.monitoring.frequency']
|
|
: FREQUENCY;
|
|
|
|
CRON_FREQ = parseCron(FREQUENCY);
|
|
|
|
!options &&
|
|
log(
|
|
'[monitoring][configuration]',
|
|
`wazuh.monitoring.enabled: ${ENABLED}`,
|
|
'info'
|
|
);
|
|
!options &&
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'info'],
|
|
`wazuh.monitoring.enabled: ${ENABLED}`
|
|
);
|
|
|
|
!options &&
|
|
log(
|
|
'[monitoring][configuration]',
|
|
`wazuh.monitoring.frequency: ${FREQUENCY} (${CRON_FREQ}) `,
|
|
'info'
|
|
);
|
|
!options &&
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'info'],
|
|
`wazuh.monitoring.frequency: ${FREQUENCY} (${CRON_FREQ})`
|
|
);
|
|
} catch (error) {
|
|
log('[monitoring][configuration]', error.message || error);
|
|
server.log([blueWazuh, 'monitoring', 'error'], error.message || error);
|
|
}
|
|
|
|
const index_pattern = 'wazuh-monitoring-3.x-*';
|
|
const index_prefix = 'wazuh-monitoring-3.x-';
|
|
|
|
// Elastic JS Client
|
|
const wzWrapper = new ElasticWrapper(server);
|
|
|
|
// Initialize
|
|
let agentsArray = [];
|
|
|
|
let fDate = new Date()
|
|
.toISOString()
|
|
.replace(/T/, '-')
|
|
.replace(/\..+/, '')
|
|
.replace(/-/g, '.')
|
|
.replace(/:/g, '')
|
|
.slice(0, -7);
|
|
let todayIndex = index_prefix + fDate;
|
|
|
|
// Check status and get agent status array
|
|
const checkStatus = async (apiEntry, maxSize) => {
|
|
try {
|
|
if (!maxSize) {
|
|
throw new Error('You must provide a max size');
|
|
}
|
|
|
|
const payload = {
|
|
offset: 0,
|
|
limit: 500
|
|
};
|
|
|
|
const options = {
|
|
headers: {
|
|
'wazuh-app-version': packageJSON.version
|
|
},
|
|
username: apiEntry.user,
|
|
password: apiEntry.password,
|
|
rejectUnauthorized: !apiEntry.insecure
|
|
};
|
|
|
|
while (agentsArray.length < maxSize) {
|
|
const response = await needle(
|
|
'get',
|
|
`${getPath(apiEntry)}/agents`,
|
|
payload,
|
|
options
|
|
);
|
|
if (!response.error && response.body.data.items) {
|
|
agentsArray = agentsArray.concat(response.body.data.items);
|
|
payload.offset += payload.limit;
|
|
} else {
|
|
throw new Error('Can not access Wazuh API');
|
|
}
|
|
}
|
|
|
|
await saveStatus(apiEntry.clusterName);
|
|
|
|
return;
|
|
} catch (error) {
|
|
agentsArray = [];
|
|
log('[monitoring][checkStatus]', error.message || error);
|
|
server.log([blueWazuh, 'monitoring', 'error'], error.message || error);
|
|
}
|
|
};
|
|
|
|
// Check API status twice and get agents total items
|
|
const checkAndSaveStatus = async apiEntry => {
|
|
try {
|
|
const payload = {
|
|
offset: 0,
|
|
limit: 1
|
|
};
|
|
|
|
const options = {
|
|
headers: {
|
|
'wazuh-app-version': packageJSON.version
|
|
},
|
|
username: apiEntry.user,
|
|
password: apiEntry.password,
|
|
rejectUnauthorized: !apiEntry.insecure
|
|
};
|
|
|
|
const response = await needle(
|
|
'get',
|
|
`${getPath(apiEntry)}/agents`,
|
|
payload,
|
|
options
|
|
);
|
|
|
|
const isCluster = await needle(
|
|
'get',
|
|
`${getPath(apiEntry)}/cluster/status`,
|
|
{},
|
|
options
|
|
);
|
|
const clusterName =
|
|
isCluster &&
|
|
isCluster.body &&
|
|
isCluster.body.data &&
|
|
isCluster.body.data.enabled === 'yes'
|
|
? await needle(
|
|
'get',
|
|
`${getPath(apiEntry)}/cluster/node`,
|
|
{},
|
|
options
|
|
)
|
|
: false;
|
|
|
|
apiEntry.clusterName =
|
|
clusterName &&
|
|
clusterName.body &&
|
|
clusterName.body.data &&
|
|
clusterName.body.data.cluster
|
|
? clusterName.body.data.cluster
|
|
: false;
|
|
|
|
if (
|
|
!response.error &&
|
|
response.body.data &&
|
|
response.body.data.totalItems
|
|
) {
|
|
await checkStatus(apiEntry, response.body.data.totalItems);
|
|
} else {
|
|
log(
|
|
'[monitoring][checkAndSaveStatus]',
|
|
'Wazuh API credentials not found or are not correct. Open the app in your browser and configure it to start monitoring agents.'
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'error'],
|
|
'Wazuh API credentials not found or are not correct. Open the app in your browser and configure it to start monitoring agents.'
|
|
);
|
|
}
|
|
return;
|
|
} catch (error) {
|
|
log('[monitoring][checkAndSaveStatus]', error.message || error);
|
|
server.log([blueWazuh, 'monitoring', 'error'], error.message || error);
|
|
}
|
|
};
|
|
|
|
// Load Wazuh API credentials from Elasticsearch document
|
|
const loadCredentials = async apiEntries => {
|
|
try {
|
|
if (typeof apiEntries === 'undefined' || !('hits' in apiEntries)) return;
|
|
|
|
const filteredApis = apiEntries.hits.filter(
|
|
(element, index, self) =>
|
|
index ===
|
|
self.findIndex(
|
|
t =>
|
|
t._source.api_user === element._source.api_user &&
|
|
t._source.api_password === element._source.api_password &&
|
|
t._source.url === element._source.url &&
|
|
t._source.api_port === element._source.api_port
|
|
)
|
|
);
|
|
|
|
for (const element of filteredApis) {
|
|
const apiEntry = {
|
|
user: element._source.api_user,
|
|
password: Buffer.from(
|
|
element._source.api_password,
|
|
'base64'
|
|
).toString('ascii'),
|
|
url: element._source.url,
|
|
port: element._source.api_port,
|
|
insecure: element._source.insecure
|
|
};
|
|
|
|
await checkAndSaveStatus(apiEntry);
|
|
}
|
|
|
|
return { result: 'ok' };
|
|
} catch (error) {
|
|
log('[monitoring][loadCredentials]', error.message || error);
|
|
server.log([blueWazuh, 'monitoring', 'error'], error.message || error);
|
|
}
|
|
};
|
|
|
|
// Get API configuration from elastic and callback to loadCredentials
|
|
const getConfig = async () => {
|
|
try {
|
|
const data = await wzWrapper.getWazuhAPIEntries();
|
|
|
|
if (data.hits.total > 0) {
|
|
return data.hits;
|
|
}
|
|
|
|
log(
|
|
'[monitoring][getConfig]',
|
|
'There is no Wazuh API entries yet',
|
|
'info'
|
|
);
|
|
return {
|
|
error: 'no credentials',
|
|
error_code: 1
|
|
};
|
|
} catch (error) {
|
|
log('[monitoring][getConfig]', error.message || error);
|
|
return {
|
|
error: 'no elasticsearch',
|
|
error_code: 2
|
|
};
|
|
}
|
|
};
|
|
|
|
// fetchAgents on demand
|
|
const fetchAgentsExternal = async () => {
|
|
try {
|
|
const data = await getConfig();
|
|
return loadCredentials(data);
|
|
} catch (error) {
|
|
return Promise.reject(error);
|
|
}
|
|
};
|
|
|
|
// Configure Kibana patterns.
|
|
const configureKibana = async () => {
|
|
try {
|
|
log(
|
|
'[monitoring][configureKibana]',
|
|
`Creating index pattern: ${index_pattern}`,
|
|
'info'
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'info'],
|
|
`Creating index pattern: ${index_pattern}`
|
|
);
|
|
|
|
await wzWrapper.createMonitoringIndexPattern(index_pattern);
|
|
|
|
log(
|
|
'[monitoring][configureKibana]',
|
|
`Created index pattern: ${index_pattern}`,
|
|
'info'
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'info'],
|
|
`Created index pattern: ${index_pattern}`
|
|
);
|
|
|
|
return;
|
|
} catch (error) {
|
|
log('[monitoring][configureKibana]', error.message || error);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'error'],
|
|
'Error creating index-pattern due to ' + error
|
|
);
|
|
}
|
|
};
|
|
|
|
// Creating wazuh-monitoring index
|
|
const createIndex = async (todayIndex, clusterName) => {
|
|
try {
|
|
if (!ENABLED) return;
|
|
const configFile = getConfiguration();
|
|
|
|
const shards =
|
|
configFile &&
|
|
typeof configFile['wazuh.monitoring.shards'] !== 'undefined'
|
|
? configFile['wazuh.monitoring.shards']
|
|
: 5;
|
|
|
|
const replicas =
|
|
configFile &&
|
|
typeof configFile['wazuh.monitoring.replicas'] !== 'undefined'
|
|
? configFile['wazuh.monitoring.replicas']
|
|
: 1;
|
|
|
|
const configuration = {
|
|
settings: {
|
|
index: {
|
|
number_of_shards: shards,
|
|
number_of_replicas: replicas
|
|
}
|
|
}
|
|
};
|
|
|
|
await wzWrapper.createIndexByName(todayIndex, configuration);
|
|
|
|
log(
|
|
'[monitoring][createIndex]',
|
|
'Successfully created today index.',
|
|
'info'
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'info'],
|
|
'Successfully created today index.'
|
|
);
|
|
await insertDocument(todayIndex, clusterName);
|
|
return;
|
|
} catch (error) {
|
|
log(
|
|
'[monitoring][createIndex]',
|
|
`Could not create ${todayIndex} index on elasticsearch due to ${error.message ||
|
|
error}`
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'error'],
|
|
`Could not create ${todayIndex} index on elasticsearch due to ${error.message ||
|
|
error}`
|
|
);
|
|
}
|
|
};
|
|
|
|
// Inserting one document per agent into Elastic. Bulk.
|
|
const insertDocument = async (todayIndex, clusterName) => {
|
|
try {
|
|
let body = '';
|
|
if (agentsArray.length > 0) {
|
|
for (const element of agentsArray) {
|
|
body +=
|
|
'{ "index": { "_index": "' +
|
|
todayIndex +
|
|
'", "_type": "wazuh-agent" } }\n';
|
|
let date = new Date(Date.now()).toISOString();
|
|
element['@timestamp'] = date;
|
|
element.host = element.manager;
|
|
element.cluster = { name: clusterName ? clusterName : 'disabled' };
|
|
body += JSON.stringify(element) + '\n';
|
|
}
|
|
if (body === '') return;
|
|
|
|
await wzWrapper.pushBulkAnyIndex(todayIndex, body);
|
|
|
|
agentsArray = [];
|
|
}
|
|
return;
|
|
} catch (error) {
|
|
log(
|
|
'[monitoring][insertDocument]',
|
|
`Error inserting agent data into elasticsearch. Bulk request failed due to ${error.message ||
|
|
error}`
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'error'],
|
|
`Error inserting agent data into elasticsearch. Bulk request failed due to ${error.message ||
|
|
error}`
|
|
);
|
|
}
|
|
};
|
|
|
|
// Save agent status into elasticsearch, create index and/or insert document
|
|
const saveStatus = async clusterName => {
|
|
try {
|
|
if (!ENABLED) return;
|
|
fDate = new Date()
|
|
.toISOString()
|
|
.replace(/T/, '-')
|
|
.replace(/\..+/, '')
|
|
.replace(/-/g, '.')
|
|
.replace(/:/g, '')
|
|
.slice(0, -7);
|
|
todayIndex = index_prefix + fDate;
|
|
|
|
const result = await wzWrapper.checkIfIndexExists(todayIndex);
|
|
|
|
if (result) {
|
|
const configurationFile = getConfiguration();
|
|
const shardConfiguration = BuildBody(
|
|
configurationFile,
|
|
'wazuh.monitoring',
|
|
5,
|
|
1
|
|
);
|
|
await wzWrapper.updateIndexSettings(todayIndex, shardConfiguration);
|
|
await insertDocument(todayIndex, clusterName);
|
|
} else {
|
|
await createIndex(todayIndex, clusterName);
|
|
}
|
|
|
|
return;
|
|
} catch (error) {
|
|
log(
|
|
'[monitoring][saveStatus]',
|
|
`Could not check if the index ${todayIndex} exists due to ${error.message ||
|
|
error}`
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'error'],
|
|
`Could not check if the index ${todayIndex} exists due to ${error.message ||
|
|
error}`
|
|
);
|
|
}
|
|
};
|
|
|
|
const createWazuhMonitoring = async () => {
|
|
try {
|
|
try {
|
|
await wzWrapper.deleteMonitoring();
|
|
|
|
log(
|
|
'[monitoring][createWazuhMonitoring]',
|
|
'Successfully deleted old wazuh-monitoring pattern.',
|
|
'info'
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'info'],
|
|
'Successfully deleted old wazuh-monitoring pattern.'
|
|
);
|
|
} catch (error) {
|
|
log(
|
|
'[monitoring][createWazuhMonitoring]',
|
|
'No need to delete old wazuh-monitoring pattern.',
|
|
'info'
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'info'],
|
|
'No need to delete old wazuh-monitoring pattern.'
|
|
);
|
|
}
|
|
|
|
await configureKibana();
|
|
return;
|
|
} catch (error) {
|
|
return Promise.reject(error);
|
|
}
|
|
};
|
|
|
|
const checkTemplate = async () => {
|
|
try {
|
|
log(
|
|
'[monitoring][checkTemplate]',
|
|
'Updating wazuh-monitoring template...',
|
|
'info'
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'info'],
|
|
'Updating wazuh-monitoring template...'
|
|
);
|
|
await wzWrapper.putMonitoringTemplate(monitoringTemplate);
|
|
return;
|
|
} catch (error) {
|
|
log(
|
|
'[monitoring][checkTemplate]',
|
|
`Something went wrong updating wazuh-monitoring template... ${error.message ||
|
|
error}`
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'error'],
|
|
`Something went wrong updating wazuh-monitoring template... ${error.message ||
|
|
error}`
|
|
);
|
|
return Promise.reject(error);
|
|
}
|
|
};
|
|
|
|
// Main. First execution when installing / loading App.
|
|
const init = async () => {
|
|
try {
|
|
log(
|
|
'[monitoring][init]',
|
|
'Creating/Updating wazuh-agent template...',
|
|
'info'
|
|
);
|
|
await checkTemplate();
|
|
|
|
log('[monitoring][init]', 'Creating today index...', 'info');
|
|
server.log([blueWazuh, 'monitoring', 'info'], 'Creating today index...');
|
|
|
|
await saveStatus();
|
|
|
|
const patternId = 'index-pattern:' + index_pattern;
|
|
|
|
// Checks if wazuh-monitoring index pattern is already created, if it fails create it
|
|
try {
|
|
log(
|
|
'[monitoring][init]',
|
|
'Checking if wazuh-monitoring pattern exists...',
|
|
'info'
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'info'],
|
|
'Checking if wazuh-monitoring pattern exists...'
|
|
);
|
|
await wzWrapper.getIndexPatternUsingGet(patternId);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'info'],
|
|
'Updating known fields for wazuh-monitoring pattern...'
|
|
);
|
|
log(
|
|
'[monitoring][init]',
|
|
'Updating known fields for wazuh-monitoring pattern...',
|
|
'info'
|
|
);
|
|
await wzWrapper.updateMonitoringIndexPatternKnownFields(patternId);
|
|
} catch (error) {
|
|
log(
|
|
'[monitoring][init]',
|
|
"Didn't find wazuh-monitoring pattern for Kibana v6.x. Proceeding to create it...",
|
|
'info'
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'info'],
|
|
"Didn't find wazuh-monitoring pattern for Kibana v6.x. Proceeding to create it..."
|
|
);
|
|
return createWazuhMonitoring();
|
|
}
|
|
|
|
log(
|
|
'[monitoring][init]',
|
|
'Skipping wazuh-monitoring pattern creation. Already exists.',
|
|
'info'
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'info'],
|
|
'Skipping wazuh-monitoring creation. Already exists.'
|
|
);
|
|
|
|
return;
|
|
} catch (error) {
|
|
server.log([blueWazuh, 'monitoring', 'error'], error.message || error);
|
|
log('[monitoring][init]', error.message || error);
|
|
return;
|
|
}
|
|
};
|
|
|
|
// Check Elasticsearch Server status and Kibana index presence
|
|
const checkElasticsearchServer = async () => {
|
|
try {
|
|
const data = await wzWrapper.checkIfIndexExists(
|
|
wzWrapper.WZ_KIBANA_INDEX
|
|
);
|
|
|
|
if (data) {
|
|
const pluginsData = await server.plugins.elasticsearch.waitUntilReady();
|
|
return pluginsData;
|
|
}
|
|
return Promise.reject(data);
|
|
} catch (error) {
|
|
log('[monitoring][checkElasticsearchServer]', error.message || error);
|
|
return Promise.reject(error);
|
|
}
|
|
};
|
|
|
|
// Wait until Kibana server is ready
|
|
const checkKibanaStatus = async () => {
|
|
try {
|
|
log(
|
|
'[monitoring][checkKibanaStatus]',
|
|
'Waiting for Kibana and Elasticsearch servers to be ready...',
|
|
'info'
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'info'],
|
|
'Waiting for Kibana and Elasticsearch servers to be ready...'
|
|
);
|
|
await checkElasticsearchServer();
|
|
await init();
|
|
return;
|
|
} catch (error) {
|
|
log(
|
|
'[monitoring][checkKibanaStatus]',
|
|
'Waiting for Kibana and Elasticsearch servers to be ready...',
|
|
'info'
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring', 'info'],
|
|
'Waiting for Kibana and Elasticsearch servers to be ready...',
|
|
'info'
|
|
);
|
|
setTimeout(() => checkKibanaStatus(), 3000);
|
|
}
|
|
};
|
|
|
|
// Check Kibana index and if it is prepared, start the initialization of Wazuh App.
|
|
if (!options) checkKibanaStatus();
|
|
|
|
const cronTask = async () => {
|
|
try {
|
|
const template = await wzWrapper.getTemplateByName('wazuh-agent');
|
|
|
|
// Prevents to insert monitoring indices without the proper template inserted
|
|
if (
|
|
typeof template === 'object' &&
|
|
typeof template['wazuh-agent'] !== 'undefined' &&
|
|
typeof template['wazuh-agent'].index_patterns !== 'undefined'
|
|
) {
|
|
agentsArray = [];
|
|
const data = await getConfig();
|
|
await loadCredentials(data);
|
|
} else {
|
|
log(
|
|
'[monitoring][cronTask]',
|
|
'No wazuh-agent template found, not inserting monitoring data',
|
|
'info'
|
|
);
|
|
server.log(
|
|
[blueWazuh, 'monitoring [cronTask]', 'info'],
|
|
'No wazuh-agent template found, not inserting monitoring data'
|
|
);
|
|
}
|
|
|
|
return;
|
|
} catch (error) {
|
|
log('[monitoring][cronTask]', error.message || error);
|
|
server.log(
|
|
[blueWazuh, 'monitoring [cronTask]', 'error'],
|
|
error.message || error
|
|
);
|
|
}
|
|
};
|
|
if (!options && ENABLED) cronTask();
|
|
// Cron tab for getting agent status.
|
|
if (!options && ENABLED) cron.schedule(CRON_FREQ, cronTask, true);
|
|
return fetchAgentsExternal;
|
|
}
|