hydra5 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: