Skip to content
This repository was archived by the owner on Jan 9, 2023. It is now read-only.

Commit 450e8e0

Browse files
Merge pull request #48 from adipirro/feature/putWhitelist
PUT Whitelist and IPv6 toggle
2 parents e28c0b1 + 9f728b1 commit 450e8e0

File tree

6 files changed

+174
-9
lines changed

6 files changed

+174
-9
lines changed

config/default.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
Cache:
22
defaultModule: "cache_fs"
33
options:
4+
processor:
5+
putWhitelist: []
46
cache_ram:
57
cachePath: ".cache_ram"
68
pageSize: 100000000
@@ -34,4 +36,7 @@ Cache:
3436
Mirror:
3537
options:
3638
queueProcessDelay: 2000
37-
connectionIdleTimeout: 10000
39+
connectionIdleTimeout: 10000
40+
Server:
41+
options:
42+
allowIpv6: false

lib/server/command_processor.js

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const helpers = require('./../helpers');
2+
const config = require('config');
23
const filesize = require('filesize');
34
const consts = require('./../constants');
45
const Duplex = require('stream').Duplex;
@@ -35,6 +36,10 @@ class CommandProcessor extends Duplex {
3536
*/
3637
this._trx = null;
3738

39+
this._options = config.get("Cache.options.processor");
40+
this._putWhitelist = this._options.putWhitelist;
41+
this._whitelistEmpty = (!Array.isArray(this._putWhitelist) || !this._putWhitelist.length);
42+
3843
this._putStream = null;
3944
this._putSize = 0;
4045
this._putSent = 0;
@@ -77,6 +82,13 @@ class CommandProcessor extends Duplex {
7782
Promise.resolve().then(() => this._read_internal());
7883
}
7984

85+
/**
86+
* @private
87+
*/
88+
_isWhitelisted(ip) {
89+
return this._whitelistEmpty || this._putWhitelist.includes(ip);
90+
}
91+
8092
/**
8193
* @private
8294
*/
@@ -330,10 +342,16 @@ class CommandProcessor extends Duplex {
330342
throw new Error("Not in a transaction");
331343
}
332344

333-
this._putStream = await this._trx.getWriteStream(type, size);
334-
this._putStream.promiseWrite = promisify(this._putStream.write).bind(this._putStream);
335-
this._putSize = size;
336-
this._writeHandler = this._writeHandlers.putStream;
345+
if (this._isWhitelisted(this._trx.clientAddress)) {
346+
this._putStream = await this._trx.getWriteStream(type, size);
347+
this._putStream.promiseWrite = promisify(this._putStream.write).bind(this._putStream);
348+
this._putSize = size;
349+
this._writeHandler = this._writeHandlers.putStream;
350+
}
351+
else {
352+
this._writeHandler = this._writeHandlers.none;
353+
helpers.log(consts.LOG_DBG, `PUT rejected from non-whitelisted IP: ${this._trx.clientAddress}`);
354+
}
337355
}
338356
}
339357

lib/server/server.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ class CacheServer {
2626
options.mirror = [].concat(options.mirror);
2727
this._mirrors = options.mirror.map(m => new TransactionMirror(m, cache));
2828
}
29+
30+
this.allowIpv6 = options.allowIpv6;
2931
}
3032

3133
/**
@@ -87,7 +89,13 @@ class CacheServer {
8789
});
8890

8991
return new Promise(resolve => {
90-
this._server.listen(this.port, () => resolve());
92+
if(this.allowIpv6) {
93+
this._server.listen(this.port, () => resolve());
94+
}
95+
else {
96+
this._server.listen(this.port, "0.0.0.0", () => resolve());
97+
}
98+
9199
});
92100
};
93101

main.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ function collect(val, memo) {
2828

2929
const defaultCacheModule = config.get("Cache.defaultModule");
3030

31+
const processorOptions = config.get("Cache.options.processor");
32+
if(Array.isArray(processorOptions.putWhitelist) && processorOptions.putWhitelist.length){
33+
helpers.log(consts.LOG_INFO, `PUT whitelist: ${processorOptions.putWhitelist}`);
34+
};
35+
3136
program.description("Unity Cache Server")
3237
.version(VERSION)
3338
.allowUnknownOption(true)
@@ -114,7 +119,8 @@ Cache.init(cacheOpts)
114119
.then(mirrors => {
115120
const opts = {
116121
port: program.port,
117-
mirror: mirrors
122+
mirror: mirrors,
123+
allowIpv6: config.has("Server.options.allowIpv6") ? config.get("Server.options.allowIpv6") : false
118124
};
119125

120126
server = new Server(Cache, opts);

test/command_processor.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
const assert = require('assert');
2+
const sinon = require('sinon');
3+
const { CommandProcessor, CacheBase , PutTransaction } = require('../lib');
4+
5+
describe("CommandProcessor", () => {
6+
describe("PUT Whitelist", () => {
7+
beforeEach(() => {
8+
cmdProc = new CommandProcessor(new CacheBase());
9+
});
10+
11+
it("should implement PUT when whitelisted", async () => {
12+
cmdProc._whitelistEmpty = false;
13+
cmdProc._putWhitelist = ["127.0.0.1"];
14+
15+
cmdProc._trx = new PutTransaction();
16+
cmdProc._trx.clientAddress = "127.0.0.1";
17+
spy = sinon.spy(cmdProc._trx, "getWriteStream");
18+
19+
p = cmdProc._onPut("a", 999)
20+
p.catch(function () {});
21+
22+
assert(spy.called)
23+
});
24+
25+
it("should implement PUT when whitelisted (multiple)", async () => {
26+
cmdProc._whitelistEmpty = false;
27+
cmdProc._putWhitelist = ["127.0.0.6", "127.0.0.3", "127.0.0.1"];
28+
29+
cmdProc._trx = new PutTransaction();
30+
cmdProc._trx.clientAddress = "127.0.0.1";
31+
spy = sinon.spy(cmdProc._trx, "getWriteStream");
32+
33+
p = cmdProc._onPut("a", 999)
34+
p.catch(function () {});
35+
36+
assert(spy.called)
37+
});
38+
39+
it("should implement PUT when whitelist empty", async () => {
40+
cmdProc._whitelistEmpty = true;
41+
cmdProc._putWhitelist = [];
42+
43+
cmdProc._trx = new PutTransaction();
44+
cmdProc._trx.clientAddress = "127.0.0.1";
45+
spy = sinon.spy(cmdProc._trx, "getWriteStream");
46+
47+
p = cmdProc._onPut("a", 999)
48+
p.catch(function () {});
49+
50+
assert(spy.called)
51+
});
52+
53+
it("should not implement PUT when not whitelisted", async () => {
54+
cmdProc._whitelistEmpty = false
55+
cmdProc._putWhitelist = ["127.0.0.1"]
56+
57+
cmdProc._trx = new PutTransaction();
58+
cmdProc._trx.clientAddress = "127.0.0.2";
59+
60+
await cmdProc._onPut("a", 999)
61+
assert.strictEqual(cmdProc._writeHandler, cmdProc._writeHandlers.none);
62+
});
63+
64+
it("should not implement PUT when not whitelisted (multiple)", async () => {
65+
cmdProc._whitelistEmpty = false
66+
cmdProc._putWhitelist = ["127.0.0.6", "127.0.0.3", "127.0.0.1"]
67+
68+
cmdProc._trx = new PutTransaction();
69+
cmdProc._trx.clientAddress = "127.0.0.2";
70+
71+
await cmdProc._onPut("a", 999)
72+
assert.strictEqual(cmdProc._writeHandler, cmdProc._writeHandlers.none);
73+
});
74+
});
75+
});

test/server.js

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const assert = require('assert');
22
const net = require('net');
3+
const os = require('os');
34
const helpers = require('../lib/helpers');
45
const consts = require('../lib/constants');
56
const CacheServer = require('../lib/server/server');
@@ -67,6 +68,57 @@ describe("Server common", function() {
6768
});
6869
});
6970

71+
describe("Ipv6", function() {
72+
const ipv6Server = new CacheServer(cache, {port: 0, allowIpv6: true});
73+
74+
before(function () {
75+
var interfaces = os.networkInterfaces();
76+
var ipv6Available = false;
77+
Object.keys(interfaces).forEach(function (interfaceName){
78+
interfaces[interfaceName].forEach(function (address){
79+
if(address.family === "IPv6"){
80+
ipv6Available = true;
81+
}
82+
});
83+
});
84+
85+
if(!ipv6Available){
86+
console.log("Skipping IPv6 tests because IPv6 is not available on this machine");
87+
this.skip();
88+
}
89+
90+
return ipv6Server.start(err => assert(!err, `Cache Server reported error! ${err}`));
91+
});
92+
93+
after(function() {
94+
ipv6Server.stop();
95+
});
96+
97+
it("should bind to ipv6 when allowed", function(done) {
98+
var serverAddress = ipv6Server._server.address();
99+
assert.strictEqual(serverAddress.family, "IPv6");
100+
done();
101+
});
102+
103+
});
104+
describe("Ipv4", function() {
105+
const ipv4Server = new CacheServer(cache, {port: 0, allowIpv6: false});
106+
107+
before(function () {
108+
return ipv4Server.start(err => assert(!err, `Cache Server reported error! ${err}`));
109+
});
110+
111+
after(function() {
112+
ipv4Server.stop();
113+
});
114+
115+
it("should bind to ipv4 when ipv6 not allowed", function(done) {
116+
var serverAddress = ipv4Server._server.address();
117+
assert.strictEqual(serverAddress.family, "IPv4");
118+
done();
119+
});
120+
});
121+
70122
describe("Other", function() {
71123
it("should force close the socket when a quit (q) command is received", function(done) {
72124
client = net.connect({port: server.port}, function (err) {
@@ -92,6 +144,7 @@ describe("Server common", function() {
92144
client.write(helpers.encodeInt32(consts.PROTOCOL_VERSION));
93145
client.write('xx');
94146
});
95-
})
147+
});
96148
})
97-
});
149+
});
150+

0 commit comments

Comments
 (0)