module.exports = function (server, options) { // Require some libraries const pciRequirementsFile = ''; const fs = require('fs'); const path = require('path'); const needle = require('needle'); var fetchAgentsExternal = require(path.resolve(__dirname, "../wazuh-monitoring.js")); const wazuh_config_file = '../../configuration/config.json'; const wazuh_temp_file = '../../configuration/.patch_version'; var colors = require('ansicolors'); var blueWazuh = colors.blue('wazuh'); var wazuh_config = {}; var app_package = {}; var wazuh_api_version; // Read Wazuh App configuration file try { wazuh_config = JSON.parse(fs.readFileSync(path.resolve(__dirname, wazuh_config_file), 'utf8')); } catch (e) { server.log([blueWazuh, 'initialize', 'error'], 'Could not read the Wazuh configuration file.'); server.log([blueWazuh, 'initialize', 'error'], 'Path: ' + wazuh_config_file); server.log([blueWazuh, 'initialize', 'error'], 'Exception: ' + e); }; if(fs.existsSync(path.resolve(__dirname, wazuh_temp_file))){ wazuh_api_version = "v2.0.0"; } else{ wazuh_api_version = wazuh_config.wazuhapi.version; } // Elastic JS Client const serverConfig = server.config(); const elasticsearch = require('elasticsearch'); const elasticRequest = server.plugins.elasticsearch.getCluster('data'); var appInfo = {} elasticRequest.callWithInternalUser('search', { index: '.kibana', type: 'wazuh-setup'}).then( function (data) { appInfo["app-version"] = data.hits.hits[0]._source['app-version']; appInfo["installationDate"] = data.hits.hits[0]._source['installationDate']; appInfo["revision"] = data.hits.hits[0]._source['revision']; }, function (error) { server.log([blueWazuh, 'initialize', 'error'], 'Could not read the Wazuh App version.'); }); //Handlers - Generic var getConfig = function (callback) { elasticRequest.callWithInternalUser('search', { index: '.kibana', type: 'wazuh-configuration', q: 'active:true'}).then( function (data) { if (data.hits.total == 1) { callback({ 'user': data.hits.hits[0]._source.api_user, 'password': new Buffer(data.hits.hits[0]._source.api_password, 'base64').toString("ascii"), 'url': data.hits.hits[0]._source.url, 'port': data.hits.hits[0]._source.api_port, 'insecure': data.hits.hits[0]._source.insecure, 'manager': data.hits.hits[0]._source.manager, 'extensions': data.hits.hits[0]._source.extensions }); } else { callback({ 'error': 'no credentials', 'error_code': 1 }); } }, function (error) { callback({ 'error': 'no elasticsearch', 'error_code': 2 }); }); }; var getAPI_entries = function (req,reply) { elasticRequest.callWithRequest(req, 'search', { index: '.kibana', type: 'wazuh-configuration'}).then( function (data) { reply(data.hits.hits); }, function (data, error) { reply(data); }); }; var deleteAPI_entries = function (req,reply) { elasticRequest.callWithRequest(req, 'delete', { index: '.kibana', type: 'wazuh-configuration', id: req.params.id}).then( function (data) { reply(data); }, function (data, error) { reply(data); }); }; var setAPI_entry_default = function (req,reply) { // Searching for previous default elasticRequest.callWithRequest(req, 'search', { index: '.kibana', type: 'wazuh-configuration', q: 'active:true'}).then( function (data) { if (data.hits.total == 1) { // Setting off previous default var idPreviousActive = data.hits.hits[0]._id; elasticRequest.callWithRequest(req, 'update', { index: '.kibana', type: 'wazuh-configuration', id: idPreviousActive, body: {doc: {"active": "false"}} }).then( function () { // Set new default elasticRequest.callWithRequest(req, 'update', { index: '.kibana', type: 'wazuh-configuration', id: req.params.id, body: {doc: {"active": "true"}} }).then( function () { reply({ 'statusCode': 200, 'message': 'ok' }); }, function (error) { reply({ 'statusCode': 500, 'error': 8, 'message': 'Could not save data in elasticsearch' }).code(500); }); }, function (error) { reply({ 'statusCode': 500, 'error': 8, 'message': 'Could not save data in elasticsearch' }).code(500); }); }else{ // Set new default elasticRequest.callWithRequest(req, 'update', { index: '.kibana', type: 'wazuh-configuration', id: req.params.id, body: {doc: {"active": "true"}} }).then( function () { reply({ 'statusCode': 200, 'message': 'ok' }); }, function (error) { reply({ 'statusCode': 500, 'error': 8, 'message': 'Could not save data in elasticsearch' }).code(500); }); } }, function () { reply({ 'statusCode': 500, 'error': 8, 'message': 'Could not set API default entry' }).code(500); }); }; var getPciRequirement = function (req,reply) { const pciRequirementsFile = '../startup/integration_files/pci_requirements.json'; var pciRequirements = {}; try { pciRequirements = JSON.parse(fs.readFileSync(path.resolve(__dirname, pciRequirementsFile), 'utf8')); } catch (e) { server.log([blueWazuh, 'initialize', 'error'], 'Could not read the mapping file.'); server.log([blueWazuh, 'initialize', 'error'], 'Path: ' + pciRequirementsFile); server.log([blueWazuh, 'initialize', 'error'], 'Exception: ' + e); }; var pci_description = ""; if(req.params.requirement == "all"){ reply(pciRequirements); return; } if(pciRequirements[req.params.requirement]) pci_description = pciRequirements[req.params.requirement]; reply({pci: {requirement: req.params.requirement, description: pci_description}}); }; var getExtensions = function (req,reply) { elasticRequest.callWithRequest(req, 'search', { index: '.kibana', type: 'wazuh-configuration'}).then( function (data) { reply(data.hits.hits); }, function (data, error) { reply(data); }); }; var toggleExtension = function (req,reply) { // Toggle extenion state var extension = {}; extension[req.params.extensionName] = (req.params.extensionValue == "true") ? true : false; elasticRequest.callWithRequest(req, 'update',{ index: '.kibana', type: 'wazuh-configuration', id: req.params.id, body: {doc: {"extensions" : extension}} }).then( function () { reply({ 'statusCode': 200, 'message': 'ok' }); }, function (error) { reply({ 'statusCode': 500, 'error': 8, 'message': 'Could not save data in elasticsearch' }).code(500); }); }; //Handlers - Test API var testApiAux2 = function (error, response, wapi_config) { if (!error && response && response.body.data) { return { 'statusCode': 200, 'data': 'ok', 'manager' : wapi_config.manager, 'extensions' : wapi_config.extensions }; } else if (response && response.statusCode == 401) { return { 'statusCode': 200, 'error': '1', 'data': 'unauthorized' }; } else if (!error && response && (!response.body.data) ) { return { 'statusCode': 200, 'error': '1', 'data': 'bad_url' }; } else { if (!wapi_config.insecure) { return { 'statusCode': 200, 'error': '1', 'data': 'self_signed' }; } else { return { 'statusCode': 200, 'error': '1', 'data': 'not_running' }; } } }; var testApiAux1 = function (error, response, wapi_config, needle, callback) { if (!error && response && response.body.data) { callback({ 'statusCode': 200, 'data': 'ok', 'manager' : wapi_config.manager, 'extensions' : wapi_config.extensions}); } else if (response && response.statusCode == 401) { callback({ 'statusCode': 200, 'error': '1', 'data': 'unauthorized' }); } else if (!error && response && (!response.body.data) ) { callback({ 'statusCode': 200, 'error': '1', 'data': 'bad_url' }); } else { needle.request('get', wapi_config.url+":"+wapi_config.port+'/version', {}, { username: wapi_config.user, password: wapi_config.password, rejectUnauthorized: !wapi_config.insecure }, function (error, response) { callback(testApiAux2(error, response, wapi_config)); }); } }; var checkStoredAPI = function (req, reply) { var needle = require('needle'); needle.defaults({ open_timeout: wazuh_config.wazuhapi.requests.timeout }); //Get config from elasticsearch getConfig(function (wapi_config) { if (wapi_config.error_code > 1) { //Can not connect to elasticsearch reply({ 'statusCode': 200, 'error': '1', 'data': 'no_elasticsearch' }); return; } else if (wapi_config.error_code > 0) { //Credentials not found reply({ 'statusCode': 200, 'error': '2', 'data': 'no_credentials' }); return; } if ((wapi_config.url.indexOf('https://') == -1) && (wapi_config.url.indexOf('http://') == -1)) { reply({ 'statusCode': 200, 'error': '1', 'data': 'protocol_error' }); } else { needle.request('get', wapi_config.url+":"+wapi_config.port+'/version', {}, { username: wapi_config.user, password: wapi_config.password }, function (error, response) { testApiAux1(error, response, wapi_config, needle, function (test_result) { reply(test_result); }); }); } }); }; var checkAPI = function (req, reply) { var needle = require('needle'); needle.defaults({ open_timeout: 1500 }); if (!req.payload.user) { reply({ 'statusCode': 400, 'error': 3, 'message': 'Missing param: API USER' }).code(400); } else if (!req.payload.password) { reply({ 'statusCode': 400, 'error': 4, 'message': 'Missing param: API PASSWORD' }).code(400); } else if (!req.payload.url) { reply({ 'statusCode': 400, 'error': 4, 'message': 'Missing param: API URL' }).code(400); } else if (!req.payload.port) { reply({ 'statusCode': 400, 'error': 4, 'message': 'Missing param: API PORT' }).code(400); } else { req.payload.password = new Buffer(req.payload.password, 'base64').toString("ascii"); if ((req.payload.url.indexOf('https://') == -1) && (req.payload.url.indexOf('http://') == -1)) { reply({ 'statusCode': 200, 'error': '1', 'data': 'protocol_error' }); } else { needle.request('get', req.payload.url+":"+req.payload.port+'/version', {}, { username: req.payload.user, password: req.payload.password, rejectUnauthorized: !req.payload.insecure }, function (error, response) { testApiAux1(error, response, req.payload, needle, function (test_result) { if(test_result.data == "ok"){ needle.request('get', req.payload.url+":"+req.payload.port+'/agents/000', {}, { username: req.payload.user, password: req.payload.password, rejectUnauthorized: !req.payload.insecure }, function (error, response) { if(!error && !response.body.error) reply(response.body.data.name); else if(response.body.error) reply({ 'statusCode': 500, 'error': 5, 'message': response.body.message }).code(500); else reply({ 'statusCode': 500, 'error': 5, 'message': 'Error occurred' }).code(500); }); }else{ reply(test_result); } }); }); } } }; //Handlers - Route request var errorControl = function (error, response) { if (error) { return ({ 'isError': true, 'body': { 'statusCode': 500, 'error': 5, 'message': 'Request error', 'errorMessage': error.message } }); } else if (!error && response.body.error) { return ({ 'isError': true, 'body': { 'statusCode': 500, 'error': 6, 'message': 'Wazuh api error', 'errorData': response.body } }); } return ({ 'isError': false }); }; var makeRequest = function (method, path, data, reply) { getConfig(function (wapi_config) { if (wapi_config.error_code > 1) { //Can not connect to elasticsearch reply({ 'statusCode': 404, 'error': 2, 'message': 'Could not connect with elasticsearch' }).code(404); return; } else if (wapi_config.error_code > 0) { //Credentials not found reply({ 'statusCode': 404, 'error': 1, 'message': 'Credentials does not exists' }).code(404); return; } var needle = require('needle'); needle.defaults({ open_timeout: wazuh_config.wazuhapi.requests.timeout }); if (!data) { data = {}; } var options = { headers: { 'api-version': wazuh_api_version, 'wazuh-app-version': appInfo['app-version'] }, username: wapi_config.user, password: wapi_config.password, rejectUnauthorized: !wapi_config.insecure }; var fullUrl = wapi_config.url + ":" + wapi_config.port + path; needle.request(method, fullUrl, data, options, function (error, response) { var errorData = errorControl(error, response); if (errorData.isError) { reply(errorData.body).code(500); } else { reply(response.body); } }); }); }; var requestApi = function (req, reply) { if (!req.payload.method) { reply({ 'statusCode': 400, 'error': 3, 'message': 'Missing param: Method' }).code(400); } else if (!req.payload.path) { reply({ 'statusCode': 400, 'error': 4, 'message': 'Missing param: Path' }).code(400); } else { makeRequest(req.payload.method, req.payload.path, req.payload.body, reply); } }; //Handlers - Save config var saveApi = function (req, reply) { if (!(req.payload.user && req.payload.password && req.payload.url && req.payload.port)) { reply({ 'statusCode': 400, 'error': 7, 'message': 'Missing data' }).code(400); return; } var options = { headers: { 'api-version': wazuh_api_version, 'wazuh-app-version': appInfo['app-version'] }, username: req.payload.user, password: req.payload.password, rejectUnauthorized: !req.payload.insecure }; needle.request('get', req.payload.url + ':' + req.payload.port +'/version', {}, options, function (error, response) { if (error || response.error || !response.body.data) { options = { headers: { 'api-version': 'v2.0.0', 'wazuh-app-version': appInfo['app-version'] }, username: req.payload.user, password: req.payload.password, rejectUnauthorized: !req.payload.insecure } needle.request('get', req.payload.url + ':' + req.payload.port +'/version', {}, options, function (error, response) { if (!error && !response.error && response.body.data) { fs.writeFile(path.resolve(__dirname, wazuh_temp_file), "#Temporal file to avoid inconsistences when using App 2.0.1 with API 2.0.0",function(err){ if(err) server.log([blueWazuh, 'initialize', 'error'], err); else{ server.log([blueWazuh, 'initialize', 'info'], 'Temporal file created to use the API 2.0.0'); } }); } else { server.log([blueWazuh, 'initialize', 'error'], 'Wazuh API credentials not found or are not correct. Open the app in your browser and configure it for start monitoring agents.'); } }); } }); var settings = { 'api_user': req.payload.user, 'api_password': req.payload.password, 'url': req.payload.url, 'api_port': req.payload.port , 'insecure': req.payload.insecure, 'component' : 'API', 'active' : req.payload.active, 'manager' : req.payload.manager, 'extensions' : req.payload.extensions}; elasticRequest.callWithRequest(req, 'index', { index: '.kibana', type: 'wazuh-configuration', body: settings, refresh: true }) .then(function (response) { reply({ 'statusCode': 200, 'message': 'ok', 'response' : response }); }, function (error) { reply({ 'statusCode': 500, 'error': 8, 'message': 'Could not save data in elasticsearch' }).code(500); }); }; //Handlers - Get API Settings var getApiSettings = function (req, reply) { getConfig(function (wapi_config) { if (wapi_config.error_code > 1) { //Can not connect to elasticsearch reply({ 'statusCode': 200, 'error': '1', 'data': 'no_elasticsearch' }); return; } else if (wapi_config.error_code > 0) { //Credentials not found reply({ 'statusCode': 200, 'error': '1', 'data': 'no_credentials' }); return; } }); }; // Fetch agent status and insert it directly on demand var fetchAgents = function (req, reply){ fetchAgentsExternal(); reply({ 'statusCode': 200, 'error': '0', 'data': '' }); } //Handlers - error loggin var postErrorLog = function (req, reply) { if (!req.payload.message) { server.log([blueWazuh, 'server', 'error'], 'Error logging failed:'); server.log([blueWazuh, 'server', 'error'], 'You must provide at least one error message to log'); reply({ 'statusCode': 500, 'message': 'You must provide at least one error message to log' }); } else { server.log([blueWazuh, 'client', 'error'], req.payload.message); if (req.payload.details) { server.log([blueWazuh, 'client', 'error'], req.payload.details); } reply({ 'statusCode': 200, 'message': 'Error logged succesfully' }); } }; //Server routes /* * GET /api/wazuh-api/test * Returns if the wazuh-api configuration is working * **/ server.route({ method: 'GET', path: '/api/wazuh-api/check', handler: checkStoredAPI }); /* * POST /api/wazuh-api/test * Check if credentials on POST connect to Wazuh API. Not storing them! * Returns if the wazuh-api configuration received in the POST body will work * **/ server.route({ method: 'POST', path: '/api/wazuh-api/check', handler: checkAPI }); /* * POST /api/wazuh-api/request * Returns the request result (With error control) * **/ server.route({ method: 'POST', path: '/api/wazuh-api/request', handler: requestApi }); /* * PUT /api/wazuh-api/settings * Save the given settings into elasticsearch * **/ server.route({ method: 'PUT', path: '/api/wazuh-api/settings', handler: saveApi }); /* * GET /api/wazuh-api/settings * Get Wazuh-API settings from elasticsearch index * **/ server.route({ method: 'GET', path: '/api/wazuh-api/settings', handler: getApiSettings }); /* * GET /api/wazuh-api/apiEntries * Get Wazuh-API entries list (Multimanager) from elasticsearch index * **/ server.route({ method: 'GET', path: '/api/wazuh-api/apiEntries', handler: getAPI_entries }); /* * DELETE /api/wazuh-api/settings * Delete Wazuh-API entry (multimanager) from elasticsearch index * **/ server.route({ method: 'DELETE', path: '/api/wazuh-api/apiEntries/{id}', handler: deleteAPI_entries }); /* * PUT /api/wazuh-api/settings * Set Wazuh-API as default (multimanager) on elasticsearch index * **/ server.route({ method: 'PUT', path: '/api/wazuh-api/apiEntries/{id}', handler: setAPI_entry_default }); /* * PUT /api/wazuh-api/extension/toggle/documentId/extensionName/trueorfalse * Toggle extension state: Enable / Disable * **/ server.route({ method: 'PUT', path: '/api/wazuh-api/extension/toggle/{id}/{extensionName}/{extensionValue}', handler: toggleExtension }); /* * GET /api/wazuh-api/extension * Return extension state list * **/ server.route({ method: 'GET', path: '/api/wazuh-api/extension', handler: getExtensions }); /* * GET /api/wazuh-api/pci/requirement * Return a PCI requirement description * **/ server.route({ method: 'GET', path: '/api/wazuh-api/pci/{requirement}', handler: getPciRequirement }); /* * POST /api/wazuh/debug * Write in debug log * **/ server.route({ method: 'POST', path: '/api/wazuh/errlog', handler: postErrorLog }); /* * GET /api/wazuh-api/pci/requirement * Return a PCI requirement description * **/ server.route({ method: 'GET', path: '/api/wazuh-api/fetchAgents', handler: fetchAgents }); };