wazuh-kibana-app/server/api/wazuh-elastic.js

458 lines
15 KiB
JavaScript
Raw Normal View History

const importAppObjects = require('../initialize');
2017-10-27 08:10:17 +00:00
module.exports = (server, options) => {
// Elastic JS Client
const elasticRequest = server.plugins.elasticsearch.getCluster('data');
const getTimeStamp = async (req,reply) => {
try {
const data = await elasticRequest.callWithInternalUser('search', {
index: '.wazuh-version',
type : 'wazuh-version'
})
if(data.hits &&
data.hits.hits[0] &&
data.hits.hits[0]._source &&
data.hits.hits[0]._source.installationDate &&
data.hits.hits[0]._source.lastRestart){
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 (err) {
2018-03-22 12:35:58 +00:00
return reply({
statusCode: 500,
error : 99,
message : err.message || 'Could not fetch .wazuh-version index'
}).code(500);
}
}
2017-01-11 20:42:06 +00:00
//Handlers
2018-03-22 12:35:58 +00:00
const fetchElastic = async (req, payload) => {
try {
const data = await elasticRequest.callWithInternalUser('search', {
index: 'wazuh-alerts-3.x-*',
type: 'wazuh',
body: payload
});
return data;
} catch (error) {
return Promise.reject(error);
}
2016-11-09 19:11:15 +00:00
};
2018-03-22 12:35:58 +00:00
const getConfig = async (id, callback) => {
try {
const data = await elasticRequest.callWithInternalUser('get', {
index: '.wazuh',
type : 'wazuh-configuration',
id
});
2018-03-22 12:35:58 +00:00
return callback({
user : data._source.api_user,
password : Buffer.from(data._source.api_password, 'base64').toString("ascii"),
url : data._source.url,
port : data._source.api_port,
insecure : data._source.insecure,
cluster_info: data._source.cluster_info,
extensions : data._source.extensions
2017-10-20 18:53:56 +00:00
});
2018-03-22 12:35:58 +00:00
} catch (error){
return callback({ error: 'no elasticsearch', error_code: 2 });
}
2017-10-20 18:53:56 +00:00
};
// Updating Wazuh app visualizations and dashboards
2018-03-22 12:35:58 +00:00
const updateAppObjects = async (req, reply) => {
try {
await elasticRequest.callWithInternalUser('deleteByQuery', {
index: '.kibana',
body : {
query: {
bool: {
must: { match: { 'visualization.title': 'Wazuh App*' } },
must_not: { match: { 'visualization.title': 'Wazuh App Overview General Agents status' } }
2017-10-27 08:10:17 +00:00
}
}
2018-03-22 12:35:58 +00:00
}
})
// Update the pattern in the configuration
importAppObjects(req.params.pattern);
2018-03-22 12:35:58 +00:00
return reply({ statusCode: 200, data: 'Index pattern updated' });
} catch (error) {
return reply({
statusCode: 500,
error : 9,
message : `Could not delete visualizations due to ${error.message || error}`
2017-10-27 08:10:17 +00:00
}).code(500);
2018-03-22 12:35:58 +00:00
}
};
2018-03-22 12:35:58 +00:00
const getTemplate = async (req, reply) => {
try {
const data = await elasticRequest.callWithInternalUser('cat.templates', {})
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}`
});
} 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-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-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}`
}).code(500);
2018-03-22 12:35:58 +00:00
}
2018-01-10 17:00:06 +00:00
};
2018-03-22 12:35:58 +00:00
const checkPattern = async (req, reply) => {
try {
const response = await elasticRequest.callWithInternalUser('search', {
index: '.kibana',
body : { query: { bool: { must: { match: { type: 'index-pattern' } } } } }
})
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({ statusCode: 200, status: false, data: 'Index pattern not found' });
} catch (error) {
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}`
}).code(500);
2018-03-22 12:35:58 +00:00
}
};
2018-03-22 12:35:58 +00:00
const getFieldTop = async (req, reply) => {
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,
order: { _count: 'desc' }
2017-10-27 08:10:17 +00:00
}
}
}
2018-03-22 12:35:58 +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
// 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 } }
);
payload.aggs['2'].terms.field = req.params.field;
2018-03-22 12:35:58 +00:00
const data = await fetchElastic(req, 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
}
2016-11-09 19:11:15 +00:00
};
2017-10-27 08:19:16 +00:00
const getSetupInfo = (req, reply) => {
elasticRequest
2017-11-17 08:56:59 +00:00
.callWithInternalUser('search', {
2017-10-27 08:10:17 +00:00
index: '.wazuh-version',
type: 'wazuh-version'
2017-10-27 08:19:16 +00:00
})
2018-03-11 16:58:57 +00:00
.then(data => {
2017-10-27 08:19:16 +00:00
if (data.hits.total === 0) {
2017-10-27 08:10:17 +00:00
reply({
2017-10-27 08:19:16 +00:00
'statusCode': 200,
'data': ''
});
} else {
reply({
'statusCode': 200,
'data': data.hits.hits[0]._source
});
}
})
2018-03-11 16:58:57 +00:00
.catch(error => {
2017-10-27 08:19:16 +00:00
reply({
'statusCode': 500,
'error': 9,
'message': 'Could not get data from elasticsearch'
}).code(500);
});
};
2018-01-18 16:17:54 +00:00
const getCurrentlyAppliedPattern = (req, reply) => {
// We search for the currently applied pattern in the visualizations
elasticRequest .callWithInternalUser('search', {
index: '.kibana',
type: 'doc',
q: `visualization.title:"Wazuh App Overview General Metric alerts"`
})
.then(data => {
if(data && data.hits && data.hits.hits && data.hits.hits[0] && data.hits.hits[0]._source){
return reply({
statusCode: 200,
data: JSON.parse(data.hits.hits[0]._source.visualization.kibanaSavedObjectMeta.searchSourceJSON).index
});
} else {
throw Error('no_visualization');
}
2018-01-18 16:17:54 +00:00
})
.catch(error => {
if(error && error.message && error.message === 'no_visualization'){
return reply('kibana_index_pattern_error').code(500);
}
return reply('elasticsearch_down').code(500);
2018-01-18 16:17:54 +00:00
});
};
2017-10-20 18:53:56 +00:00
module.exports = getConfig;
const getAllowedIndices = async roles => {
try {
const xpacksec = await elasticRequest
.callWithInternalUser('search', {
index: '.security-6',
type: 'doc'
});
let allowedIndices = [];
for(const rol of roles){
const myIndices = xpacksec.hits.hits.filter(item => item._id === `role-${rol}`);
for(let set of myIndices){
if(set && set._source && set._source.indices){
for(let index of set._source.indices){
if(index.privileges.includes('read')){
for(let name of index.names){
allowedIndices.push(name);
}
}
}
}
}
}
return allowedIndices;
} catch (error) {
return Promise.reject(error);
}
}
const filterAllowedIndexPatternList = (allowedIndices,list) => {
let finalList = [];
for(let allowed of allowedIndices){
let sanitized = allowed[allowed.length-1] === '*' ? allowed.split('*')[0] : allowed;
for(let item of list){
if(item.title.includes(sanitized)){
finalList.push(item);
}
}
}
return finalList;
}
const getlist = async (req,res) => {
try {
const xpack = await elasticRequest.callWithInternalUser('cat.plugins', { });
const isXpackEnabled = typeof xpack === 'string' && xpack.includes('x-pack');
const isSuperUser = isXpackEnabled && req.auth.credentials.roles.includes('superuser');
const allowedIndices = isXpackEnabled && !isSuperUser ? await getAllowedIndices(req.auth.credentials.roles) : false;
2018-03-22 10:24:40 +00:00
const data = await elasticRequest
.callWithInternalUser('search', {
index: '.kibana',
type: 'doc',
body: {
"query":{
"match":{
"type": "index-pattern"
}
}
}
});
2018-03-19 08:50:26 +00:00
if(data && data.hits && data.hits.hits){
const minimum = ["@timestamp", "full_log", "manager.name", "agent.id"];
let list = [];
if(data.hits.hits.length === 0) throw new Error('There is no index pattern');
for(const index of data.hits.hits){
let valid, parsed;
try{
parsed = JSON.parse(index._source['index-pattern'].fields)
} catch (error){
continue;
}
valid = parsed.filter(item => minimum.includes(item.name));
2018-03-19 08:50:26 +00:00
if(valid.length === 4){
list.push({
id: index._id.split('index-pattern:')[1],
title: index._source['index-pattern'].title
})
}
}
return res({data: isXpackEnabled && !isSuperUser ? filterAllowedIndexPatternList(allowedIndices,list) : list});
}
2018-03-19 08:50:26 +00:00
throw new Error('The Elasticsearch request didn\'t fetch the expected data');
} catch(error){
2018-03-22 10:24:40 +00:00
return res({error: error.message || error}).code(500)
}
}
// Get index patterns list
server.route({
method: 'GET',
path: '/get-list',
handler: getlist
});
//Server routes
2016-11-09 19:11:15 +00:00
2018-01-18 16:17:54 +00:00
/*
* GET /api/wazuh-elastic/current-pattern
* Returns the currently applied pattern
*
**/
server.route({
method: 'GET',
path: '/api/wazuh-elastic/current-pattern',
handler: getCurrentlyAppliedPattern
});
2018-01-10 17:00:06 +00:00
/*
* GET /api/wazuh-elastic/template/{pattern}
* Returns whether a correct template is being applied for the index-pattern
*
**/
server.route({
method: 'GET',
path: '/api/wazuh-elastic/template/{pattern}',
handler: getTemplate
});
/*
* GET /api/wazuh-elastic/pattern/{pattern}
2018-01-18 16:17:54 +00:00
* Returns whether the pattern exists or not
*
**/
server.route({
method: 'GET',
path: '/api/wazuh-elastic/pattern/{pattern}',
handler: checkPattern
});
2018-01-18 16:17:54 +00:00
2016-11-09 19:11:15 +00:00
/*
2017-10-27 08:10:17 +00:00
* GET /api/wazuh-elastic/top/{cluster}/{field}/{time?}
* Returns the agent with most alerts
*
**/
2016-11-09 19:11:15 +00:00
server.route({
method: 'GET',
path: '/api/wazuh-elastic/top/{mode}/{cluster}/{field}',
2016-11-09 19:11:15 +00:00
handler: getFieldTop
});
2017-10-27 08:10:17 +00:00
/*
* GET /api/wazuh-elastic/setup
* Return Wazuh Appsetup info
2017-10-27 08:10:17 +00:00
*
**/
2017-01-16 18:27:53 +00:00
server.route({
method: 'GET',
path: '/api/wazuh-elastic/setup',
handler: getSetupInfo
});
2017-10-27 08:10:17 +00:00
/*
* POST /api/wazuh-elastic/updatePattern
* Update the index pattern in the app visualizations
2017-10-27 08:10:17 +00:00
*
**/
server.route({
method: 'GET',
path: '/api/wazuh-elastic/updatePattern/{pattern}',
handler: updateAppObjects
});
server.route({
method: 'GET',
path: '/api/wazuh-elastic/timestamp',
handler: getTimeStamp
});
2017-10-27 08:10:17 +00:00
};