2016-09-30 22:03:41 +00:00
#!/usr/bin/env node
( function ( ) {
'use strict' ;
var pkg = require ( '../package.json' ) ;
var program = require ( 'commander' ) ;
var stunneld = require ( '../wstunneld.js' ) ;
2017-04-05 02:31:58 +00:00
var greenlock = require ( 'greenlock' ) ;
function collectServernames ( val , memo ) {
2017-09-07 00:41:26 +00:00
var lowerCase = val . split ( /,/ ) . map ( function ( servername ) {
return servername . toLowerCase ( ) ;
2017-04-05 02:31:58 +00:00
} ) ;
2017-09-07 00:41:26 +00:00
return memo . concat ( lowerCase ) ;
2017-04-05 02:31:58 +00:00
}
2016-09-30 22:03:41 +00:00
function collectProxies ( val , memo ) {
var vals = val . split ( /,/g ) ;
vals . map ( function ( location ) {
// http:john.example.com:3000
// http://john.example.com:3000
var parts = location . split ( ':' ) ;
2016-10-01 06:39:20 +00:00
if ( 1 === parts . length ) {
parts [ 1 ] = parts [ 0 ] ;
parts [ 0 ] = 'wss' ;
}
if ( 2 === parts . length ) {
if ( /\./ . test ( parts [ 0 ] ) ) {
parts [ 2 ] = parts [ 1 ] ;
parts [ 1 ] = parts [ 0 ] ;
parts [ 0 ] = 'wss' ;
}
if ( ! /\./ . test ( parts [ 1 ] ) ) {
throw new Error ( "bad --serve option Example: wss://tunnel.example.com:1337" ) ;
}
}
2016-09-30 22:03:41 +00:00
parts [ 0 ] = parts [ 0 ] . toLowerCase ( ) ;
parts [ 1 ] = parts [ 1 ] . toLowerCase ( ) . replace ( /(\/\/)?/ , '' ) || '*' ;
parts [ 2 ] = parseInt ( parts [ 2 ] , 10 ) || 0 ;
if ( ! parts [ 2 ] ) {
// TODO grab OS list of standard ports?
2016-10-01 06:39:20 +00:00
if ( - 1 !== [ 'ws' , 'http' ] . indexOf ( parts [ 0 ] ) ) {
//parts[2] = 80;
2016-09-30 22:03:41 +00:00
}
2016-10-01 06:39:20 +00:00
else if ( - 1 !== [ 'wss' , 'https' ] . indexOf ( parts [ 0 ] ) ) {
//parts[2] = 443;
2016-09-30 22:03:41 +00:00
}
else {
throw new Error ( "port must be specified - ex: tls:*:1337" ) ;
}
}
return {
protocol : parts [ 0 ]
, hostname : parts [ 1 ]
, port : parts [ 2 ]
} ;
} ) . forEach ( function ( val ) {
memo . push ( val ) ;
} ) ;
return memo ;
}
2016-10-01 06:39:20 +00:00
function collectPorts ( val , memo ) {
2017-09-07 00:41:26 +00:00
return memo . concat ( val . split ( /,/g ) . map ( Number ) . filter ( Boolean ) ) ;
2016-10-01 06:39:20 +00:00
}
2016-09-30 22:03:41 +00:00
program
. version ( pkg . version )
2017-04-05 02:31:58 +00:00
. option ( '--agree-tos' , "Accept the Daplie and Let's Encrypt Terms of Service" )
. option ( '--email <EMAIL>' , "Email to use for Daplie and Let's Encrypt accounts" )
2016-10-01 06:39:20 +00:00
. option ( '--serve <URL>' , 'comma separated list of <proto>:<//><servername>:<port> to which matching incoming http and https should forward (reverse proxy). Ex: https://john.example.com,tls:*:1337' , collectProxies , [ ] )
. option ( '--ports <PORT>' , 'comma separated list of ports on which to listen. Ex: 80,443,1337' , collectPorts , [ ] )
2017-04-05 02:31:58 +00:00
. option ( '--servernames <STRING>' , 'comma separated list of servernames to use for the admin interface. Ex: tunnel.example.com,tunnel.example.net' , collectServernames , [ ] )
2016-09-30 22:03:41 +00:00
. option ( '--secret <STRING>' , 'the same secret used by stunneld (used for JWT authentication)' )
. parse ( process . argv )
;
2017-04-04 18:22:39 +00:00
var portsMap = { } ;
var servernamesMap = { } ;
2016-10-01 05:52:37 +00:00
program . serve . forEach ( function ( proxy ) {
2017-04-04 18:22:39 +00:00
servernamesMap [ proxy . hostname ] = true ;
2016-10-01 06:39:20 +00:00
if ( proxy . port ) {
2017-04-04 18:22:39 +00:00
portsMap [ proxy . port ] = true ;
2016-10-01 06:39:20 +00:00
}
2016-09-30 22:03:41 +00:00
} ) ;
2017-04-05 16:46:17 +00:00
program . servernames . forEach ( function ( name ) {
servernamesMap [ name ] = true ;
} ) ;
2017-04-04 18:22:39 +00:00
program . ports . forEach ( function ( port ) {
portsMap [ port ] = true ;
} ) ;
2016-09-30 22:03:41 +00:00
2017-04-05 16:46:17 +00:00
program . servernames = Object . keys ( servernamesMap ) ;
if ( ! program . servernames . length ) {
2018-04-24 16:34:20 +00:00
throw new Error ( 'You must give this server at least one servername for its admin interface. Example:\n\n\t--servernames tunnel.example.com,tunnel.example.net' ) ;
2017-04-05 16:46:17 +00:00
}
program . ports = Object . keys ( portsMap ) ;
2017-04-07 00:34:33 +00:00
if ( ! program . ports . length ) {
2017-04-05 16:46:17 +00:00
program . ports = [ 80 , 443 ] ;
2016-10-01 06:39:20 +00:00
}
2016-10-01 05:52:37 +00:00
if ( ! program . secret ) {
// TODO randomly generate and store in file?
2017-04-05 02:31:58 +00:00
console . warn ( "[SECURITY] you must provide --secret '" + require ( 'crypto' ) . randomBytes ( 16 ) . toString ( 'hex' ) + "'" ) ;
process . exit ( 1 ) ;
return ;
2016-10-01 05:52:37 +00:00
}
2017-04-04 18:22:39 +00:00
// TODO letsencrypt
2017-04-25 20:32:46 +00:00
program . tlsOptions = require ( 'localhost.daplie.me-certificates' ) . merge ( { } ) ;
2017-04-05 08:13:03 +00:00
2017-04-05 02:31:58 +00:00
function approveDomains ( opts , certs , cb ) {
// This is where you check your database and associated
// email addresses with domains and agreements and such
// The domains being approved for the first time are listed in opts.domains
// Certs being renewed are listed in certs.altnames
if ( certs ) {
opts . domains = certs . altnames ;
}
else {
2017-04-05 08:13:03 +00:00
if ( - 1 !== program . servernames . indexOf ( opts . domain ) ) {
opts . email = program . email ;
opts . agreeTos = program . agreeTos ;
}
2017-04-05 02:31:58 +00:00
}
// NOTE: you can also change other options such as `challengeType` and `challenge`
// opts.challengeType = 'http-01';
// opts.challenge = require('le-challenge-fs').create({});
cb ( null , { options : opts , certs : certs } ) ;
}
2017-04-05 08:13:03 +00:00
if ( ! program . email || ! program . agreeTos ) {
console . error ( "You didn't specify --email <EMAIL> and --agree-tos" ) ;
console . error ( "(required for ACME / Let's Encrypt / Greenlock TLS/SSL certs)" ) ;
console . error ( "" ) ;
}
2017-04-05 08:18:35 +00:00
else {
program . greenlock = greenlock . create ( {
2017-04-05 02:31:58 +00:00
2018-04-24 16:34:20 +00:00
version : 'draft-11'
, server : 'https://acme-v02.api.letsencrypt.org/directory'
2017-04-05 02:31:58 +00:00
2017-04-05 08:18:35 +00:00
, challenges : {
// TODO dns-01
'http-01' : require ( 'le-challenge-fs' ) . create ( { webrootPath : '/tmp/acme-challenges' } )
}
2017-04-05 02:31:58 +00:00
2017-04-05 08:18:35 +00:00
, store : require ( 'le-store-certbot' ) . create ( { webrootPath : '/tmp/acme-challenges' } )
2017-04-05 02:31:58 +00:00
2017-04-05 08:18:35 +00:00
, email : program . email
2017-04-05 02:31:58 +00:00
2017-04-05 08:18:35 +00:00
, agreeTos : program . agreeTos
2017-04-05 02:31:58 +00:00
2017-04-05 08:18:35 +00:00
, approveDomains : approveDomains
2017-04-05 08:13:03 +00:00
2017-04-05 08:18:35 +00:00
//, approvedDomains: program.servernames
2017-04-05 02:31:58 +00:00
2017-04-05 08:18:35 +00:00
} ) ;
}
//program.tlsOptions.SNICallback = program.greenlock.httpsOptions.SNICallback;
2017-04-05 08:13:03 +00:00
/ *
program . middleware = program . greenlock . middleware ( function ( req , res ) {
res . end ( 'Hello, World!' ) ;
2017-04-05 02:31:58 +00:00
} ) ;
* /
require ( '../handlers' ) . create ( program ) ; // adds directly to program for now...
2016-10-01 06:39:20 +00:00
//require('cluster-store').create().then(function (store) {
//program.store = store;
2016-10-01 05:52:37 +00:00
2017-04-05 02:31:58 +00:00
var net = require ( 'net' ) ;
var netConnHandlers = stunneld . create ( program ) ; // { tcp, ws }
var WebSocketServer = require ( 'ws' ) . Server ;
var wss = new WebSocketServer ( { server : ( program . httpTunnelServer || program . httpServer ) } ) ;
wss . on ( 'connection' , netConnHandlers . ws ) ;
program . ports . forEach ( function ( port ) {
2017-09-08 17:24:30 +00:00
var tcp3000 = net . createServer ( ) ;
2017-04-05 02:31:58 +00:00
tcp3000 . listen ( port , function ( ) {
console . log ( 'listening on ' + port ) ;
} ) ;
tcp3000 . on ( 'connection' , netConnHandlers . tcp ) ;
} ) ;
2016-10-01 06:39:20 +00:00
//});
2016-09-30 22:03:41 +00:00
} ( ) ) ;