v1.3.0: add inverse ssh proxy
This commit is contained in:
		
							parent
							
								
									eba2b4e5b2
								
							
						
					
					
						commit
						0361e5762d
					
				
							
								
								
									
										15
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								README.md
									
									
									
									
									
								
							| @ -55,8 +55,9 @@ sclient [flags] <remote> <local> | ||||
| ``` | ||||
| 
 | ||||
| * flags | ||||
|   * -k, --insecure ignore invalid TLS (SSL/HTTPS) certificates | ||||
|   * --servername <string> spoof SNI (to disable use IP as <remote> and do not use this option) | ||||
|   * `-k, --insecure` ignore invalid TLS (SSL/HTTPS) certificates | ||||
|   * `--servername <string>` spoof SNI (to disable use IP as <remote> and do not use this option) | ||||
|   * `--ssh proxy` proxy ssh over https (_inverse_ ssh proxy, like stunnel) | ||||
| * remote | ||||
|   * must have servername (i.e. example.com) | ||||
|   * port is optional (default is 443) | ||||
| @ -85,7 +86,7 @@ Ignore a bad TLS/SSL/HTTPS certificate and connect anyway. | ||||
| sclient -k badtls.telebit.cloud:443 localhost:3000 | ||||
| ``` | ||||
| 
 | ||||
| Reading from stdin | ||||
| ### Reading from stdin | ||||
| 
 | ||||
| ```bash | ||||
| sclient telebit.cloud:443 - | ||||
| @ -95,7 +96,13 @@ sclient telebit.cloud:443 - | ||||
| sclient telebit.cloud:443 - </path/to/file | ||||
| ``` | ||||
| 
 | ||||
| Piping | ||||
| ### ssh over https | ||||
| 
 | ||||
| ```bash | ||||
| sclient --ssh user@telebit.cloud | ||||
| ``` | ||||
| 
 | ||||
| ### Piping | ||||
| 
 | ||||
| ```bash | ||||
| printf "GET / HTTP/1.1\r\nHost: telebit.cloud\r\n\r\n" | sclient telebit.cloud:443 | ||||
|  | ||||
| @ -8,6 +8,7 @@ var localAddress; | ||||
| var localPort; | ||||
| var rejectUnauthorized; | ||||
| var servername; | ||||
| var sshProxy; | ||||
| 
 | ||||
| function usage() { | ||||
|   console.info(""); | ||||
| @ -38,6 +39,14 @@ function parseFlags() { | ||||
|       return true; | ||||
|     } | ||||
|   }); | ||||
|   process.argv.some(function (arg, i) { | ||||
|     if (/^--?ssh$/.test(arg)) { | ||||
|       sshProxy = true; | ||||
|       process.argv.splice(i, 1); | ||||
|       process.argv[3] = '$'; | ||||
|       return true; | ||||
|     } | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| parseFlags(); | ||||
| @ -45,6 +54,15 @@ parseFlags(); | ||||
| remote = (process.argv[2]||'').split(':'); | ||||
| local = (process.argv[3]||'').split(':'); | ||||
| 
 | ||||
| if ('ssh' === remote[0]) { | ||||
|   sshProxy = true; | ||||
|   remote = local; | ||||
|   local = ['$']; | ||||
| } else if ('ssh' === local[0]) { | ||||
|   sshProxy = true; | ||||
|   local[0] = '$'; | ||||
| } | ||||
| 
 | ||||
| // arg 0 is node
 | ||||
| // arg 1 is sclient
 | ||||
| // arg 2 is remote
 | ||||
| @ -72,23 +90,57 @@ if (local[0] === String(parseInt(local[0], 10))) { | ||||
|   localPort = parseInt(local[0], 10); | ||||
|   localAddress = 'localhost'; | ||||
| } else { | ||||
|   localAddress = local[0]; // potentially '-' or '|'
 | ||||
|   localAddress = local[0]; // potentially '-' or '|' or '$'
 | ||||
|   localPort = parseInt(local[1], 10); | ||||
| } | ||||
| 
 | ||||
| if ('-' === localAddress || '|' === localAddress) { | ||||
|   // no need for port
 | ||||
| } else if (!localPort) { | ||||
|   usage(); | ||||
|   return; | ||||
| } | ||||
| 
 | ||||
| var rparts = remote[0].split('@'); | ||||
| var username = rparts[1] ? (rparts[0] + '@') : ''; | ||||
| var opts = { | ||||
|   remoteAddr: remote[0] | ||||
|   remoteAddr: rparts[1] || rparts[0] | ||||
| , remotePort: remote[1] || 443 | ||||
| , localAddress: localAddress | ||||
| , localPort: localPort | ||||
| , rejectUnauthorized: rejectUnauthorized | ||||
| , servername: servername | ||||
| , stdin: null | ||||
| , stdout: null | ||||
| }; | ||||
| require('../')(opts); | ||||
| 
 | ||||
| if ('-' === localAddress || '|' === localAddress) { | ||||
|   opts.stdin = process.stdin; | ||||
|   opts.stdout = process.stdout; | ||||
|   // no need for port
 | ||||
| } else if ('$' === localAddress) { | ||||
|   sshProxy = true; | ||||
|   opts.localAddress = 'localhost'; | ||||
|   opts.localPort = 0; // choose at random
 | ||||
| } else if (!localPort) { | ||||
|   usage(); | ||||
|   return; | ||||
| } | ||||
| 
 | ||||
| var emitter = require('../')(opts); | ||||
| emitter.once('listening', function (opts) { | ||||
|   var port = opts.localPort; | ||||
|   console.info('[listening] ' + opts.remoteAddr + ":" + opts.remotePort | ||||
|     + " <= " + opts.localAddress + ":" + opts.localPort); | ||||
| 
 | ||||
|   if (sshProxy) { | ||||
|     // TODO choose at random and connect to ssh after test
 | ||||
|     var spawn = require('child_process').spawn; | ||||
|     var ssh = spawn('ssh', [ | ||||
|       username + 'localhost' | ||||
|     , '-p', port | ||||
|     // we're _inverse_ proxying ssh, so we must alias the serveranem and ignore the IP
 | ||||
|     , '-o', 'HostKeyAlias=' + opts.remoteAddr | ||||
|     , '-o', 'CheckHostIP=no' | ||||
|     ], { stdio: 'inherit' }); | ||||
|     ssh.on('exit', function () { | ||||
|       console.info('shutting down...'); | ||||
|     }); | ||||
|     ssh.on('close', function () { | ||||
|       opts.server.close(); | ||||
|     }); | ||||
|   } | ||||
| }); | ||||
|  | ||||
							
								
								
									
										24
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								index.js
									
									
									
									
									
								
							| @ -3,7 +3,7 @@ | ||||
| var net = require('net'); | ||||
| var tls = require('tls'); | ||||
| 
 | ||||
| function listenForConns(opts) { | ||||
| function listenForConns(emitter, opts) { | ||||
|   function pipeConn(c, out) { | ||||
|     var sclient = tls.connect({ | ||||
|       servername: opts.remoteAddr, host: opts.remoteAddr, port: opts.remotePort | ||||
| @ -28,7 +28,7 @@ function listenForConns(opts) { | ||||
|   } | ||||
| 
 | ||||
|   if ('-' === opts.localAddress || '|' === opts.localAddress) { | ||||
|     pipeConn(process.stdin, process.stdout); | ||||
|     pipeConn(opts.stdin, opts.stdout); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
| @ -40,12 +40,15 @@ function listenForConns(opts) { | ||||
|     host: opts.localAddress | ||||
|   , port: opts.localPort | ||||
|   }, function () { | ||||
|     console.info('[listening] ' + opts.remoteAddr + ":" + opts.remotePort | ||||
|       + " <= " + opts.localAddress + ":" + opts.localPort); | ||||
|     opts.localPort = this.address().port; | ||||
|     opts.server = this; | ||||
|     emitter.emit('listening', opts); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| function testConn(opts) { | ||||
|   var emitter = new (require('events').EventEmitter)(); | ||||
| 
 | ||||
|   // Test connection first
 | ||||
|   var tlsOpts = { | ||||
|     host: opts.remoteAddr, port: opts.remotePort | ||||
| @ -53,15 +56,24 @@ function testConn(opts) { | ||||
|   }; | ||||
|   if (opts.servername) { | ||||
|     tlsOpts.servername = opts.servername; | ||||
|   } else if (/^[\w\.\-]+\.[a-z]{2,}$/i.test(opts.remoteAddr)) { | ||||
|     tlsOpts.servername = opts.remoteAddr.toLowerCase(); | ||||
|   } | ||||
|   if (opts.alpn) { | ||||
|     tlsOpts.ALPNProtocols = [ 'http', 'h2' ]; | ||||
|   } | ||||
|   var tlsSock = tls.connect(tlsOpts, function () { | ||||
|     setTimeout(function () { | ||||
|       tlsSock.end(); | ||||
|     listenForConns(opts); | ||||
|       listenForConns(emitter, opts); | ||||
|     }, 200); | ||||
|   }); | ||||
|   tlsSock.on('error', function (err) { | ||||
|     console.warn("[warn] '" + opts.remoteAddr + ":" + opts.remotePort + "' may not be accepting connections: ", err.toString(), '\n'); | ||||
|     listenForConns(opts); | ||||
|     listenForConns(emitter, opts); | ||||
|   }); | ||||
| 
 | ||||
|   return emitter; | ||||
| } | ||||
| 
 | ||||
| module.exports = testConn; | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "sclient", | ||||
|   "version": "1.2.2", | ||||
|   "version": "1.3.0", | ||||
|   "description": "Secure Client for exposing TLS (aka SSL) secured services as plain-text connections locally. Also ideal for multiplexing a single port with multiple protocols using SNI.", | ||||
|   "main": "index.js", | ||||
|   "homepage": "https://telebit.cloud/sclient/", | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user