Can now connect directly to an instance via http

This commit is contained in:
Zachary Boyd 2018-09-09 23:44:24 -04:00
parent 4553bcc82b
commit ece4d4ea02
9 changed files with 149 additions and 27 deletions

View file

@ -17,7 +17,6 @@
"devDependencies": {
"chai": "^4.1.2",
"mocha": "^5.2.0",
"proxy-agent": "^3.0.1",
"request": "^2.79.0",
"request-promise": "^4.2.2"
},
@ -37,6 +36,7 @@
"socksv5": "git+https://github.com/znetstar/socksv5.git#431e541390314adbbe765650d8d810ec1df38d8a",
"temp": "^0.8.3",
"winston": "^3.0.0-rc5",
"yargs": "^11.0.0"
"yargs": "^11.0.0",
"proxy-agent": "^3.0.1"
}
}

View file

@ -144,14 +144,14 @@ class ControlServer {
}
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);
this.logger.info(`[socks]: listening on socks5://${hostname}:${port}`);
this.socksServer;
}
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);
this.logger.info(`[http]: listening on http://${hostname}:${port}`);
this.httpServer;

View file

@ -4,7 +4,7 @@ const { Server } = http;
const Promise = require('bluebird');
const socks = require('socksv5');
const SocksProxyAgent = require('socks-proxy-agent');
const ProxyAgent = require('proxy-agent');
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) => {
if (!this.authenticate_user_http(req, res))
return;
let { instance } = req;
let url = URL.parse(req.url);
url.port = url.port || 80;
@ -52,7 +101,7 @@ class HTTPServer extends Server {
port: url.port,
path: url.path,
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.on('data', (chunk) => {
res.write(chunk);
@ -83,8 +132,17 @@ class HTTPServer extends Server {
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());
} else {
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) => {
if (!this.authenticate_user_connect(req, inbound_socket))
return;
let { instance } = req;
let hostname = req.url.split(':').shift();
let port = Number(req.url.split(':').pop());
@ -135,11 +198,21 @@ class HTTPServer extends Server {
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 {
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.tor_pool = tor_pool;
this.proxy_by_name = proxy_by_name;
}
};

View file

@ -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) {
let handleConnection = (info, accept, deny) => {
let inbound_socket = accept(true);
var outbound_socket;
let instance;
if (inbound_socket.user)
instance = inbound_socket.user;
let outbound_socket;
let buffer = [];
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 {
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);
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) {
this.logger.debug(`[socks]: connecting to a specific instance by name has ben turned off`);
let auth = socks.auth.None();
} else {
this.logger.debug(`[socks]: connecting to a specific instance by name has ben turned on`);
let auth = socks.auth.UserPassword(
(username, password, cb) => {
}
);
auth = socks.auth.UserPassword(this.authenticate_user);
}
this.useAuth(auth);

View file

@ -124,11 +124,11 @@ class TorPool extends EventEmitter {
}
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) {
return this.instances[index];
return this._instances[index];
}
async remove(instances) {

View file

@ -64,6 +64,8 @@ class TorProcess extends EventEmitter {
return this._controller || null;
}
get ready() { return this._ready; }
/* Passthrough to granax */
async new_identity() {
@ -133,7 +135,7 @@ class TorProcess extends EventEmitter {
});
this.once('ready', () => {
this.ready = true;
this._ready = true;
this.logger.info(`[tor-${this.instance_name}]: tor is ready`);
});

View file

@ -9,6 +9,8 @@ module.exports = {
"socksHost": null,
"dnsHost": null,
"httpHost": null,
"proxyByName": true,
"denyUnidentifedUsers": false,
"logLevel": "info",
"loadBalanceMethod": "round_robin",
"torConfig": {

View file

@ -177,6 +177,11 @@ let argv_config =
alias: "torPath",
describe: "Provide the path for the Tor executable that will be used",
demand: false
},
n: {
alias: 'proxyByName',
describe: 'Allow connecting to a specific instance identified by the username field when connecting to a proxy',
demand: false
}
});

View file

@ -8,6 +8,8 @@ const env_whitelist = [ "CONTROL_HOST",
'PARENT_DATA_DIRECTORIES',
'LOAD_BALANCE_METHOD',
"WEBSOCKET_CONTROL_PORT",
"PROXY_BY_NAME",
"DENY_UNIDENTIFIED_USERS",
"controlHost",
"torPath",
"instances",
@ -17,7 +19,9 @@ const env_whitelist = [ "CONTROL_HOST",
"logLevel",
'parentDataDirectories',
'loadBalanceMethod',
"websocketControlPort"
"websocketControlPort",
"proxyByName",
"denyUnidentifedUsers"
];
module.exports = (nconf) => {