Merge branch 'master' of github.com:progrium/localtunnel
This commit is contained in:
commit
3ee78a5f7a
|
@ -0,0 +1,3 @@
|
|||
.idea
|
||||
.idea/*
|
||||
.idea/**/*
|
33
README
33
README
|
@ -1,33 +0,0 @@
|
|||
localtunnel -- instant public tunnel for local web servers
|
||||
|
||||
Dependencies: Ruby (with libopenssl) and Rubygems
|
||||
|
||||
Install: sudo gem install localtunnel
|
||||
|
||||
Usage: localtunnel [options] <localport>
|
||||
-k, --key FILE upload a public key for authentication
|
||||
|
||||
localtunnel is a client to a free and open source reverse tunneling service
|
||||
made specifically for web traffic. It's intended to be used to temporarily
|
||||
expose local web servers to the greater Internet for debugging, unit tests,
|
||||
demos, etc.
|
||||
|
||||
This is how you make your local port 8080 public:
|
||||
|
||||
$ localtunnel 8080
|
||||
Port 8080 is now publicly accessible from http://8bv2.localtunnel.com ...
|
||||
|
||||
Using localtunnel is comparable to using SSH reverse/remote port forwarding on
|
||||
a remote host that has GatewayPorts enabled, but without all the configuration
|
||||
or the need of a host. The localtunnel command works with a server component
|
||||
that is running on localtunnel.com, which is provided as a free service.
|
||||
|
||||
If you haven't run the command before, you'll need to upload a public key to
|
||||
authenticate. You do this like so:
|
||||
|
||||
$ localtunnel -k ~/.ssh/id_rsa.pub 8080
|
||||
|
||||
After that, you shouldn't have to use -k again.
|
||||
|
||||
The tunnel remains open for as long as the command is running. The tunnel is
|
||||
closed if the command exits.
|
|
@ -0,0 +1,57 @@
|
|||
= localtunnel -- instant public tunnel for local web servers
|
||||
|
||||
Dependencies:
|
||||
- Ruby (with libopenssl) and Rubygems.
|
||||
- A public key.
|
||||
|
||||
To get the dependencies, type:
|
||||
|
||||
sudo apt-get install ruby ruby1.8-dev rubygems1.8 libopenssl-ruby
|
||||
|
||||
If you have never made a public key, then run:
|
||||
|
||||
ssh-keygen
|
||||
|
||||
== Install
|
||||
|
||||
sudo gem install localtunnel
|
||||
|
||||
or to get the source:
|
||||
|
||||
git clone http://github.com/progrium/localtunnel.git
|
||||
|
||||
|
||||
== Usage
|
||||
|
||||
localtunnel [options] <localport>
|
||||
-k, --key FILE upload a public key for authentication
|
||||
|
||||
Localtunnel is a client to a free and open source reverse tunneling
|
||||
service made specifically for web traffic. It's intended to be used to
|
||||
temporarily expose local web servers to the greater Internet for
|
||||
debugging, unit tests, demos, etc.
|
||||
|
||||
This is how you make your local port 8080 public:
|
||||
|
||||
$ localtunnel 8080
|
||||
|
||||
Port 8080 is now publicly accessible from http://8bv2.localtunnel.com ...
|
||||
|
||||
Using localtunnel is comparable to using SSH reverse/remote port
|
||||
forwarding on a remote host that has GatewayPorts enabled, but without
|
||||
all the configuration or the need of a host. The localtunnel command
|
||||
works with a server component that is running on localtunnel.com,
|
||||
which is provided as a free service.
|
||||
|
||||
If have never run localtunnel before, you'll need to upload a public
|
||||
key to authenticate. You do this once:
|
||||
|
||||
$ localtunnel -k ~/.ssh/id_rsa.pub 8080
|
||||
|
||||
After that, you shouldn't have to use -k again.
|
||||
|
||||
Localtunnel can be started before or after the local web server. It
|
||||
tunnels through to the url given in that status message "publicly
|
||||
accessible from..." for as long as the command is running. The tunnel
|
||||
is closed if the command exits.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env ruby
|
||||
# Copyright (c) 2010 Jeff Lindsay
|
||||
#
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation
|
||||
# files (the "Software"), to deal in the Software without
|
||||
|
@ -9,10 +9,10 @@
|
|||
# copies of the Software, and to permit persons to whom the
|
||||
# Software is furnished to do so, subject to the following
|
||||
# conditions:
|
||||
#
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
@ -22,88 +22,10 @@
|
|||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
require 'rubygems'
|
||||
require 'net/ssh'
|
||||
require 'net/ssh/gateway'
|
||||
require 'net/http'
|
||||
require 'uri'
|
||||
require 'optparse'
|
||||
require 'json'
|
||||
base_dir = File.dirname(File.expand_path(__FILE__))
|
||||
|
||||
def register_tunnel(key=nil)
|
||||
url = URI.parse("http://open.localtunnel.com/")
|
||||
if key
|
||||
resp = JSON.parse(Net::HTTP.post_form(url, {"key" => key}).body)
|
||||
else
|
||||
resp = JSON.parse(Net::HTTP.get(url))
|
||||
end
|
||||
if resp.has_key? 'error'
|
||||
puts " [Error] #{resp['error']}"
|
||||
exit
|
||||
end
|
||||
return resp
|
||||
rescue
|
||||
puts " [Error] Unable to register tunnel. Perhaps service is down?"
|
||||
exit
|
||||
end
|
||||
|
||||
def start_tunnel(port, tunnel)
|
||||
gateway = Net::SSH::Gateway.new(tunnel['host'], tunnel['user'])
|
||||
gateway.open_remote(port.to_i, '127.0.0.1', tunnel['through_port'].to_i) do |rp,rh|
|
||||
puts " " << tunnel['banner'] if tunnel.has_key? 'banner'
|
||||
puts " Port #{port} is now publicly accessible from http://#{tunnel['host']} ..."
|
||||
begin
|
||||
sleep 1 while true
|
||||
rescue Interrupt
|
||||
gateway.close_remote(rp, rh)
|
||||
exit
|
||||
end
|
||||
end
|
||||
rescue Net::SSH::AuthenticationFailed
|
||||
possible_key = Dir[File.expand_path('~/.ssh/*.pub')].first
|
||||
puts " Failed to authenticate. If this is your first tunnel, you need to"
|
||||
puts " upload a public key using the -k option. Try this:\n\n"
|
||||
puts " localtunnel -k #{possible_key ? possible_key : '~/path/to/key'} #{port}"
|
||||
exit
|
||||
end
|
||||
|
||||
# http://groups.google.com/group/capistrano/browse_thread/thread/455c0c8a6faa9cc8?pli=1
|
||||
class Net::SSH::Gateway
|
||||
# Opens a SSH tunnel from a port on a remote host to a given host and port
|
||||
# on the local side
|
||||
# (equivalent to openssh -R parameter)
|
||||
def open_remote(port, host, remote_port, remote_host = "127.0.0.1")
|
||||
ensure_open!
|
||||
|
||||
@session_mutex.synchronize do
|
||||
@session.forward.remote(port, host, remote_port, remote_host)
|
||||
end
|
||||
|
||||
if block_given?
|
||||
begin
|
||||
yield [remote_port, remote_host]
|
||||
ensure
|
||||
close_remote(remote_port, remote_host)
|
||||
end
|
||||
else
|
||||
return [remote_port, remote_host]
|
||||
end
|
||||
rescue Errno::EADDRINUSE
|
||||
retry
|
||||
end
|
||||
|
||||
# Cancels port-forwarding over an open port that was previously opened via
|
||||
# #open_remote.
|
||||
def close_remote(port, host = "127.0.0.1")
|
||||
ensure_open!
|
||||
|
||||
@session_mutex.synchronize do
|
||||
@session.forward.cancel_remote(port, host)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
### Main
|
||||
require base_dir + '/../lib/local_tunnel'
|
||||
require base_dir + '/../lib/net_ssh_gateway_patch'
|
||||
|
||||
key = nil
|
||||
options = OptionParser.new do |o|
|
||||
|
@ -121,4 +43,6 @@ unless local_port
|
|||
exit
|
||||
end
|
||||
|
||||
start_tunnel(local_port, register_tunnel(key))
|
||||
x = LocalTunnel.new(local_port, key)
|
||||
x.register_tunnel
|
||||
x.start_tunnel
|
|
@ -0,0 +1,59 @@
|
|||
require 'rubygems'
|
||||
require 'net/ssh'
|
||||
require 'net/ssh/gateway'
|
||||
require 'net/http'
|
||||
require 'uri'
|
||||
require 'optparse'
|
||||
require 'json'
|
||||
|
||||
class LocalTunnel
|
||||
|
||||
attr_accessor :port, :key, :host
|
||||
|
||||
def initialize(port, key)
|
||||
@port = port
|
||||
@key = key
|
||||
@host = ""
|
||||
end
|
||||
|
||||
def register_tunnel(key=@key)
|
||||
url = URI.parse("http://open.localtunnel.com/")
|
||||
if key
|
||||
resp = JSON.parse(Net::HTTP.post_form(url, {"key" => key}).body)
|
||||
else
|
||||
resp = JSON.parse(Net::HTTP.get(url))
|
||||
end
|
||||
if resp.has_key? 'error'
|
||||
puts " [Error] #{resp['error']}"
|
||||
exit
|
||||
end
|
||||
@host = resp['host']
|
||||
@tunnel = resp
|
||||
return resp
|
||||
rescue
|
||||
puts " [Error] Unable to register tunnel. Perhaps service is down?"
|
||||
exit
|
||||
end
|
||||
|
||||
def start_tunnel
|
||||
port = @port
|
||||
tunnel = @tunnel
|
||||
gateway = Net::SSH::Gateway.new(tunnel['host'], tunnel['user'])
|
||||
gateway.open_remote(port.to_i, '127.0.0.1', tunnel['through_port'].to_i) do |rp,rh|
|
||||
puts " " << tunnel['banner'] if tunnel.has_key? 'banner'
|
||||
puts " Port #{port} is now publicly accessible from http://#{tunnel['host']} ..."
|
||||
begin
|
||||
sleep 1 while true
|
||||
rescue Interrupt
|
||||
gateway.close_remote(rp, rh)
|
||||
exit
|
||||
end
|
||||
end
|
||||
rescue Net::SSH::AuthenticationFailed
|
||||
possible_key = Dir[File.expand_path('~/.ssh/*.pub')].first
|
||||
puts " Failed to authenticate. If this is your first tunnel, you need to"
|
||||
puts " upload a public key using the -k option. Try this:\n\n"
|
||||
puts " localtunnel -k #{possible_key ? possible_key : '~/path/to/key'} #{port}"
|
||||
exit
|
||||
end
|
||||
end
|
|
@ -0,0 +1,43 @@
|
|||
require 'rubygems'
|
||||
require 'net/ssh'
|
||||
require 'net/ssh/gateway'
|
||||
require 'net/http'
|
||||
require 'uri'
|
||||
require 'optparse'
|
||||
require 'json'
|
||||
|
||||
# http://groups.google.com/group/capistrano/browse_thread/thread/455c0c8a6faa9cc8?pli=1
|
||||
class Net::SSH::Gateway
|
||||
# Opens a SSH tunnel from a port on a remote host to a given host and port
|
||||
# on the local side
|
||||
# (equivalent to openssh -R parameter)
|
||||
def open_remote(port, host, remote_port, remote_host = "127.0.0.1")
|
||||
ensure_open!
|
||||
|
||||
@session_mutex.synchronize do
|
||||
@session.forward.remote(port, host, remote_port, remote_host)
|
||||
end
|
||||
|
||||
if block_given?
|
||||
begin
|
||||
yield [remote_port, remote_host]
|
||||
ensure
|
||||
close_remote(remote_port, remote_host)
|
||||
end
|
||||
else
|
||||
return [remote_port, remote_host]
|
||||
end
|
||||
rescue Errno::EADDRINUSE
|
||||
retry
|
||||
end
|
||||
|
||||
# Cancels port-forwarding over an open port that was previously opened via
|
||||
# #open_remote.
|
||||
def close_remote(port, host = "127.0.0.1")
|
||||
ensure_open!
|
||||
|
||||
@session_mutex.synchronize do
|
||||
@session.forward.cancel_remote(port, host)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,3 +1,7 @@
|
|||
try:
|
||||
from twisted.internet import pollreactor
|
||||
pollreactor.install()
|
||||
except: pass
|
||||
from twisted.internet import protocol, reactor, defer, task
|
||||
from twisted.web import http, proxy, resource, server
|
||||
from twisted.python import log
|
||||
|
@ -21,7 +25,7 @@ def port_available(port):
|
|||
except socket.error:
|
||||
return True
|
||||
|
||||
def baseN(num,b=36,numerals="0123456789abcdefghijklmnopqrstuvwxyz"):
|
||||
def baseN(num,b=32,numerals="23456789abcdefghijkmnpqrstuvwxyz"):
|
||||
return ((num == 0) and "0" ) or (baseN(num // b, b).lstrip("0") + numerals[num % b])
|
||||
|
||||
class LocalTunnelReverseProxy(proxy.ReverseProxyResource):
|
||||
|
@ -99,4 +103,4 @@ class LocalTunnelReverseProxy(proxy.ReverseProxyResource):
|
|||
|
||||
log.startLogging(sys.stdout)
|
||||
reactor.listenTCP(80, server.Site(LocalTunnelReverseProxy(SSH_USER)))
|
||||
reactor.run()
|
||||
reactor.run()
|
||||
|
|
Loading…
Reference in New Issue