From b23a858a8c91cfdd35126454db32438d56a8c23e Mon Sep 17 00:00:00 2001 From: Calvin Montgomery Date: Mon, 5 Jun 2017 23:14:45 -0700 Subject: [PATCH] Integrate socket.io ban check with GlobalBanDB --- src/io/globalban.js | 36 ++++++++++++++++++++++++++++++ src/io/ioserver.js | 14 +++++++++++- test/io/globalban.js | 53 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 src/io/globalban.js create mode 100644 test/io/globalban.js diff --git a/src/io/globalban.js b/src/io/globalban.js new file mode 100644 index 00000000..40738e95 --- /dev/null +++ b/src/io/globalban.js @@ -0,0 +1,36 @@ +import { LoggerFactory } from '@calzoneman/jsli'; +import { getIPRange, getWideIPRange } from '../utilities'; + +const LOGGER = LoggerFactory.getLogger('CachingGlobalBanlist'); + +class CachingGlobalBanlist { + constructor(globalBanDB) { + this.globalBanDB = globalBanDB; + this.cache = new Set(); + this.cacheTimer = null; + } + + refreshCache() { + return this.globalBanDB.listGlobalBans().then(bans => { + this.cache.clear(); + bans.forEach(ban => { + this.cache.add(ban.ip); + }); + }).catch(error => { + LOGGER.error('Unable to refresh global banlist cache: %s', error.stack); + }); + } + + startCacheTimer(interval) { + clearInterval(this.cacheTimer); + this.cacheTimer = setInterval(this.refreshCache.bind(this), interval); + } + + isIPGlobalBanned(ip) { + return this.cache.has(ip) + || this.cache.has(getIPRange(ip)) + || this.cache.has(getWideIPRange(ip)); + } +} + +export { CachingGlobalBanlist }; diff --git a/src/io/ioserver.js b/src/io/ioserver.js index 823bbc87..8461e283 100644 --- a/src/io/ioserver.js +++ b/src/io/ioserver.js @@ -18,6 +18,7 @@ import Promise from 'bluebird'; import { LoggerFactory } from '@calzoneman/jsli'; const verifySession = Promise.promisify(session.verifySession); const getAliases = Promise.promisify(db.getAliases); +import { CachingGlobalBanlist } from './globalban'; const LOGGER = LoggerFactory.getLogger('ioserver'); @@ -197,6 +198,17 @@ function ipForwardingMiddleware(webConfig) { } } +let globalIPBanlist = null; +function isIPGlobalBanned(ip) { + if (globalIPBanlist === null) { + globalIPBanlist = new CachingGlobalBanlist(db.getGlobalBanDB()); + globalIPBanlist.refreshCache(); + globalIPBanlist.startCacheTimer(60 * 1000); + } + + return globalIPBanlist.isIPGlobalBanned(ip); +} + /** * Called after a connection is accepted */ @@ -226,7 +238,7 @@ function handleConnection(sock) { } // Check for global ban on the IP - if (db.isGlobalIPBanned(ip)) { + if (isIPGlobalBanned(ip)) { LOGGER.info("Rejecting " + ip + " - global banned"); sock.emit("kick", { reason: "Your IP is globally banned." }); sock.disconnect(); diff --git a/test/io/globalban.js b/test/io/globalban.js new file mode 100644 index 00000000..45077141 --- /dev/null +++ b/test/io/globalban.js @@ -0,0 +1,53 @@ +const assert = require('assert'); +const sinon = require('sinon'); +const GlobalBanDB = require('../../lib/db/globalban').GlobalBanDB; +const CachingGlobalBanlist = require('../../lib/io/globalban').CachingGlobalBanlist; + +describe('CachingGlobalBanlist', () => { + let banlist = null; + let banDB = null; + beforeEach(() => { + banDB = new GlobalBanDB(); + banlist = new CachingGlobalBanlist(banDB); + }); + + describe('refreshCache', () => { + it('caches bans', () => { + const bans = [{ ip: '1.1.1.1', reason: 'test' }]; + sinon.stub(banDB, 'listGlobalBans').resolves(bans); + return banlist.refreshCache().then(() => { + assert(banlist.cache.has(bans[0].ip), 'Cache was not populated'); + }); + }); + + it('clears removed bans', () => { + banlist.cache.add('1.1.1.1'); + sinon.stub(banDB, 'listGlobalBans').resolves([]); + return banlist.refreshCache().then(() => { + assert(!banlist.cache.has('1.1.1.1'), 'Cache was not updated'); + }); + }); + + it('fails open', () => { + sinon.stub(banDB, 'listGlobalBans').rejects(new Error('Broken')); + return banlist.refreshCache(); + }); + }); + + describe('isIPGlobalBanned', () => { + it('checks the full IP', () => { + banlist.cache.add('1.2.3.4'); + assert(banlist.isIPGlobalBanned('1.2.3.4'), 'Expected IP to be banned'); + }); + + it('checks the range IP', () => { + banlist.cache.add('1.2.3'); + assert(banlist.isIPGlobalBanned('1.2.3.4'), 'Expected IP to be banned'); + }); + + it('checks the wrange IP', () => { + banlist.cache.add('1.2'); + assert(banlist.isIPGlobalBanned('1.2.3.4'), 'Expected IP to be banned'); + }); + }); +});