all files / src/ pe-app.js

73.81% Statements 62/84
50% Branches 14/28
66.67% Functions 4/6
73.81% Lines 62/84
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179                                                                                                                                                                                                                    152×                      
'use strict';
 
var express = require('express'),
   http = require('http'),
   bodyParser = require('body-parser'),
   config = require(__dirname + '/config'),
   logger = require(__dirname + '/loggers/logger'),
   peController = require(__dirname + '/controllers/pe-controller'),
   classification = require(__dirname + '/controllers/classification').init(),
   _ = require('lodash');
 
const nonWorkerApp = require(__dirname + '/non-worker-app');
 
var app = express();
 
app.set('port', config.networking.pnr_enforcement_port);
app.set('host', config.networking.pnr_enforcement_host);
app.use(bodyParser.json());
app.disable('x-powered-by');
app.disable('etag');
 
// api routes
app.post('/api/v1/pe/request', peController.requestHandler);
app.post('/api/v1/pe/response', peController.responseHandler);
app.post('/api/v1/pe/avcheck', peController.avCheckHandler);
app.post('/api/v1/pe/avlog', peController.avLogHandler);
app.post('/api/v1/pe/ssl_bump_bypass_check',
   peController.sslBumpBypassCheckHandler);
app.post('/api/v1/pe/bounce_all_check', peController.bounceAllCheckHandler);
 
Iif (classification.error) {
   logger.error('pnr-enforcement could not set up URL classifiers',
                classification.error);
} else {
   app.post('/api/v1/pe/pdp', peController.pdpHandler);
}
 
app.post('/api/pe/v1/risk_score', peController.riskScoreHandler);
 
//'is_bypass_allowed' api is deprecated. Use 'user_policy' route instead
app.post('/api/v1/pe/is_bypass_allowed', peController.isBypassAllowed);
 
app.post('/api/v1/pe/get_bypass_token', peController.getBypassToken);
// DEPRECATED. use /api/pe/v1/user_policy, which follows the correct long-term
// naming convention. Other than that, the API is fully backwards-compatible
app.post('/api/v1/pe/user_policy', peController.getUserPolicy);
app.post('/api/pe/v1/user_policy', peController.getUserPolicy);
 
app.post('/api/pe/v1/policy_lookup', peController.getPolicyKeys);
 
app.post('/api/v1/pe/flash_site_list', peController.getFlashSiteListV1);
app.post('/api/v2/pe/flash_site_list', peController.getFlashSiteListV2);
 
//returns site_flags used by surrogate to make per site customization.
app.get('/api/v1/pe/site_flags', peController.getSiteFlagsV1);
 
// whitelisting needs this to discover known policies and email domains.
// Then it can use user_policies to retrieve the contents of the policy and
// per-tenant config
app.get('/api/pe/v1/list_policies', peController.listPolicies);
 
// DEPRECATED. use /api/pe/v1/list_policies
app.get('/api/policy/v1/pe', peController.listPolicies);
 
// tenant configuration information
app.get('/api/v1/tenants/flash_on', peController.getFlashOnTidList);
 
// tenant capability information
app.post('/api/v1/tenants/capabilities', peController.getTenantCapabilities);
 
// map of capabilities to tenants
app.post('/api/v1/capabilities/tenants', peController.getTenantsPerCapability);
 
// tenant customization information
app.post('/api/v1/pe/customization/web', peController.getTenantCustomization);
 
//Run multiple processes using cluster
var cluster = require('cluster');
var numWorkers = require('os').cpus().length;
 
const maxWorkers = parseInt(config.pnr_enforcement.max_workers, 10);
Eif (maxWorkers > 0 && maxWorkers < numWorkers) {
   numWorkers = maxWorkers;
}
 
Eif(process.env.NODE_ENV !== 'production') {
   numWorkers = 1;
}
 
// leave one processor free for system
Iif(numWorkers > 1) {
   numWorkers -= 1;
}
 
//Check if secret key to access policy server is available.
Iif (config.networking.policy_server_enabled && !config.pnr_policy.root_secret) {
   const type = cluster.isMaster ? 'master' : 'worker';
   logger.error('event=no_root_secret process=' + type);
   process.exit(9);
}
 
// Reserve one more process for the non_pe_worker
numWorkers += 1;
 
Iif (cluster.isMaster) {
   for(var i = 0; i < numWorkers; ++i) {
      let env = _.cloneDeep(process.env);
      env.WORKER_NAME = (i === 0 ? 'non_pe_worker' : 'pe_worker_' + i);
      logger.info("ELIS: " + JSON.stringify(process.env));
      if(process.env.running_under_istanbul) {
         cluster.setupMaster({
                exec: '/usr/bin/istanbul',
                args: [
                    'cover', '--handle-sigint', '--report', 'none', '--print', 'none', '--include-pid',
                    process.argv[1], '--'].concat(process.argv.slice(2))
            });
      }
      let newWorker = cluster.fork(env);
      newWorker.process.env = env;
   }
   cluster.on('exit', function(worker, code) {
      let env = _.cloneDeep(worker.process.env);
      logger.error('event="worker died" process_id=' + worker.process.pid +
                   ' error_code=' + code + ' action="restarting worker"' +
                   ' worker_name="' + env.WORKER_NAME + '"');
      let newWorker = cluster.fork(env);
      newWorker.process.env = env;
   });
 
} else if (process.env.WORKER_NAME === 'non_pe_worker') {
   logger.info('Starting Non PE worker app');
   nonWorkerApp();
} else {
 
   // In safeview context, PE tries to listen in '10.3.0.1' network
   // which may not be available till safeview is started atleast once.
   // So, PE should keep trying to connect to network every minute
   // if it failed to listen in the previous attempt.
   var serverListening = false,
      server = null;
 
   var launchServer = function () {
      server = http.createServer(app);
      server.on('error', function (e) {
         serverListening = false;
         logger.error('pnr-enforcement server listening error - ' + e);
      });
 
      logger.info('Going to launch server listening on ' +
            app.get('host') + ':' +  app.get('port'));
 
      server.listen(app.get('port'), app.get('host'), function () {
         serverListening = true;
         //logger.info("ELIS1worker: " + JSON.stringify(process.env));
         logger.info(process.env.WORKER_NAME + ' listening on ' +
               app.get('host') + ':' +  app.get('port'));
      });
   };
 
   launchServer();
   setInterval(function () {
      Iif (!serverListening) {
         server = null;
         launchServer();
      }
   }, 60 * 1000); //every minute
 
   process.on('uncaughtException', function(err) {
      var msg = 'event=UncaughtException error_msg="' + err.message + '"';
      Eif (err.stack) {
         msg += ' stack="' + err.stack + '"';
      }
      logger.error(msg);
      process.exit(1);
   });
}