2018-04-22 19:10:29 +00:00
|
|
|
/*
|
|
|
|
* Wazuh app - Class for Wazuh-Elastic functions
|
2019-01-14 16:36:47 +00:00
|
|
|
* Copyright (C) 2015-2019 Wazuh, Inc.
|
2018-04-22 19:10:29 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
import { ElasticWrapper } from '../lib/elastic-wrapper';
|
2018-09-03 09:46:55 +00:00
|
|
|
import { ErrorResponse } from './error-response';
|
2018-09-10 08:32:49 +00:00
|
|
|
import { log } from '../logger';
|
2018-10-05 13:10:59 +00:00
|
|
|
import { getConfiguration } from '../lib/get-configuration';
|
2018-09-10 08:32:49 +00:00
|
|
|
import {
|
|
|
|
AgentsVisualizations,
|
|
|
|
OverviewVisualizations,
|
|
|
|
ClusterVisualizations
|
|
|
|
} from '../integration-files/visualizations';
|
2018-04-23 10:31:28 +00:00
|
|
|
|
2018-12-03 15:07:59 +00:00
|
|
|
import { Base } from '../reporting/base-query';
|
2019-01-03 10:35:02 +00:00
|
|
|
import { checkKnownFields } from '../lib/refresh-known-fields';
|
2018-09-03 09:46:55 +00:00
|
|
|
export class WazuhElasticCtrl {
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
2018-12-13 10:02:53 +00:00
|
|
|
* Constructor
|
|
|
|
* @param {*} server
|
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
constructor(server) {
|
|
|
|
this.wzWrapper = new ElasticWrapper(server);
|
|
|
|
}
|
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
2018-12-13 10:02:53 +00:00
|
|
|
* This get the timestamp field
|
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} reply
|
|
|
|
* @returns {Object} timestamp field or ErrorResponse
|
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
async getTimeStamp(req, reply) {
|
|
|
|
try {
|
|
|
|
const data = await this.wzWrapper.getWazuhVersionIndexAsSearch();
|
2018-12-19 11:10:14 +00:00
|
|
|
const source =
|
|
|
|
((((data || {}).hits || {}).hits || [])[0] || {})._source || {};
|
2018-12-19 11:04:57 +00:00
|
|
|
|
|
|
|
if (source.installationDate && source.lastRestart) {
|
2018-09-10 08:32:49 +00:00
|
|
|
return reply({
|
|
|
|
installationDate: data.hits.hits[0]._source.installationDate,
|
|
|
|
lastRestart: data.hits.hits[0]._source.lastRestart
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
throw new Error('Could not fetch .wazuh-version index');
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
return ErrorResponse(
|
|
|
|
error.message || 'Could not fetch .wazuh-version index',
|
|
|
|
4001,
|
|
|
|
500,
|
|
|
|
reply
|
|
|
|
);
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
2018-12-13 10:02:53 +00:00
|
|
|
* This retrieve a template from Elasticsearch
|
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} reply
|
|
|
|
* @returns {Object} template or ErrorResponse
|
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
async getTemplate(req, reply) {
|
|
|
|
try {
|
|
|
|
if (!req.params || !req.params.pattern) {
|
|
|
|
throw new Error(
|
|
|
|
'An index pattern is needed for checking the Elasticsearch template existance'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const data = await this.wzWrapper.getTemplates();
|
|
|
|
|
|
|
|
if (!data || typeof data !== 'string') {
|
|
|
|
throw new Error(
|
|
|
|
'An unknown error occurred when fetching templates from Elasticseach'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const lastChar = req.params.pattern[req.params.pattern.length - 1];
|
2018-12-19 11:10:14 +00:00
|
|
|
|
|
|
|
// Split into separate patterns
|
2018-12-19 10:49:41 +00:00
|
|
|
const tmpdata = data.match(/\[.*\]/g);
|
|
|
|
const tmparray = [];
|
2018-12-19 11:10:14 +00:00
|
|
|
for (let item of tmpdata) {
|
2018-12-19 10:49:41 +00:00
|
|
|
// A template might use more than one pattern
|
2018-12-19 11:10:14 +00:00
|
|
|
if (item.includes(',')) {
|
2018-12-19 10:49:41 +00:00
|
|
|
item = item.substr(1).slice(0, -1);
|
2018-12-19 11:10:14 +00:00
|
|
|
const subItems = item.split(',');
|
|
|
|
for (const subitem of subItems) {
|
|
|
|
tmparray.push(`[${subitem.trim()}]`);
|
2018-12-19 10:49:41 +00:00
|
|
|
}
|
|
|
|
} else {
|
2018-12-19 11:10:14 +00:00
|
|
|
tmparray.push(item);
|
2018-12-19 10:49:41 +00:00
|
|
|
}
|
|
|
|
}
|
2018-12-19 11:10:14 +00:00
|
|
|
|
2018-12-19 10:49:41 +00:00
|
|
|
// Ensure we are handling just patterns
|
2018-12-19 11:10:14 +00:00
|
|
|
const array = tmparray.filter(
|
|
|
|
item => item.includes('[') && item.includes(']')
|
|
|
|
);
|
2018-12-19 10:49:41 +00:00
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
const pattern =
|
|
|
|
lastChar === '*' ? req.params.pattern.slice(0, -1) : req.params.pattern;
|
2018-10-28 09:43:09 +00:00
|
|
|
const isIncluded = array.filter(item => {
|
|
|
|
item = item.slice(1, -1);
|
|
|
|
const lastChar = item[item.length - 1];
|
|
|
|
item = lastChar === '*' ? item.slice(0, -1) : item;
|
|
|
|
return item.includes(pattern) || pattern.includes(item);
|
|
|
|
});
|
2018-09-10 08:32:49 +00:00
|
|
|
|
|
|
|
return isIncluded && Array.isArray(isIncluded) && isIncluded.length
|
|
|
|
? reply({
|
2018-12-13 10:02:53 +00:00
|
|
|
statusCode: 200,
|
|
|
|
status: true,
|
|
|
|
data: `Template found for ${req.params.pattern}`
|
|
|
|
})
|
2018-09-10 08:32:49 +00:00
|
|
|
: reply({
|
2018-12-13 10:02:53 +00:00
|
|
|
statusCode: 200,
|
|
|
|
status: false,
|
|
|
|
data: `No template found for ${req.params.pattern}`
|
|
|
|
});
|
2018-09-10 08:32:49 +00:00
|
|
|
} catch (error) {
|
2018-10-01 07:56:50 +00:00
|
|
|
log('GET /elastic/template/{pattern}', error.message || error);
|
2018-09-10 08:32:49 +00:00
|
|
|
return ErrorResponse(
|
|
|
|
`Could not retrieve templates from Elasticsearch due to ${error.message ||
|
2018-12-13 10:02:53 +00:00
|
|
|
error}`,
|
2018-09-10 08:32:49 +00:00
|
|
|
4002,
|
|
|
|
500,
|
|
|
|
reply
|
|
|
|
);
|
2018-03-11 11:16:29 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
2018-12-13 10:02:53 +00:00
|
|
|
* This check index-pattern
|
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} reply
|
|
|
|
* @returns {Object} status obj or ErrorResponse
|
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
async checkPattern(req, reply) {
|
|
|
|
try {
|
|
|
|
const response = await this.wzWrapper.getAllIndexPatterns();
|
|
|
|
|
|
|
|
const filtered = response.hits.hits.filter(
|
|
|
|
item => item._source['index-pattern'].title === req.params.pattern
|
|
|
|
);
|
|
|
|
|
|
|
|
return filtered.length >= 1
|
|
|
|
? reply({ statusCode: 200, status: true, data: 'Index pattern found' })
|
|
|
|
: reply({
|
2018-12-13 10:02:53 +00:00
|
|
|
statusCode: 500,
|
|
|
|
status: false,
|
|
|
|
error: 10020,
|
|
|
|
message: 'Index pattern not found'
|
|
|
|
});
|
2018-09-10 08:32:49 +00:00
|
|
|
} catch (error) {
|
2018-10-01 07:56:50 +00:00
|
|
|
log('GET /elastic/index-patterns/{pattern}', error.message || error);
|
2018-09-10 08:32:49 +00:00
|
|
|
return ErrorResponse(
|
|
|
|
`Something went wrong retrieving index-patterns from Elasticsearch due to ${error.message ||
|
2018-12-13 10:02:53 +00:00
|
|
|
error}`,
|
2018-09-10 08:32:49 +00:00
|
|
|
4003,
|
|
|
|
500,
|
|
|
|
reply
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
|
|
|
* This get the fields keys
|
2018-12-13 10:02:53 +00:00
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} reply
|
2018-12-12 11:47:05 +00:00
|
|
|
* @returns {Array<Object>} fields or ErrorResponse
|
2018-12-11 16:12:59 +00:00
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
async getFieldTop(req, reply) {
|
|
|
|
try {
|
|
|
|
// Top field payload
|
|
|
|
let payload = {
|
|
|
|
size: 1,
|
|
|
|
query: {
|
|
|
|
bool: {
|
|
|
|
must: [],
|
|
|
|
filter: { range: { '@timestamp': {} } }
|
|
|
|
}
|
|
|
|
},
|
|
|
|
aggs: {
|
|
|
|
'2': {
|
|
|
|
terms: {
|
|
|
|
field: '',
|
|
|
|
size: 1,
|
|
|
|
order: { _count: 'desc' }
|
2018-01-10 17:00:06 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
2018-03-22 12:35:58 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Set up time interval, default to Last 24h
|
|
|
|
const timeGTE = 'now-1d';
|
|
|
|
const timeLT = 'now';
|
|
|
|
payload.query.bool.filter.range['@timestamp']['gte'] = timeGTE;
|
|
|
|
payload.query.bool.filter.range['@timestamp']['lt'] = timeLT;
|
|
|
|
|
|
|
|
// Set up match for default cluster name
|
|
|
|
payload.query.bool.must.push(
|
|
|
|
req.params.mode === 'cluster'
|
|
|
|
? { match: { 'cluster.name': req.params.cluster } }
|
|
|
|
: { match: { 'manager.name': req.params.cluster } }
|
|
|
|
);
|
|
|
|
|
|
|
|
payload.aggs['2'].terms.field = req.params.field;
|
|
|
|
payload.pattern = req.params.pattern;
|
|
|
|
const data = await this.wzWrapper.searchWazuhAlertsWithPayload(payload);
|
|
|
|
|
|
|
|
return data.hits.total === 0 ||
|
|
|
|
typeof data.aggregations['2'].buckets[0] === 'undefined'
|
|
|
|
? reply({ statusCode: 200, data: '' })
|
|
|
|
: reply({
|
2018-12-13 10:02:53 +00:00
|
|
|
statusCode: 200,
|
|
|
|
data: data.aggregations['2'].buckets[0].key
|
|
|
|
});
|
2018-09-10 08:32:49 +00:00
|
|
|
} catch (error) {
|
|
|
|
return ErrorResponse(error.message || error, 4004, 500, reply);
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
|
|
|
* This get the elastic setup settings
|
2018-12-13 10:02:53 +00:00
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} reply
|
2018-12-12 11:47:05 +00:00
|
|
|
* @returns {Object} setup info or ErrorResponse
|
2018-12-11 16:12:59 +00:00
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
async getSetupInfo(req, reply) {
|
|
|
|
try {
|
|
|
|
const data = await this.wzWrapper.getWazuhVersionIndexAsSearch();
|
|
|
|
|
|
|
|
return data.hits.total === 0
|
|
|
|
? reply({ statusCode: 200, data: '' })
|
|
|
|
: reply({ statusCode: 200, data: data.hits.hits[0]._source });
|
|
|
|
} catch (error) {
|
2018-10-01 07:56:50 +00:00
|
|
|
log('GET /elastic/setup', error.message || error);
|
2018-09-10 08:32:49 +00:00
|
|
|
return ErrorResponse(
|
|
|
|
`Could not get data from elasticsearch due to ${error.message ||
|
2018-12-13 10:02:53 +00:00
|
|
|
error}`,
|
2018-09-10 08:32:49 +00:00
|
|
|
4005,
|
|
|
|
500,
|
|
|
|
reply
|
|
|
|
);
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks one by one if the requesting user has enough privileges to use
|
|
|
|
* an index pattern from the list.
|
|
|
|
* @param {Array<Object>} list List of index patterns
|
2018-12-11 16:12:59 +00:00
|
|
|
* @param {Object} req
|
2018-12-12 11:47:05 +00:00
|
|
|
* @returns {Array<Object>} List of allowed index
|
2018-09-10 08:32:49 +00:00
|
|
|
*/
|
|
|
|
async filterAllowedIndexPatternList(list, req) {
|
|
|
|
let finalList = [];
|
|
|
|
for (let item of list) {
|
|
|
|
let results = false,
|
|
|
|
forbidden = false;
|
|
|
|
try {
|
|
|
|
results = await this.wzWrapper.searchWazuhElementsByIndexWithRequest(
|
|
|
|
req,
|
|
|
|
item.title
|
|
|
|
);
|
|
|
|
} catch (error) {
|
|
|
|
forbidden = true;
|
|
|
|
}
|
|
|
|
if (
|
2018-12-18 15:19:05 +00:00
|
|
|
((results || {}).hits || {}).total >= 1 ||
|
|
|
|
(!forbidden && ((results || {}).hits || {}).total === 0)
|
2018-09-10 08:32:49 +00:00
|
|
|
) {
|
|
|
|
finalList.push(item);
|
|
|
|
}
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
return finalList;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks for minimum index pattern fields in a list of index patterns.
|
|
|
|
* @param {Array<Object>} indexPatternList List of index patterns
|
|
|
|
*/
|
|
|
|
validateIndexPattern(indexPatternList) {
|
|
|
|
const minimum = ['@timestamp', 'full_log', 'manager.name', 'agent.id'];
|
|
|
|
let list = [];
|
|
|
|
for (const index of indexPatternList) {
|
|
|
|
let valid, parsed;
|
|
|
|
try {
|
|
|
|
parsed = JSON.parse(index._source['index-pattern'].fields);
|
|
|
|
} catch (error) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
valid = parsed.filter(item => minimum.includes(item.name));
|
|
|
|
if (valid.length === 4) {
|
|
|
|
list.push({
|
|
|
|
id: index._id.split('index-pattern:')[1],
|
|
|
|
title: index._source['index-pattern'].title
|
|
|
|
});
|
|
|
|
}
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
|
|
|
* This get the list of index-patterns
|
2018-12-13 10:02:53 +00:00
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} reply
|
2018-12-12 11:47:05 +00:00
|
|
|
* @returns {Array<Object>} list of index-patterns or ErrorResponse
|
2018-12-11 16:12:59 +00:00
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
async getlist(req, reply) {
|
|
|
|
try {
|
2018-10-05 13:10:59 +00:00
|
|
|
const config = getConfiguration();
|
2018-10-28 09:43:09 +00:00
|
|
|
|
2018-12-12 11:23:49 +00:00
|
|
|
const usingCredentials = await this.wzWrapper.usingCredentials();
|
2018-09-10 08:32:49 +00:00
|
|
|
|
|
|
|
const isXpackEnabled =
|
|
|
|
typeof XPACK_RBAC_ENABLED !== 'undefined' &&
|
|
|
|
XPACK_RBAC_ENABLED &&
|
2018-12-12 11:23:49 +00:00
|
|
|
usingCredentials;
|
2018-09-10 08:32:49 +00:00
|
|
|
|
|
|
|
const isSuperUser =
|
|
|
|
isXpackEnabled &&
|
|
|
|
req.auth &&
|
|
|
|
req.auth.credentials &&
|
|
|
|
req.auth.credentials.roles &&
|
|
|
|
req.auth.credentials.roles.includes('superuser');
|
|
|
|
|
|
|
|
const data = await this.wzWrapper.getAllIndexPatterns();
|
|
|
|
|
2018-12-18 15:19:05 +00:00
|
|
|
if ((((data || {}).hits || {}).hits || []).length === 0)
|
2018-09-10 08:32:49 +00:00
|
|
|
throw new Error('There is no index pattern');
|
|
|
|
|
2018-12-18 15:19:05 +00:00
|
|
|
if (((data || {}).hits || {}).hits) {
|
2018-10-05 13:10:59 +00:00
|
|
|
let list = this.validateIndexPattern(data.hits.hits);
|
2018-10-28 09:43:09 +00:00
|
|
|
if (
|
|
|
|
config &&
|
|
|
|
config['ip.ignore'] &&
|
|
|
|
Array.isArray(config['ip.ignore']) &&
|
|
|
|
config['ip.ignore'].length
|
|
|
|
) {
|
|
|
|
list = list.filter(
|
|
|
|
item =>
|
|
|
|
item && item.title && !config['ip.ignore'].includes(item.title)
|
|
|
|
);
|
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
return reply({
|
|
|
|
data:
|
|
|
|
isXpackEnabled && !isSuperUser
|
|
|
|
? await this.filterAllowedIndexPatternList(list, req)
|
|
|
|
: list
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new Error(
|
|
|
|
"The Elasticsearch request didn't fetch the expected data"
|
|
|
|
);
|
|
|
|
} catch (error) {
|
2018-10-01 07:56:50 +00:00
|
|
|
log('GET /elastic/index-patterns', error.message || error);
|
2018-09-10 08:32:49 +00:00
|
|
|
return ErrorResponse(error.message || error, 4006, 500, reply);
|
2018-03-21 14:48:05 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replaces visualizations main fields to fit a certain pattern.
|
|
|
|
* @param {Array<Object>} app_objects Object containing raw visualizations.
|
|
|
|
* @param {String} id Index-pattern id to use in the visualizations. Eg: 'wazuh-alerts'
|
|
|
|
*/
|
|
|
|
buildVisualizationsRaw(app_objects, id) {
|
|
|
|
try {
|
2018-12-17 10:57:31 +00:00
|
|
|
const config = getConfiguration();
|
|
|
|
const monitoringPattern =
|
|
|
|
(config || {})['wazuh.monitoring.pattern'] || 'wazuh-monitoring-3.x-*';
|
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
const visArray = [];
|
|
|
|
let aux_source, bulk_content;
|
|
|
|
for (let element of app_objects) {
|
2018-12-13 10:02:53 +00:00
|
|
|
aux_source = JSON.parse(JSON.stringify(element._source));
|
2018-12-11 16:12:59 +00:00
|
|
|
|
2018-12-03 11:07:18 +00:00
|
|
|
// Replace index-pattern for visualizations
|
2018-12-13 10:02:53 +00:00
|
|
|
if (
|
|
|
|
aux_source &&
|
|
|
|
aux_source.kibanaSavedObjectMeta &&
|
|
|
|
aux_source.kibanaSavedObjectMeta.searchSourceJSON &&
|
|
|
|
typeof aux_source.kibanaSavedObjectMeta.searchSourceJSON === 'string'
|
|
|
|
) {
|
2018-12-17 10:57:31 +00:00
|
|
|
const defaultStr = aux_source.kibanaSavedObjectMeta.searchSourceJSON;
|
|
|
|
|
|
|
|
defaultStr.includes('wazuh-monitoring')
|
|
|
|
? (aux_source.kibanaSavedObjectMeta.searchSourceJSON = defaultStr.replace(
|
|
|
|
'wazuh-monitoring',
|
|
|
|
monitoringPattern[monitoringPattern.length - 1] === '*'
|
|
|
|
? monitoringPattern
|
|
|
|
: monitoringPattern + '*'
|
|
|
|
))
|
|
|
|
: (aux_source.kibanaSavedObjectMeta.searchSourceJSON = defaultStr.replace(
|
|
|
|
'wazuh-alerts',
|
|
|
|
id
|
|
|
|
));
|
2018-12-03 11:07:18 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
|
2018-12-03 11:07:18 +00:00
|
|
|
// Replace index-pattern for selector visualizations
|
2018-12-13 10:02:53 +00:00
|
|
|
if (
|
|
|
|
aux_source &&
|
|
|
|
aux_source.visState &&
|
|
|
|
aux_source.visState &&
|
|
|
|
typeof aux_source.visState === 'string'
|
|
|
|
) {
|
|
|
|
aux_source.visState = aux_source.visState.replace('wazuh-alerts', id);
|
2018-12-03 11:07:18 +00:00
|
|
|
}
|
2018-12-11 16:12:59 +00:00
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
// Bulk source
|
|
|
|
bulk_content = {};
|
|
|
|
bulk_content[element._type] = aux_source;
|
|
|
|
|
|
|
|
visArray.push({
|
|
|
|
attributes: bulk_content.visualization,
|
|
|
|
type: element._type,
|
|
|
|
id: element._id,
|
|
|
|
_version: bulk_content.visualization.version
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return visArray;
|
|
|
|
} catch (error) {
|
|
|
|
return Promise.reject(error);
|
2018-04-11 11:31:46 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replaces cluster visualizations main fields.
|
|
|
|
* @param {Array<Object>} app_objects Object containing raw visualizations.
|
|
|
|
* @param {String} id Index-pattern id to use in the visualizations. Eg: 'wazuh-alerts'
|
|
|
|
* @param {Array<String>} nodes Array of node names. Eg: ['node01', 'node02']
|
|
|
|
* @param {String} name Cluster name. Eg: 'wazuh'
|
|
|
|
* @param {String} master_node Master node name. Eg: 'node01'
|
|
|
|
*/
|
|
|
|
buildClusterVisualizationsRaw(
|
|
|
|
app_objects,
|
|
|
|
id,
|
|
|
|
nodes = [],
|
|
|
|
name,
|
|
|
|
master_node,
|
|
|
|
pattern_name = '*'
|
|
|
|
) {
|
|
|
|
try {
|
|
|
|
const visArray = [];
|
|
|
|
let aux_source, bulk_content;
|
|
|
|
|
|
|
|
for (const element of app_objects) {
|
|
|
|
// Stringify and replace index-pattern for visualizations
|
|
|
|
aux_source = JSON.stringify(element._source);
|
|
|
|
aux_source = aux_source.replace('wazuh-alerts', id);
|
|
|
|
aux_source = JSON.parse(aux_source);
|
|
|
|
|
|
|
|
// Bulk source
|
|
|
|
bulk_content = {};
|
|
|
|
bulk_content[element._type] = aux_source;
|
|
|
|
|
|
|
|
const visState = JSON.parse(bulk_content.visualization.visState);
|
|
|
|
const title = visState.title;
|
|
|
|
|
|
|
|
if (visState.type && visState.type === 'timelion') {
|
|
|
|
let query = '';
|
|
|
|
if (title === 'Wazuh App Cluster Overview') {
|
|
|
|
for (const node of nodes) {
|
|
|
|
query += `.es(index=${pattern_name},q="cluster.name: ${name} AND cluster.node: ${
|
|
|
|
node.name
|
2018-12-13 10:02:53 +00:00
|
|
|
}").label("${node.name}"),`;
|
2018-03-17 11:33:07 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
query = query.substring(0, query.length - 1);
|
|
|
|
} else if (title === 'Wazuh App Cluster Overview Manager') {
|
|
|
|
query += `.es(index=${pattern_name},q="cluster.name: ${name}").label("${name} cluster")`;
|
|
|
|
}
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
visState.params.expression = query;
|
|
|
|
bulk_content.visualization.visState = JSON.stringify(visState);
|
2018-04-04 15:58:27 +00:00
|
|
|
}
|
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
visArray.push({
|
|
|
|
attributes: bulk_content.visualization,
|
|
|
|
type: element._type,
|
|
|
|
id: element._id,
|
|
|
|
_version: bulk_content.visualization.version
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return visArray;
|
|
|
|
} catch (error) {
|
|
|
|
return Promise.reject(error);
|
2018-04-18 11:38:08 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
|
|
|
* This creates a visualization of data in req
|
2018-12-13 10:02:53 +00:00
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} reply
|
2018-12-12 11:47:05 +00:00
|
|
|
* @returns {Object} vis obj or ErrorResponse
|
2018-12-11 16:12:59 +00:00
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
async createVis(req, reply) {
|
|
|
|
try {
|
|
|
|
if (
|
|
|
|
!req.params.pattern ||
|
|
|
|
!req.params.tab ||
|
|
|
|
(req.params.tab &&
|
|
|
|
!req.params.tab.includes('overview-') &&
|
|
|
|
!req.params.tab.includes('agents-'))
|
|
|
|
) {
|
|
|
|
throw new Error('Missing parameters creating visualizations');
|
|
|
|
}
|
|
|
|
|
|
|
|
const tabPrefix = req.params.tab.includes('overview')
|
|
|
|
? 'overview'
|
|
|
|
: 'agents';
|
|
|
|
|
|
|
|
const tabSplit = req.params.tab.split('-');
|
|
|
|
const tabSufix = tabSplit[1];
|
|
|
|
|
|
|
|
const file =
|
|
|
|
tabPrefix === 'overview'
|
|
|
|
? OverviewVisualizations[tabSufix]
|
|
|
|
: AgentsVisualizations[tabSufix];
|
|
|
|
|
|
|
|
const raw = await this.buildVisualizationsRaw(file, req.params.pattern);
|
|
|
|
return reply({ acknowledge: true, raw: raw });
|
|
|
|
} catch (error) {
|
|
|
|
return ErrorResponse(error.message || error, 4007, 500, reply);
|
2018-05-23 11:44:45 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
2018-12-13 10:02:53 +00:00
|
|
|
* This creates a visualization of cluster
|
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} reply
|
|
|
|
* @returns {Object} vis obj or ErrorResponse
|
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
async createClusterVis(req, reply) {
|
|
|
|
try {
|
|
|
|
if (
|
|
|
|
!req.params.pattern ||
|
|
|
|
!req.params.tab ||
|
|
|
|
!req.payload ||
|
|
|
|
!req.payload.nodes ||
|
|
|
|
!req.payload.nodes.items ||
|
|
|
|
!req.payload.nodes.name ||
|
|
|
|
(req.params.tab && !req.params.tab.includes('cluster-'))
|
|
|
|
) {
|
|
|
|
throw new Error('Missing parameters creating visualizations');
|
|
|
|
}
|
|
|
|
|
|
|
|
const file = ClusterVisualizations['monitoring'];
|
|
|
|
const nodes = req.payload.nodes.items;
|
|
|
|
const name = req.payload.nodes.name;
|
|
|
|
const master_node = req.payload.nodes.master_node;
|
|
|
|
|
|
|
|
const pattern_doc = await this.wzWrapper.getIndexPatternUsingGet(
|
|
|
|
req.params.pattern
|
|
|
|
);
|
|
|
|
const pattern_name = pattern_doc._source['index-pattern'].title;
|
|
|
|
|
|
|
|
const raw = await this.buildClusterVisualizationsRaw(
|
|
|
|
file,
|
|
|
|
req.params.pattern,
|
|
|
|
nodes,
|
|
|
|
name,
|
|
|
|
master_node,
|
|
|
|
pattern_name
|
|
|
|
);
|
|
|
|
|
|
|
|
return reply({ acknowledge: true, raw: raw });
|
|
|
|
} catch (error) {
|
|
|
|
return ErrorResponse(error.message || error, 4009, 500, reply);
|
2018-05-23 11:44:45 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
2018-05-23 11:44:45 +00:00
|
|
|
|
2018-12-11 16:12:59 +00:00
|
|
|
/**
|
|
|
|
* Reload elastic index
|
2018-12-13 10:02:53 +00:00
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} reply
|
2018-12-12 11:47:05 +00:00
|
|
|
* @returns {Object} status obj or ErrorResponse
|
2018-12-11 16:12:59 +00:00
|
|
|
*/
|
2018-09-10 08:32:49 +00:00
|
|
|
async refreshIndex(req, reply) {
|
|
|
|
try {
|
|
|
|
if (!req.params.pattern) throw new Error('Missing parameters');
|
2018-04-13 16:00:13 +00:00
|
|
|
|
2019-01-03 10:35:02 +00:00
|
|
|
const output =
|
|
|
|
((req || {}).params || {}).pattern === 'all'
|
|
|
|
? await checkKnownFields(this.wzWrapper, false, false, false, true)
|
|
|
|
: await this.wzWrapper.updateIndexPatternKnownFields(
|
|
|
|
req.params.pattern
|
|
|
|
);
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-09-10 08:32:49 +00:00
|
|
|
return reply({ acknowledge: true, output: output });
|
|
|
|
} catch (error) {
|
2018-10-01 07:56:50 +00:00
|
|
|
log('GET /elastic/known-fields/{pattern}', error.message || error);
|
2018-09-10 08:32:49 +00:00
|
|
|
return ErrorResponse(error.message || error, 4008, 500, reply);
|
2018-04-13 16:00:13 +00:00
|
|
|
}
|
2018-09-10 08:32:49 +00:00
|
|
|
}
|
2018-12-03 13:54:39 +00:00
|
|
|
|
|
|
|
/**
|
2018-12-11 16:12:59 +00:00
|
|
|
* This returns de the alerts of an angent
|
2018-12-03 15:07:59 +00:00
|
|
|
* @param {*} req
|
2018-12-03 13:54:39 +00:00
|
|
|
* POST /elastic/alerts
|
|
|
|
* {
|
|
|
|
* "agent.id": 100 ,
|
|
|
|
* "cluster.name": "wazuh",
|
|
|
|
* "date.from": "now-1d/timestamp/standard date", // Like Elasticsearch does
|
|
|
|
* "date.to": "now/timestamp/standard date", // Like Elasticsearch does
|
|
|
|
* "rule.group": ["onegroup", "anothergroup"] // Or empty array [ ]
|
|
|
|
* "size": 5 // Optional parameter
|
|
|
|
* }
|
2018-12-03 15:07:59 +00:00
|
|
|
*
|
|
|
|
* @param {*} reply
|
|
|
|
* {alerts: [...]} or ErrorResponse
|
2018-12-03 13:54:39 +00:00
|
|
|
*/
|
2018-12-03 15:07:59 +00:00
|
|
|
async alerts(req, reply) {
|
2018-12-03 13:54:39 +00:00
|
|
|
try {
|
2018-12-03 15:07:59 +00:00
|
|
|
const pattern = req.payload.pattern || 'wazuh-alerts-3.x-*';
|
|
|
|
const from = req.payload.from || 'now-1d';
|
|
|
|
const to = req.payload.to || 'now';
|
|
|
|
const size = req.payload.size || 10;
|
|
|
|
const payload = Base(pattern, [], from, to);
|
|
|
|
|
|
|
|
payload.query = { bool: { must: [] } };
|
|
|
|
|
|
|
|
const agent = req.payload['agent.id'];
|
|
|
|
const manager = req.payload['manager.name'];
|
|
|
|
const cluster = req.payload['cluster.name'];
|
2018-12-04 08:38:18 +00:00
|
|
|
const rulegGroups = req.payload['rule.groups'];
|
2018-12-03 15:07:59 +00:00
|
|
|
if (agent)
|
|
|
|
payload.query.bool.must.push({
|
|
|
|
match: { 'agent.id': agent }
|
|
|
|
});
|
|
|
|
if (cluster)
|
|
|
|
payload.query.bool.must.push({
|
|
|
|
match: { 'cluster.name': cluster }
|
|
|
|
});
|
|
|
|
if (manager)
|
|
|
|
payload.query.bool.must.push({
|
|
|
|
match: { 'manager.name': manager }
|
|
|
|
});
|
2018-12-04 08:38:18 +00:00
|
|
|
if (rulegGroups)
|
|
|
|
payload.query.bool.must.push({
|
|
|
|
match: { 'rule.groups': rulegGroups }
|
|
|
|
});
|
2018-12-03 15:07:59 +00:00
|
|
|
|
|
|
|
payload.size = size;
|
|
|
|
payload.docvalue_fields = [
|
|
|
|
'@timestamp',
|
|
|
|
'cluster.name',
|
|
|
|
'manager.name',
|
|
|
|
'agent.id',
|
|
|
|
'rule.id',
|
|
|
|
'rule.group',
|
|
|
|
'rule.description'
|
|
|
|
];
|
|
|
|
const data = await this.wzWrapper.searchWazuhAlertsWithPayload(payload);
|
|
|
|
return reply({ alerts: data.hits.hits });
|
2018-12-03 13:54:39 +00:00
|
|
|
} catch (error) {
|
|
|
|
log('POST /elastic/alerts', error.message || error);
|
|
|
|
return ErrorResponse(error.message || error, 4010, 500, reply);
|
|
|
|
}
|
|
|
|
}
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|