Merge branch 'master' of github.com:progrium/localtunnel

This commit is contained in:
Jeff Lindsay 2011-02-23 13:49:45 -08:00
commit 3ee78a5f7a
7 changed files with 177 additions and 120 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.idea
.idea/*
.idea/**/*

33
README
View File

@ -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.

57
README.rdoc Normal file
View File

@ -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.

View File

@ -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

59
lib/local_tunnel.rb Normal file
View File

@ -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

View File

@ -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

View File

@ -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()