diff --git a/Dockerfile b/Dockerfile index ae9ba4b..622950f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ WORKDIR /app EXPOSE 9050 -EXPOSE 53 +EXPOSE 9053 EXPOSE 9077 @@ -12,9 +12,11 @@ ENV PARENT_DATA_DIRECTORTY /var/lib/tor-router ENV TOR_PATH /usr/bin/tor +ENV NODE_ENV production + ENV PATH $PATH:/app/bin -RUN apt-get update && apt-get install -y tor +RUN apt-get update && apt-get install -y tor && rm -rf /var/lib/apt/lists/* RUN useradd -ms /bin/bash tor_router @@ -24,7 +26,7 @@ USER tor_router ADD package.json /app/package.json -RUN npm install +RUN npm install ADD . /app diff --git a/README.md b/README.md index 586c026..0c0a8cd 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ If just a port number is passed in place of a host, it will bind to all interfac |-p, --parentDataDirectory |PARENT_DATA_DIRECTORY|Parent directory that will contain the data directories for the instances| |-b, --loadBalanceMethod |LOAD_BALANCE_METHOD |Method that will be used to sort the instances between each request. Currently supports "round_robin" and "weighted". | |-t, --torPath |TOR_PATH |Provide the path for the Tor executable that will be used| -|-n, --proxyByName |PROXY_BY_NAME |Controls how authenticated requests will be handled. Can be set to "individual", "groups" or false to disable| +|-n, --proxyByName |PROXY_BY_NAME |Controls how authenticated requests will be handled. Can be set to "individual", "group" or false to disable| A full list of all available configuration options and their defaults can be found in [default_config.js](https://github.com/znetstar/tor-router/blob/master/src/default_config.js) @@ -78,7 +78,7 @@ Using the configuration file you can set a default configuration for all Tor ins You can also specify a configuration for individual instances by setting the "instances" field to an array instead of an integer. -Instances can optionally be assigned name and a weight. If the `loadBalanceMethod` config variable is set to "weighted" the weight field will determine how frequently the instance is used. If the instance is assigned a name the data directory will be preserved when the process is killed saving time when Tor is restarted. +Instances can optionally be assigned name and a weight. If the `loadBalanceMethod` config variable is set to "weighted" the weight field will determine how frequently the instance is used. If the instance is assigned a name the data directory will be preserved when the process is killed saving time when Tor is restarted. They can also be assigned one or more groups. ``` { @@ -100,9 +100,39 @@ Instances can optionally be assigned name and a weight. If the `loadBalanceMetho } ``` +If the `proxyByName` (argument `-n`) configuration property is set to 'individual', which it is by default, you can use the instance name to send requests to a specific instance. The username field in the proxy URL will identify the instance. For example, using `http://instance-1:@localhost:9080` when connecting to the HTTP Proxy would route requests to "instance-1". + +This feature works on the HTTP Proxy as well as the SOCKS Proxy, but not the DNS proxy since DNS lacks authentication. + +## Groups + +Instances can be assigned one or more groups. + +``` +{ + "loadBalanceMethod": "weighted", + "instances": [ + { + "Name": "instance-1", + "Group": [ "foo", "bar ] + }, + { + "Name": "instance-2", + "Group": [ "foo" ] + }, + { + "Name": "instance-3", + "Group": "baz" + } + ] +} +``` + +If the `proxyByName` (argument `-n`) configuration property is set to "group" requests can be routed to a group of instances using the username field in the proxy URL. For example, using `http://foo:@localhost:9080` as the proxy URL will send requests too "instance-1" and "instance-2" rotating them in a round-robin fashion. + ## Control Server -A JSON-RPC 2 TCP Server will listen on port 9077 by default. Using the rpc server the client can add/remove Tor instances and get a new identity (which includes a new ip address) while Tor Router is running. +A JSON-RPC 2 TCP Server will listen on port 9077 by default. Using the rpc server the client can add/remove Tor instances and get a new identity (which includes a new ip address) while Tor Router is running. The control server will also accept websocket connections if the `--websocketControlHost` or `-w` flag is set. Example (in node): diff --git a/package.json b/package.json index 53dab57..d336e6d 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "scripts": { "start": "bin/tor-router -s -d -j 1", - "test": "mocha --exit test", + "test": "mocha -u tdd --exit test/index.js", "debug": "node --inspect-brk bin/tor-router" }, "devDependencies": { diff --git a/src/ControlServer.js b/src/ControlServer.js index e79d7a4..a416007 100644 --- a/src/ControlServer.js +++ b/src/ControlServer.js @@ -197,14 +197,14 @@ class ControlServer { } async createSOCKSServer(port, hostname) { - this.socksServer = new SOCKSServer(this.torPool, this.logger, (this.nconf.get('proxyByName') ? { mode: this.nconf.get('proxyByName'), deny_unidentified_users: this.nconf.get('denyUnidentifedUsers') } : "")); + this.socksServer = new SOCKSServer(this.torPool, this.logger, (this.nconf.get('proxyByName') ? { mode: this.nconf.get('proxyByName'), deny_unidentified_users: this.nconf.get('denyUnidentifiedUsers') } : "")); await this.socksServer.listen(port || default_ports.socks, hostname); this.logger.info(`[socks]: listening on socks5://${hostname}:${port}`); this.socksServer; } async createHTTPServer(port, hostname) { - this.httpServer = new HTTPServer(this.torPool, this.logger, (this.nconf.get('proxyByName') ? { mode: this.nconf.get('proxyByName'), deny_unidentified_users: this.nconf.get('denyUnidentifedUsers') } : "")); + this.httpServer = new HTTPServer(this.torPool, this.logger, (this.nconf.get('proxyByName') ? { mode: this.nconf.get('proxyByName'), deny_unidentified_users: this.nconf.get('denyUnidentifiedUsers') } : "")); await this.httpServer.listen(port || default_ports.http, hostname); this.logger.info(`[http]: listening on http://${hostname}:${port}`); this.httpServer; diff --git a/src/HTTPServer.js b/src/HTTPServer.js index c573a2a..461c4d9 100644 --- a/src/HTTPServer.js +++ b/src/HTTPServer.js @@ -39,8 +39,6 @@ class HTTPServer extends Server { if (!this.proxy_by_name) return true; - this.logger.verbose(`[http]: connected attempted to instance "${username}"`); - let deny_un = this.proxy_by_name.deny_unidentified_users; let header = req.headers['authorization'] || req.headers['proxy-authorization']; @@ -52,6 +50,9 @@ class HTTPServer extends Server { else if (!token) return true; let buf = new Buffer.from(token, 'base64').toString(); + if ( !buf && deny_un ) return deny(); + else if (!buf) return true; + let username = buf.split(/:/).shift(); if ( !username && deny_un ) return deny(); else if (!username) return true; diff --git a/src/SOCKSServer.js b/src/SOCKSServer.js index 427e9db..91a2ab8 100644 --- a/src/SOCKSServer.js +++ b/src/SOCKSServer.js @@ -26,8 +26,6 @@ class SOCKSServer extends Server{ authenticate_user(username, password, callback) { let deny_un = this.proxy_by_name.deny_unidentified_users; - - this.logger.verbose(`[socks]: connected attempted to instance "${username}"`); // No username and deny unindentifed then deny if (!username && deny_un) callback(false); diff --git a/test/DNSServer.js b/test/DNSServer.js index 0993601..61f0e47 100644 --- a/test/DNSServer.js +++ b/test/DNSServer.js @@ -55,7 +55,6 @@ describe('DNSServer', function () { }); after('shutdown tor pool', async function () { - dnsServer.close(); await dnsServerTorPool.exit(); }); }); \ No newline at end of file diff --git a/test/HTTPServer.js b/test/HTTPServer.js index 93e51db..452f15a 100644 --- a/test/HTTPServer.js +++ b/test/HTTPServer.js @@ -147,7 +147,6 @@ describe('HTTPServer', function () { let request = require('request-promise').defaults({ proxy: `http://foo:@127.0.0.1:${httpPort}` }); - let names_requested = []; let connectionHandler = (instance, source) => { @@ -180,7 +179,7 @@ describe('HTTPServer', function () { }) .catch(done); }); - + it(`shouldn't be able to send a request without a username`, async function() { let f = () => {}; try { diff --git a/test/TorPool.js b/test/TorPool.js index ef30ce4..a880f50 100644 --- a/test/TorPool.js +++ b/test/TorPool.js @@ -89,8 +89,8 @@ describe('TorPool', function () { describe('#add(instance_defintions)', function () { let instance_defintions = [ - { Name: 'instance-1', Config: { ProtocolWarnings: 1} }, - { Name: 'instance-2', Config: { ProtocolWarnings: 1 } } + { Name: 'instance-1', Group: [], Config: { ProtocolWarnings: 1} }, + { Name: 'instance-2', Group: [], Config: { ProtocolWarnings: 1 } } ]; let torPool; diff --git a/test/constants.js b/test/constants.js index c814caa..f48edd2 100644 --- a/test/constants.js +++ b/test/constants.js @@ -1,4 +1,4 @@ module.exports = { WAIT_FOR_CREATE: 240000, - PAGE_LOAD_TIME: 60000 * 5 + PAGE_LOAD_TIME: 60000 }; \ No newline at end of file