hydra5 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.graffle
data/.rspec ADDED
File without changes
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in hydra.gemspec
4
+ gemspec
@@ -0,0 +1,32 @@
1
+ # Hydra5: load-balanced (multi-headed) SOCKS5 proxy
2
+
3
+ Given a list of hosts hydra5 opens a pool of SOCKS5 tunnels and routes incoming requests via a random tunnel. In effect, hydra5 is a regular SOCKS5 proxy, except that each outbound request is automatically load-balanced.
4
+
5
+ ![hydra overview](https://github.com/igrigorik/hydra5/raw/master/misc/hydra.png)
6
+
7
+ Hydra uses SSH to establish the SOCKS5 tunnels and [em-proxy](https://github.com/igrigorik/em-proxy) to provide the transparent routing for any SOCKS5 compatible client.
8
+
9
+ ## Getting started
10
+
11
+ ```
12
+ $> gem install hydra5
13
+ $> hydra5 --listen 8080 --hosts host1,host2 --key ssh_key.pub --user name --verbose
14
+ ```
15
+
16
+ ```ruby
17
+ c = Curl::Easy.new('http://jsonip.com')
18
+ c.proxy_url = 'localhost:8080'
19
+ c.proxy_type = Curl::CURLPROXY_SOCKS5
20
+
21
+ c.perform
22
+ c.body_str # => => {"ip":"72.52.131.237"}
23
+
24
+ c.perform
25
+ c.body_str # => => {"ip":"34.22.124.45"}
26
+ ```
27
+
28
+ Of course, you can also convert hydra5 into a proper HTTP proxy by deploying [privoxy](http://www.privoxy.org/) or an equivalent tool in front.
29
+
30
+ ## License
31
+
32
+ The MIT License - Copyright (c) 2011 Ilya Grigorik
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ task :default => [:spec]
5
+ task :test => [:spec]
6
+
7
+ desc "run spec tests"
8
+ RSpec::Core::RakeTask.new('spec') do |t|
9
+ t.pattern = 'spec/**_spec.rb'
10
+ end
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'hydra5'
7
+ require 'optparse'
8
+
9
+ ARGV << '--help' if ARGV.empty?
10
+
11
+ options = {}
12
+ OptionParser.new do |opts|
13
+ opts.banner = "Usage: hydra5 [options]"
14
+
15
+ opts.on("-l", "--listen PORT", Integer, "Port to listen on") do |v|
16
+ options[:listen] = v
17
+ end
18
+
19
+ opts.on("-h", "--hosts host1,host2,...", Array, "List of proxy hosts") do |v|
20
+ options[:hosts] = v
21
+ end
22
+
23
+ opts.on("-k", "--key path/to/key", String, "SSH key for the proxy hosts") do |v|
24
+ options[:key] = v
25
+ end
26
+
27
+ opts.on("-u", "--user username", String, "SSH username for the proxy hosts") do |v|
28
+ options[:user] = v
29
+ end
30
+
31
+ opts.on("-v", "--verbose", "Run in debug mode") do |v|
32
+ options[:verbose] = v
33
+ end
34
+ end.parse!
35
+
36
+ Hydra5::Proxy.new(options).start!
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "hydra5/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "hydra5"
7
+ s.version = Hydra5::VERSION
8
+ s.authors = ["Ilya Grigorik"]
9
+ s.email = ["ilya@igvita.com"]
10
+ s.homepage = "https://github.com/igrigorik/hydra5"
11
+ s.summary = "Load-balanced (multi-headed) SOCKS5 proxy"
12
+ s.description = s.summary
13
+
14
+ s.rubyforge_project = "hydra"
15
+
16
+ s.add_dependency "eventmachine", ">= 1.0.0.beta.4"
17
+ s.add_dependency "em-proxy"
18
+ s.add_development_dependency "rspec"
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
24
+ end
@@ -0,0 +1,2 @@
1
+ require "hydra5/version"
2
+ require "hydra5/proxy"
@@ -0,0 +1,73 @@
1
+ require 'em-proxy'
2
+
3
+ module Hydra5
4
+
5
+ class Logger
6
+ [:info, :error].each do |m|
7
+ define_method m do |msg|
8
+ msg = msg.join(", ") if msg.is_a? Array
9
+ puts ["Hydra5", m, msg].join(" :: ")
10
+ end
11
+ end
12
+ end
13
+
14
+ class Proxy
15
+ def initialize(options)
16
+ @listen = options.delete(:listen) || 8080
17
+ @user = options.delete(:user) || 'root'
18
+ @key = options.delete(:key)
19
+ @log = Logger.new
20
+
21
+ @hosts = options.delete(:hosts)
22
+ @hosts = @hosts.inject({}) {|h,k| h[@hosts.index(k)] = k; h}
23
+
24
+ @log.info "Establishing #{@hosts.size} SOCKS5 tunnels"
25
+ @@live = {}
26
+ @pids = []
27
+ end
28
+
29
+ def start!
30
+ EM.epoll
31
+ EM.run do
32
+ @hosts.each do |index, host|
33
+ tunnel = <<-CMD
34
+ ssh #{@user}@#{host} -D #{7000 + index} -NT
35
+ -o StrictHostKeyChecking=no
36
+ -o ServerAliveInterval=300
37
+ -o ConnectTimeout=5
38
+ -o ExitOnForwardFailure=yes
39
+ -i #{@key}
40
+ CMD
41
+
42
+ @pids << EM.system(tunnel) do |cmd, out|
43
+ @log.error ["Connection closed", out, @hosts[index]]
44
+ @@live.delete(index)
45
+
46
+ if @@live.empty?
47
+ @log.info "No live tunnels left, exiting"
48
+ exit
49
+ end
50
+ end
51
+
52
+ @@live[index] = host
53
+ end
54
+
55
+ at_exit do
56
+ pids = @pids.join(' ')
57
+ unless system("kill #{pids}")
58
+ @log.error "Could not kill all tunnels: #{pids}"
59
+ end
60
+ end
61
+
62
+ @log.info "Starting proxy on port #{@listen}"
63
+
64
+ ::Proxy.start(:host => "0.0.0.0", :port => @listen, :debug => @verbose) do |conn|
65
+ tunnel = @@live.keys.shuffle.first
66
+
67
+ puts "routing connection to #{@@live[tunnel]}"
68
+ conn.server :srv, :host => "127.0.0.1", :port => 7000 + tunnel, :relay_client => true, :relay_server => true
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,3 @@
1
+ module Hydra5
2
+ VERSION = "0.1.0"
3
+ end
Binary file
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hydra5
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ilya Grigorik
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-07 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: eventmachine
16
+ requirement: &2154144020 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.0.beta.4
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2154144020
25
+ - !ruby/object:Gem::Dependency
26
+ name: em-proxy
27
+ requirement: &2154143600 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2154143600
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &2154143140 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2154143140
47
+ description: Load-balanced (multi-headed) SOCKS5 proxy
48
+ email:
49
+ - ilya@igvita.com
50
+ executables:
51
+ - hydra5
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - .gitignore
56
+ - .rspec
57
+ - Gemfile
58
+ - README.md
59
+ - Rakefile
60
+ - bin/hydra5
61
+ - hydra5.gemspec
62
+ - lib/hydra5.rb
63
+ - lib/hydra5/proxy.rb
64
+ - lib/hydra5/version.rb
65
+ - misc/hydra.png
66
+ homepage: https://github.com/igrigorik/hydra5
67
+ licenses: []
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project: hydra
86
+ rubygems_version: 1.8.5
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: Load-balanced (multi-headed) SOCKS5 proxy
90
+ test_files: []
91
+ has_rdoc: