Can now connect directly to an instance via http
This commit is contained in:
parent
4553bcc82b
commit
ece4d4ea02
|
@ -17,7 +17,6 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"chai": "^4.1.2",
|
"chai": "^4.1.2",
|
||||||
"mocha": "^5.2.0",
|
"mocha": "^5.2.0",
|
||||||
"proxy-agent": "^3.0.1",
|
|
||||||
"request": "^2.79.0",
|
"request": "^2.79.0",
|
||||||
"request-promise": "^4.2.2"
|
"request-promise": "^4.2.2"
|
||||||
},
|
},
|
||||||
|
@ -37,6 +36,7 @@
|
||||||
"socksv5": "git+https://github.com/znetstar/socksv5.git#431e541390314adbbe765650d8d810ec1df38d8a",
|
"socksv5": "git+https://github.com/znetstar/socksv5.git#431e541390314adbbe765650d8d810ec1df38d8a",
|
||||||
"temp": "^0.8.3",
|
"temp": "^0.8.3",
|
||||||
"winston": "^3.0.0-rc5",
|
"winston": "^3.0.0-rc5",
|
||||||
"yargs": "^11.0.0"
|
"yargs": "^11.0.0",
|
||||||
|
"proxy-agent": "^3.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,14 +144,14 @@ class ControlServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
async createSOCKSServer(port, hostname) {
|
async createSOCKSServer(port, hostname) {
|
||||||
this.socksServer = new SOCKSServer(this.torPool, this.logger);
|
this.socksServer = new SOCKSServer(this.torPool, this.logger, (this.nconf.get('proxyByName') ? { deny_unidentified_users: this.nconf.get('denyUnidentifedUsers') } : ""));
|
||||||
await this.socksServer.listen(port || default_ports.socks, hostname);
|
await this.socksServer.listen(port || default_ports.socks, hostname);
|
||||||
this.logger.info(`[socks]: listening on socks5://${hostname}:${port}`);
|
this.logger.info(`[socks]: listening on socks5://${hostname}:${port}`);
|
||||||
this.socksServer;
|
this.socksServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createHTTPServer(port, hostname) {
|
async createHTTPServer(port, hostname) {
|
||||||
this.httpServer = new HTTPServer(this.torPool, this.logger);
|
this.httpServer = new HTTPServer(this.torPool, this.logger, (this.nconf.get('proxyByName') ? { deny_unidentified_users: this.nconf.get('denyUnidentifedUsers') } : ""));
|
||||||
await this.httpServer.listen(port || default_ports.http, hostname);
|
await this.httpServer.listen(port || default_ports.http, hostname);
|
||||||
this.logger.info(`[http]: listening on http://${hostname}:${port}`);
|
this.logger.info(`[http]: listening on http://${hostname}:${port}`);
|
||||||
this.httpServer;
|
this.httpServer;
|
||||||
|
|
|
@ -4,7 +4,7 @@ const { Server } = http;
|
||||||
|
|
||||||
const Promise = require('bluebird');
|
const Promise = require('bluebird');
|
||||||
const socks = require('socksv5');
|
const socks = require('socksv5');
|
||||||
const SocksProxyAgent = require('socks-proxy-agent');
|
const ProxyAgent = require('proxy-agent');
|
||||||
|
|
||||||
const TOR_ROUTER_PROXY_AGENT = 'tor-router';
|
const TOR_ROUTER_PROXY_AGENT = 'tor-router';
|
||||||
|
|
||||||
|
@ -21,8 +21,57 @@ class HTTPServer extends Server {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(tor_pool, logger) {
|
authenticate_user_http(req, res) {
|
||||||
|
return this.authenticate_user(req, () => {
|
||||||
|
res.writeHead(407, { 'Proxy-Authenticate': `Basic realm="Name of instance to route to"` });
|
||||||
|
res.end();
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticate_user_connect(req, socket) {
|
||||||
|
return this.authenticate_user(req, () => {
|
||||||
|
socket.end();
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticate_user(req, deny) {
|
||||||
|
if (!this.proxy_by_name)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
let deny_un = this.proxy_by_name.deny_unidentified_users;
|
||||||
|
|
||||||
|
let header = req.headers['authorization'] || req.headers['proxy-authorization'];
|
||||||
|
if (!header && deny_un) return deny();
|
||||||
|
else if (!header) return true;
|
||||||
|
|
||||||
|
let token = header.split(/\s+/).pop();
|
||||||
|
if (!token && deny_un) return deny();
|
||||||
|
else if (!token) return true;
|
||||||
|
|
||||||
|
let buf = new Buffer.from(token, 'base64').toString();
|
||||||
|
let username = buf.split(/:/).shift();
|
||||||
|
if ( !username && deny_un ) return deny();
|
||||||
|
else if (!username) return true;
|
||||||
|
|
||||||
|
this.logger.verbose(`[http]: connected attempted to instance "${username}"`);
|
||||||
|
|
||||||
|
let instance = this.tor_pool.instance_by_name(username);
|
||||||
|
|
||||||
|
if (!instance) return deny();
|
||||||
|
req.instance = instance;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(tor_pool, logger, proxy_by_name) {
|
||||||
let handle_http_connections = (req, res) => {
|
let handle_http_connections = (req, res) => {
|
||||||
|
if (!this.authenticate_user_http(req, res))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let { instance } = req;
|
||||||
|
|
||||||
let url = URL.parse(req.url);
|
let url = URL.parse(req.url);
|
||||||
url.port = url.port || 80;
|
url.port = url.port || 80;
|
||||||
|
|
||||||
|
@ -52,7 +101,7 @@ class HTTPServer extends Server {
|
||||||
port: url.port,
|
port: url.port,
|
||||||
path: url.path,
|
path: url.path,
|
||||||
headers: req.headers,
|
headers: req.headers,
|
||||||
agent: new SocksProxyAgent(`socks://127.0.0.1:${socks_port}`)
|
agent: new ProxyAgent(`socks://127.0.0.1:${socks_port}`)
|
||||||
}, (proxy_res) => {
|
}, (proxy_res) => {
|
||||||
proxy_res.on('data', (chunk) => {
|
proxy_res.on('data', (chunk) => {
|
||||||
res.write(chunk);
|
res.write(chunk);
|
||||||
|
@ -83,8 +132,17 @@ class HTTPServer extends Server {
|
||||||
proxy_req.end();
|
proxy_req.end();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (tor_pool.instances.length) {
|
if (instance) {
|
||||||
|
if (instance.ready) {
|
||||||
|
connect(instance);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.logger.debug(`[socks]: a connection has been attempted to "${instance.instance_name}", but it is not live... waiting for the instance to come online`);
|
||||||
|
instance.once('ready', (() => connect(instance)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (this.tor_pool.instances.length) {
|
||||||
connect(tor_pool.next());
|
connect(tor_pool.next());
|
||||||
} else {
|
} else {
|
||||||
this.logger.debug(`[http-proxy]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`);
|
this.logger.debug(`[http-proxy]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`);
|
||||||
|
@ -93,6 +151,11 @@ class HTTPServer extends Server {
|
||||||
};
|
};
|
||||||
|
|
||||||
let handle_connect_connections = (req, inbound_socket, head) => {
|
let handle_connect_connections = (req, inbound_socket, head) => {
|
||||||
|
if (!this.authenticate_user_connect(req, inbound_socket))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let { instance } = req;
|
||||||
|
|
||||||
let hostname = req.url.split(':').shift();
|
let hostname = req.url.split(':').shift();
|
||||||
let port = Number(req.url.split(':').pop());
|
let port = Number(req.url.split(':').pop());
|
||||||
|
|
||||||
|
@ -135,11 +198,21 @@ class HTTPServer extends Server {
|
||||||
inbound_socket.pipe(outbound_socket);
|
inbound_socket.pipe(outbound_socket);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
if (tor_pool.instances.length) {
|
|
||||||
connect(tor_pool.next());
|
if (instance) {
|
||||||
|
if (instance.ready) {
|
||||||
|
connect(instance);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.logger.debug(`[socks]: a connection has been attempted to "${instance.instance_name}", but it is not live... waiting for the instance to come online`);
|
||||||
|
instance.once('ready', (() => connect(instance)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (this.tor_pool.instances.length) {
|
||||||
|
connect(this.tor_pool.next());
|
||||||
} else {
|
} else {
|
||||||
this.logger.debug(`[http-connect]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`);
|
this.logger.debug(`[http-connect]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`);
|
||||||
tor_pool.once('instance_created', connect);
|
this.tor_pool.once('instance_created', connect);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -148,6 +221,7 @@ class HTTPServer extends Server {
|
||||||
|
|
||||||
this.logger = logger || require('./winston-silent-logger');
|
this.logger = logger || require('./winston-silent-logger');
|
||||||
this.tor_pool = tor_pool;
|
this.tor_pool = tor_pool;
|
||||||
|
this.proxy_by_name = proxy_by_name;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,35 @@ class SOCKSServer extends Server{
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
authenticate_user(username, password, callback) {
|
||||||
|
let deny_un = this.proxy_by_name.deny_unidentifed_users;
|
||||||
|
|
||||||
|
// No username and deny unindentifed then deny
|
||||||
|
if (!username && deny_un) callback(false);
|
||||||
|
// Otherwise if there is no username allow
|
||||||
|
else if (!username) callback(true);
|
||||||
|
|
||||||
|
this.logger.verbose(`[socks]: connected attempted to instance "${username}"`);
|
||||||
|
|
||||||
|
let instance = this.tor_pool.instance_by_name(username);
|
||||||
|
|
||||||
|
// If a username is specified but no instances match that username deny
|
||||||
|
if (!instance)
|
||||||
|
return callback(false);
|
||||||
|
|
||||||
|
// Otherwise allow
|
||||||
|
callback(true, instance);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(tor_pool, logger, proxy_by_name) {
|
constructor(tor_pool, logger, proxy_by_name) {
|
||||||
let handleConnection = (info, accept, deny) => {
|
let handleConnection = (info, accept, deny) => {
|
||||||
let inbound_socket = accept(true);
|
let inbound_socket = accept(true);
|
||||||
var outbound_socket;
|
let instance;
|
||||||
|
|
||||||
|
if (inbound_socket.user)
|
||||||
|
instance = inbound_socket.user;
|
||||||
|
|
||||||
|
let outbound_socket;
|
||||||
let buffer = [];
|
let buffer = [];
|
||||||
|
|
||||||
let onInboundData = (data) => buffer.push(data)
|
let onInboundData = (data) => buffer.push(data)
|
||||||
|
@ -70,27 +95,37 @@ class SOCKSServer extends Server{
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
if (tor_pool.instances.length) {
|
|
||||||
connect(tor_pool.next());
|
if (instance) {
|
||||||
|
if (instance.ready) {
|
||||||
|
connect(instance);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.logger.debug(`[socks]: a connection has been attempted to "${instance.instance_name}", but it is not live... waiting for the instance to come online`);
|
||||||
|
instance.once('ready', (() => connect(instance)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (this.tor_pool.instances.length) {
|
||||||
|
connect(this.tor_pool.next());
|
||||||
} else {
|
} else {
|
||||||
this.logger.debug(`[socks]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`);
|
this.logger.debug(`[socks]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`);
|
||||||
tor_pool.once('instance_created', connect);
|
this.tor_pool.once('instance_created', connect);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
super(handleConnection);
|
super(handleConnection);
|
||||||
|
|
||||||
this.logger = logger || require('./winston-silent-logger');;
|
this.logger = logger || require('./winston-silent-logger');
|
||||||
|
this.tor_pool = tor_pool;
|
||||||
|
this.proxy_by_name = proxy_by_name;
|
||||||
|
|
||||||
|
let auth = socks.auth.None();
|
||||||
|
|
||||||
if (!proxy_by_name) {
|
if (!proxy_by_name) {
|
||||||
this.logger.debug(`[socks]: connecting to a specific instance by name has ben turned off`);
|
this.logger.debug(`[socks]: connecting to a specific instance by name has ben turned off`);
|
||||||
let auth = socks.auth.None();
|
|
||||||
} else {
|
} else {
|
||||||
this.logger.debug(`[socks]: connecting to a specific instance by name has ben turned on`);
|
this.logger.debug(`[socks]: connecting to a specific instance by name has ben turned on`);
|
||||||
let auth = socks.auth.UserPassword(
|
auth = socks.auth.UserPassword(this.authenticate_user);
|
||||||
(username, password, cb) => {
|
|
||||||
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.useAuth(auth);
|
this.useAuth(auth);
|
||||||
|
|
|
@ -124,11 +124,11 @@ class TorPool extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
instance_by_name(name) {
|
instance_by_name(name) {
|
||||||
return this.instances.filter((i) => i.definition.Name === name)[0];
|
return this._instances.filter((i) => i.definition.Name === name)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
instance_at(index) {
|
instance_at(index) {
|
||||||
return this.instances[index];
|
return this._instances[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
async remove(instances) {
|
async remove(instances) {
|
||||||
|
|
|
@ -64,6 +64,8 @@ class TorProcess extends EventEmitter {
|
||||||
return this._controller || null;
|
return this._controller || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get ready() { return this._ready; }
|
||||||
|
|
||||||
/* Passthrough to granax */
|
/* Passthrough to granax */
|
||||||
|
|
||||||
async new_identity() {
|
async new_identity() {
|
||||||
|
@ -133,7 +135,7 @@ class TorProcess extends EventEmitter {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.once('ready', () => {
|
this.once('ready', () => {
|
||||||
this.ready = true;
|
this._ready = true;
|
||||||
this.logger.info(`[tor-${this.instance_name}]: tor is ready`);
|
this.logger.info(`[tor-${this.instance_name}]: tor is ready`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ module.exports = {
|
||||||
"socksHost": null,
|
"socksHost": null,
|
||||||
"dnsHost": null,
|
"dnsHost": null,
|
||||||
"httpHost": null,
|
"httpHost": null,
|
||||||
|
"proxyByName": true,
|
||||||
|
"denyUnidentifedUsers": false,
|
||||||
"logLevel": "info",
|
"logLevel": "info",
|
||||||
"loadBalanceMethod": "round_robin",
|
"loadBalanceMethod": "round_robin",
|
||||||
"torConfig": {
|
"torConfig": {
|
||||||
|
|
|
@ -177,6 +177,11 @@ let argv_config =
|
||||||
alias: "torPath",
|
alias: "torPath",
|
||||||
describe: "Provide the path for the Tor executable that will be used",
|
describe: "Provide the path for the Tor executable that will be used",
|
||||||
demand: false
|
demand: false
|
||||||
|
},
|
||||||
|
n: {
|
||||||
|
alias: 'proxyByName',
|
||||||
|
describe: 'Allow connecting to a specific instance identified by the username field when connecting to a proxy',
|
||||||
|
demand: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@ const env_whitelist = [ "CONTROL_HOST",
|
||||||
'PARENT_DATA_DIRECTORIES',
|
'PARENT_DATA_DIRECTORIES',
|
||||||
'LOAD_BALANCE_METHOD',
|
'LOAD_BALANCE_METHOD',
|
||||||
"WEBSOCKET_CONTROL_PORT",
|
"WEBSOCKET_CONTROL_PORT",
|
||||||
|
"PROXY_BY_NAME",
|
||||||
|
"DENY_UNIDENTIFIED_USERS",
|
||||||
"controlHost",
|
"controlHost",
|
||||||
"torPath",
|
"torPath",
|
||||||
"instances",
|
"instances",
|
||||||
|
@ -17,7 +19,9 @@ const env_whitelist = [ "CONTROL_HOST",
|
||||||
"logLevel",
|
"logLevel",
|
||||||
'parentDataDirectories',
|
'parentDataDirectories',
|
||||||
'loadBalanceMethod',
|
'loadBalanceMethod',
|
||||||
"websocketControlPort"
|
"websocketControlPort",
|
||||||
|
"proxyByName",
|
||||||
|
"denyUnidentifedUsers"
|
||||||
];
|
];
|
||||||
|
|
||||||
module.exports = (nconf) => {
|
module.exports = (nconf) => {
|
||||||
|
|
Loading…
Reference in a new issue