From 5068605dc7d7d4f323a58b313754977744d7e9db Mon Sep 17 00:00:00 2001 From: Nick Muerdter Date: Thu, 15 Sep 2016 10:30:06 -0600 Subject: [PATCH] Don't bind Elasticsearch or Mongo to public network interface by default If you were running API Umbrella on an unprotected network without firewall rules in place, this could have allowed external access to these databases. Now if you're setting up a multi-server environment, then you must explicitly change these settings so that the servers are listening on a public interface (eg, 0.0.0.0 so cross-server communication can happen). By making this configuration explicit, it should hopefully ensure the appropriate firewall/network rules get put in place to protect these services in a multi-server environment (but a default single-server environment won't be open if running on an unprotected network). This also aligns with the newer defaults in Elasticsearch 2 (https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking_20_network_changes.html) and MongoDB's default rpm/deb package installations (https://jira.mongodb.org/browse/SERVER-792). See https://github.com/NREL/api-umbrella/issues/287 --- config/default.yml | 3 ++ templates/etc/nginx/test_backends.conf | 12 +++--- test/integration/processes.js | 55 ++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/config/default.yml b/config/default.yml index 61636019b..c0b19e53d 100644 --- a/config/default.yml +++ b/config/default.yml @@ -142,6 +142,7 @@ mongodb: processManagement: fork: false net: + bindIp: 127.0.0.1 port: 14001 mora: host: 127.0.0.1 @@ -154,6 +155,8 @@ elasticsearch: heap_size: 512m api_version: 1 embedded_server_config: + network: + host: 127.0.0.1 http: port: 14002 transport: diff --git a/templates/etc/nginx/test_backends.conf b/templates/etc/nginx/test_backends.conf index ad2d1ea5e..c74385b92 100644 --- a/templates/etc/nginx/test_backends.conf +++ b/templates/etc/nginx/test_backends.conf @@ -3,8 +3,8 @@ lua_shared_dict test_data 32k; server { listen 9444; listen [::]:9444; - listen 9441; - listen [::]:9441; + listen 127.0.0.1:9441; + listen [::1]:9441; set $x_api_umbrella_request_id $http_x_api_umbrella_request_id; lua_use_default_type off; @@ -577,8 +577,8 @@ server { } server { - listen 9443; - listen [::]:9443; + listen 127.0.0.1:9443; + listen [::1]:9443; location = / { echo -n "Test Website Home Page"; @@ -592,8 +592,8 @@ server { } server { - listen 9442; - listen [::]:9442; + listen 127.0.0.1:9442; + listen [::1]:9442; set $x_api_umbrella_request_id $http_x_api_umbrella_request_id; diff --git a/test/integration/processes.js b/test/integration/processes.js index f71f32343..4ce35a8cd 100644 --- a/test/integration/processes.js +++ b/test/integration/processes.js @@ -5,6 +5,7 @@ require('../test_helper'); var _ = require('lodash'), async = require('async'), config = require('../support/config'), + exec = require('child_process').exec, execFile = require('child_process').execFile, Factory = require('factory-lady'), fs = require('fs'), @@ -15,6 +16,60 @@ var _ = require('lodash'), describe('processes', function() { shared.runServer(); + describe('network listening', function() { + it('only listens for the HTTP/HTTPS ports on the public interface by default (all other processes are bound to localhost)', function(done) { + exec('lsof -n -P -l -R -p $(pstree -p $(cat ' + path.join(config.get('run_dir'), 'perpboot.pid') + ') | grep -o "([0-9]\\+)" | grep -o "[0-9]\\+" | tr "\\012" ",") | grep LISTEN', function(error, stdout, stderr) { + if(error) { + return done('Error gathering lsof details: ' + error.message + '\n\nSTDOUT: ' + stdout + '\n\nSTDERR:' + stderr); + } + + var listening = { + local: [], + public: [], + }; + var lines = stdout.trim().split('\n'); + lines.forEach(function(line) { + var ipVersion = line.match(/(IPv4|IPv6)/)[1]; + should.exist(ipVersion); + + var port = line.match(/:(\d+) \(LISTEN\)/)[1]; + should.exist(port); + + var listen = port + ':' + ipVersion; + if(_.contains(line, 'TCP 127.0.0.1:') || _.contains(line, 'TCP [::1]:')) { + if(listening.local.indexOf(listen) === -1) { + listening.local.push(listen); + } + } else if(_.contains(line, 'TCP *:')) { + if(listening.public.indexOf(listen) === -1) { + listening.public.push(listen); + } + } else { + return done('Unknown listening (not localhost or public): ' + line); + } + }); + + _.uniq(listening.public).sort().should.eql([ + // HTTP port in test environment. + '9080:IPv4', + '9080:IPv6', + + // HTTPS port in test environment. + '9081:IPv4', + '9081:IPv6', + + // API backend for our test environment that needs to listen on all + // interfaces for some of our DNS tests that use 127.0.0.2, + // 127.0.0.3, etc. + '9444:IPv4', + '9444:IPv6', + ]); + + done(); + }); + }); + }); + describe('nginx', function() { beforeEach(function setOptionDefaults(done) { Factory.create('api_user', { settings: { rate_limit_mode: 'unlimited' } }, function(user) {