2018-04-22 19:10:29 +00:00
|
|
|
/*
|
|
|
|
* Wazuh app - Class for Wazuh-Elastic 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.
|
|
|
|
*/
|
2018-04-22 13:41:10 +00:00
|
|
|
import ElasticWrapper from '../lib/elastic-wrapper';
|
2018-04-21 11:31:47 +00:00
|
|
|
import fs from 'fs';
|
|
|
|
import yml from 'js-yaml';
|
|
|
|
import path from 'path';
|
2018-04-11 11:31:46 +00:00
|
|
|
|
2018-04-23 10:31:28 +00:00
|
|
|
import { AgentsVisualizations, OverviewVisualizations, RulesetVisualizations } from '../integration-files/visualizations/index'
|
|
|
|
|
2018-04-22 13:41:10 +00:00
|
|
|
export default class WazuhElastic {
|
2018-04-11 07:58:23 +00:00
|
|
|
constructor(server){
|
|
|
|
this.wzWrapper = new ElasticWrapper(server);
|
|
|
|
}
|
2017-10-27 08:10:17 +00:00
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
async getTimeStamp(req,reply) {
|
2018-03-11 11:16:29 +00:00
|
|
|
try {
|
2018-03-12 15:34:58 +00:00
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
const data = await this.wzWrapper.getWazuhVersionIndexAsSearch();
|
2018-03-12 15:34:58 +00:00
|
|
|
|
2018-04-22 19:10:29 +00:00
|
|
|
if(data.hits &&
|
|
|
|
data.hits.hits[0] &&
|
|
|
|
data.hits.hits[0]._source &&
|
|
|
|
data.hits.hits[0]._source.installationDate &&
|
2018-03-12 15:34:58 +00:00
|
|
|
data.hits.hits[0]._source.lastRestart){
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-03-12 15:34:58 +00:00
|
|
|
return reply({
|
|
|
|
installationDate: data.hits.hits[0]._source.installationDate,
|
|
|
|
lastRestart : data.hits.hits[0]._source.lastRestart
|
|
|
|
});
|
|
|
|
|
2018-03-11 11:16:29 +00:00
|
|
|
} else {
|
|
|
|
throw new Error('Could not fetch .wazuh-version index');
|
|
|
|
}
|
2018-03-12 15:34:58 +00:00
|
|
|
|
2018-03-11 11:16:29 +00:00
|
|
|
} catch (err) {
|
2018-03-22 12:35:58 +00:00
|
|
|
return reply({
|
|
|
|
statusCode: 500,
|
|
|
|
error : 99,
|
|
|
|
message : err.message || 'Could not fetch .wazuh-version index'
|
2018-03-11 11:16:29 +00:00
|
|
|
}).code(500);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
async getTemplate(req, reply) {
|
2018-03-22 12:35:58 +00:00
|
|
|
try {
|
2018-04-11 07:58:23 +00:00
|
|
|
const data = await this.wzWrapper.getTemplates();
|
2018-03-22 12:35:58 +00:00
|
|
|
|
2018-01-12 15:00:50 +00:00
|
|
|
if (req.params.pattern == "wazuh-alerts-3.x-*" && data.includes("wazuh-alerts-3.*")) {
|
2018-03-22 12:35:58 +00:00
|
|
|
return reply({
|
|
|
|
statusCode: 200,
|
|
|
|
status : true,
|
|
|
|
data : `Template found for ${req.params.pattern}`
|
2018-04-22 19:10:29 +00:00
|
|
|
});
|
2018-01-11 18:41:50 +00:00
|
|
|
} else {
|
2018-03-22 12:35:58 +00:00
|
|
|
|
|
|
|
const lastChar = req.params.pattern[req.params.pattern.length -1];
|
|
|
|
const array = data.match(/[^\s]+/g);
|
2018-01-18 11:44:58 +00:00
|
|
|
|
|
|
|
let pattern = req.params.pattern;
|
|
|
|
if (lastChar === '*') { // Remove last character if it is a '*'
|
|
|
|
pattern = pattern.slice(0, -1);
|
|
|
|
}
|
|
|
|
|
2018-01-14 13:05:54 +00:00
|
|
|
for (let i = 1; i < array.length; i++) {
|
2018-01-18 11:44:58 +00:00
|
|
|
if (array[i].includes(pattern) && array[i-1] == `wazuh`) {
|
2018-03-22 12:35:58 +00:00
|
|
|
return reply({
|
|
|
|
statusCode: 200,
|
|
|
|
status : true,
|
|
|
|
data : `Template found for ${req.params.pattern}`
|
2018-04-22 19:10:29 +00:00
|
|
|
});
|
2018-01-14 13:05:54 +00:00
|
|
|
}
|
|
|
|
}
|
2018-03-22 12:35:58 +00:00
|
|
|
|
|
|
|
return reply({
|
|
|
|
statusCode: 200,
|
|
|
|
status : false,
|
|
|
|
data : `No template found for ${req.params.pattern}`
|
2018-04-22 19:10:29 +00:00
|
|
|
});
|
2018-01-10 17:00:06 +00:00
|
|
|
}
|
2018-03-22 12:35:58 +00:00
|
|
|
|
|
|
|
} catch (error){
|
|
|
|
return reply({
|
|
|
|
statusCode: 500,
|
|
|
|
error : 10000,
|
|
|
|
message : `Could not retrieve templates from Elasticsearch due to ${error.message || error}`
|
2018-01-11 18:41:50 +00:00
|
|
|
}).code(500);
|
2018-03-22 12:35:58 +00:00
|
|
|
}
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2018-01-10 17:00:06 +00:00
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
async checkPattern (req, reply) {
|
2018-03-22 12:35:58 +00:00
|
|
|
try {
|
2018-04-11 07:58:23 +00:00
|
|
|
const response = await this.wzWrapper.getAllIndexPatterns();
|
2018-03-22 12:35:58 +00:00
|
|
|
|
|
|
|
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' }) :
|
2018-03-24 11:04:51 +00:00
|
|
|
reply({ statusCode: 500, status: false, error:10020, message: 'Index pattern not found' });
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-03-22 12:35:58 +00:00
|
|
|
} catch (error) {
|
2018-01-11 18:41:50 +00:00
|
|
|
return reply({
|
2018-03-22 12:35:58 +00:00
|
|
|
statusCode: 500,
|
|
|
|
error : 10000,
|
|
|
|
message : `Something went wrong retrieving index-patterns from Elasticsearch due to ${error.message || error}`
|
2018-01-11 18:41:50 +00:00
|
|
|
}).code(500);
|
2018-03-22 12:35:58 +00:00
|
|
|
}
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2018-01-11 18:41:50 +00:00
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
|
|
|
|
async getFieldTop (req, reply) {
|
2018-03-22 12:35:58 +00:00
|
|
|
try{
|
|
|
|
// Top field payload
|
|
|
|
let payload = {
|
|
|
|
size: 1,
|
|
|
|
query: {
|
|
|
|
bool: {
|
|
|
|
must : [],
|
|
|
|
filter: { range: { '@timestamp': {} } }
|
2017-10-27 08:10:17 +00:00
|
|
|
}
|
2018-03-22 12:35:58 +00:00
|
|
|
},
|
|
|
|
aggs: {
|
|
|
|
'2': {
|
|
|
|
terms: {
|
|
|
|
field: '',
|
|
|
|
size : 1,
|
2018-04-22 19:10:29 +00:00
|
|
|
order: { _count: 'desc' }
|
2017-10-27 08:10:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-03-22 12:35:58 +00:00
|
|
|
};
|
2017-08-13 19:54:56 +00:00
|
|
|
|
2018-03-22 12:35:58 +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;
|
2017-10-27 08:10:17 +00:00
|
|
|
|
2017-12-03 14:30:47 +00:00
|
|
|
// Set up match for default cluster name
|
2018-03-22 12:35:58 +00:00
|
|
|
payload.query.bool.must.push(
|
|
|
|
req.params.mode === 'cluster' ?
|
|
|
|
{ match: { 'cluster.name': req.params.cluster } } :
|
|
|
|
{ match: { 'manager.name': req.params.cluster } }
|
|
|
|
);
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-03-22 12:35:58 +00:00
|
|
|
payload.aggs['2'].terms.field = req.params.field;
|
2017-12-03 14:30:47 +00:00
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
const data = await this.wzWrapper.searchWazuhAlertsWithPayload(payload);
|
2016-11-09 19:11:15 +00:00
|
|
|
|
2018-03-22 12:35:58 +00:00
|
|
|
return (data.hits.total === 0 || typeof data.aggregations['2'].buckets[0] === 'undefined') ?
|
|
|
|
reply({ statusCode: 200, data: '' }) :
|
|
|
|
reply({ statusCode: 200, data: data.aggregations['2'].buckets[0].key });
|
2016-11-09 19:11:15 +00:00
|
|
|
|
2018-03-22 12:35:58 +00:00
|
|
|
} catch (error) {
|
|
|
|
return reply({
|
|
|
|
statusCode: 500,
|
|
|
|
error : 9,
|
|
|
|
message : error.message || error
|
2017-10-27 08:10:17 +00:00
|
|
|
}).code(500);
|
2018-03-22 12:35:58 +00:00
|
|
|
}
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2016-11-09 19:11:15 +00:00
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
async getSetupInfo (req, reply) {
|
2018-03-22 14:12:40 +00:00
|
|
|
try {
|
2018-04-11 07:58:23 +00:00
|
|
|
const data = await this.wzWrapper.getWazuhVersionIndexAsSearch();
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-03-22 14:12:40 +00:00
|
|
|
return data.hits.total === 0 ?
|
|
|
|
reply({ statusCode: 200, data: '' }) :
|
|
|
|
reply({ statusCode: 200, data: data.hits.hits[0]._source });
|
2018-04-22 19:10:29 +00:00
|
|
|
|
|
|
|
|
2018-03-22 14:12:40 +00:00
|
|
|
} catch (error) {
|
|
|
|
return reply({
|
|
|
|
statusCode: 500,
|
|
|
|
error : 9,
|
|
|
|
message : `Could not get data from elasticsearch due to ${error.message || error}`
|
2017-10-27 08:19:16 +00:00
|
|
|
}).code(500);
|
2018-03-22 14:12:40 +00:00
|
|
|
}
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2017-08-13 19:54:56 +00:00
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
async filterAllowedIndexPatternList (list,req) {
|
2018-03-26 08:09:48 +00:00
|
|
|
let finalList = [];
|
|
|
|
for(let item of list){
|
|
|
|
try {
|
2018-04-11 07:58:23 +00:00
|
|
|
const allow = await this.wzWrapper.searchWazuhElementsByIndexWithRequest(req, item.title);
|
2018-03-26 08:56:50 +00:00
|
|
|
if(allow && allow.hits && allow.hits.total >= 1) finalList.push(item);
|
2018-03-26 08:09:48 +00:00
|
|
|
} catch (error){
|
|
|
|
console.log(`Some user trys to fetch the index pattern ${item.title} without permissions`)
|
2018-03-21 14:48:05 +00:00
|
|
|
}
|
2018-03-22 14:12:40 +00:00
|
|
|
|
2018-03-21 14:48:05 +00:00
|
|
|
}
|
|
|
|
return finalList;
|
|
|
|
}
|
2018-03-17 11:33:07 +00:00
|
|
|
|
2018-04-11 11:31:46 +00:00
|
|
|
validateIndexPattern(indexPatternList){
|
|
|
|
const minimum = ["@timestamp", "full_log", "manager.name", "agent.id"];
|
|
|
|
let list = [];
|
2018-04-22 19:10:29 +00:00
|
|
|
for(const index of indexPatternList){
|
|
|
|
let valid, parsed;
|
2018-04-11 11:31:46 +00:00
|
|
|
try{
|
|
|
|
parsed = JSON.parse(index._source['index-pattern'].fields)
|
|
|
|
} catch (error){
|
|
|
|
continue;
|
2018-04-22 19:10:29 +00:00
|
|
|
}
|
|
|
|
|
2018-04-11 11:31:46 +00:00
|
|
|
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
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
async getlist (req,res) {
|
2018-03-17 11:33:07 +00:00
|
|
|
try {
|
2018-04-11 07:58:23 +00:00
|
|
|
const xpack = await this.wzWrapper.getPlugins();
|
2018-03-21 14:48:05 +00:00
|
|
|
const isXpackEnabled = typeof xpack === 'string' && xpack.includes('x-pack');
|
|
|
|
const isSuperUser = isXpackEnabled && req.auth.credentials.roles.includes('superuser');
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
const data = await this.wzWrapper.getAllIndexPatterns();
|
2018-04-10 13:11:06 +00:00
|
|
|
|
|
|
|
if(data && data.hits && data.hits.hits.length === 0) throw new Error('There is no index pattern');
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-03-19 08:50:26 +00:00
|
|
|
if(data && data.hits && data.hits.hits){
|
2018-04-11 11:31:46 +00:00
|
|
|
const list = this.validateIndexPattern(data.hits.hits);
|
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
return res({data: isXpackEnabled && !isSuperUser ? await this.filterAllowedIndexPatternList(list,req) : list});
|
2018-03-17 11:33:07 +00:00
|
|
|
}
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-03-19 08:50:26 +00:00
|
|
|
throw new Error('The Elasticsearch request didn\'t fetch the expected data');
|
2018-03-17 11:33:07 +00:00
|
|
|
|
|
|
|
} catch(error){
|
2018-03-22 10:24:40 +00:00
|
|
|
return res({error: error.message || error}).code(500)
|
2018-03-17 11:33:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
async deleteVis (req, res) {
|
2018-04-04 15:58:27 +00:00
|
|
|
try {
|
2018-04-13 15:26:41 +00:00
|
|
|
await this.wzWrapper.refreshIndexByName(this.wzWrapper.WZ_KIBANA_INDEX);
|
2018-04-04 15:58:27 +00:00
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
const tmp = await this.wzWrapper.deleteVisualizationByDescription(req.params.timestamp);
|
2018-04-10 14:38:47 +00:00
|
|
|
|
2018-04-08 17:55:29 +00:00
|
|
|
return res({acknowledge: true , output: tmp});
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-04-04 15:58:27 +00:00
|
|
|
} catch(error){
|
|
|
|
return res({error:error.message || error}).code(500);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-04-08 17:55:29 +00:00
|
|
|
* Replaces visualizations main fields to fit a certain pattern.
|
|
|
|
* @param {*} app_objects Object containing raw visualizations.
|
|
|
|
* @param {*} id Index-pattern id to use in the visualizations. Eg: 'wazuh-alerts'
|
2018-04-04 15:58:27 +00:00
|
|
|
*/
|
2018-04-23 11:07:02 +00:00
|
|
|
buildVisualizationsRaw (app_objects, id) {
|
2018-04-18 11:38:08 +00:00
|
|
|
try{
|
2018-04-23 10:31:28 +00:00
|
|
|
const visArray = [];
|
|
|
|
let aux_source, bulk_content;
|
2018-04-18 11:38:08 +00:00
|
|
|
for (let element of app_objects) {
|
|
|
|
// Stringify and replace index-pattern for visualizations
|
2018-04-23 10:31:28 +00:00
|
|
|
aux_source = JSON.stringify(element._source);
|
2018-04-18 11:38:08 +00:00
|
|
|
aux_source = aux_source.replace("wazuh-alerts", id);
|
|
|
|
aux_source = JSON.parse(aux_source);
|
|
|
|
|
|
|
|
// Bulk source
|
|
|
|
bulk_content = {};
|
|
|
|
bulk_content[element._type] = aux_source;
|
|
|
|
|
|
|
|
visArray.push({
|
|
|
|
attributes: bulk_content.visualization,
|
2018-04-23 11:07:02 +00:00
|
|
|
type : element._type,
|
|
|
|
id : element._id,
|
|
|
|
_version : bulk_content.visualization.version
|
2018-04-18 11:38:08 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
return visArray;
|
|
|
|
} catch (error) {
|
2018-04-23 11:07:02 +00:00
|
|
|
return Promise.reject(error)
|
2018-04-18 11:38:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
async createVis (req, res) {
|
2018-04-04 15:58:27 +00:00
|
|
|
try {
|
2018-04-22 19:10:29 +00:00
|
|
|
if(!req.params.pattern ||
|
|
|
|
!req.params.tab ||
|
2018-04-23 10:31:28 +00:00
|
|
|
(req.params.tab && !req.params.tab.includes('manager-') && !req.params.tab.includes('overview-') && !req.params.tab.includes('agents-'))
|
2018-04-04 15:58:27 +00:00
|
|
|
) {
|
|
|
|
throw new Error('Missing parameters');
|
|
|
|
}
|
2018-04-17 10:39:37 +00:00
|
|
|
|
|
|
|
const apiConfig = (req.headers && req.headers.id) ? await this.wzWrapper.getWazuhConfigurationById(req.headers.id) : false;
|
|
|
|
const clusterName = apiConfig && apiConfig.cluster_info ? apiConfig.cluster_info.cluster : false;
|
2018-04-22 19:10:29 +00:00
|
|
|
const tabPrefix = req.params.tab.includes('overview') ?
|
|
|
|
'overview' : req.params.tab.includes('manager') ?
|
|
|
|
'manager' :
|
2018-04-09 17:11:22 +00:00
|
|
|
'agents';
|
|
|
|
|
2018-04-23 10:31:28 +00:00
|
|
|
const tabSplit = req.params.tab.split('-');
|
|
|
|
const tabSufix = tabPrefix === 'manager' ? tabSplit[2] : tabSplit[1];
|
|
|
|
|
2018-04-22 19:10:29 +00:00
|
|
|
const file = tabPrefix === 'manager' ?
|
2018-04-23 10:31:28 +00:00
|
|
|
RulesetVisualizations[tabSufix] :
|
|
|
|
tabPrefix === 'overview' ?
|
|
|
|
OverviewVisualizations[tabSufix] :
|
|
|
|
AgentsVisualizations[tabSufix];
|
|
|
|
|
2018-04-23 11:07:02 +00:00
|
|
|
const raw = await this.buildVisualizationsRaw(file, req.params.pattern);
|
2018-04-18 11:38:08 +00:00
|
|
|
return res({acknowledge: true, raw: raw });
|
2018-04-04 15:58:27 +00:00
|
|
|
|
|
|
|
} catch(error){
|
|
|
|
return res({error:error.message || error}).code(500);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-13 16:00:13 +00:00
|
|
|
async refreshIndex (req,res) {
|
|
|
|
try {
|
|
|
|
if(!req.params.pattern) throw new Error('Missing parameters');
|
|
|
|
|
|
|
|
const output = await this.wzWrapper.updateIndexPatternKnownFields(req.params.pattern);
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-04-13 16:00:13 +00:00
|
|
|
return res({acknowledge: true, output: output });
|
|
|
|
|
|
|
|
} catch(error){
|
|
|
|
return res({error:error.message || error}).code(500);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|