tormanager 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +11 -0
- data/Guardfile +18 -0
- data/LICENSE.txt +21 -0
- data/README.md +195 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/tormanager/eye/eye.tor.test.rb +34 -0
- data/lib/tormanager/eye/sugar.rb +62 -0
- data/lib/tormanager/eye/tor.god.rb +105 -0
- data/lib/tormanager/eye/tor.template.eye.rb +27 -0
- data/lib/tormanager/ip_address_control.rb +80 -0
- data/lib/tormanager/process_helper.rb +76 -0
- data/lib/tormanager/proxy.rb +30 -0
- data/lib/tormanager/tor_process.rb +193 -0
- data/lib/tormanager/version.rb +3 -0
- data/lib/tormanager.rb +6 -0
- data/tormanager.gemspec +33 -0
- metadata +191 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 04505aabbc5658c80ee5124866e5fa939a1d42d1
|
4
|
+
data.tar.gz: 7798de7e4ac6228cc2afd6e2bf68873d94fcbc82
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cf53e41f6cab0da777462c69a526e63de3dc5802fac1b07b82425d5ba66cadcd3f62b115fb948354e4dcbd9417aa15d5eb8410686558a86a9cfe381191c87726
|
7
|
+
data.tar.gz: e7828a3ae09c95e22ee3f04dbb5abcf71efc7264a024da5f6ec441e3f3f35b40cd58c0973b8f1022d784b2898cc4e029c327651cb343344edc00e26ad350ab90
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in tormanager.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem 'tor', :git => 'https://github.com/bendiken/tor-ruby.git', :ref => '08e589d17196a5dc640e7b38cb1acc5b4c5ced05'
|
7
|
+
#gem 'tor', :git => 'https://github.com/dryruby/tor.rb.git'
|
8
|
+
group :development, :test do
|
9
|
+
gem "rb-fsevent", :require => false if RUBY_PLATFORM =~ /darwin/i
|
10
|
+
gem "guard-rspec"
|
11
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
2
|
+
require "guard/rspec/dsl"
|
3
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
4
|
+
|
5
|
+
# RSpec files
|
6
|
+
rspec = dsl.rspec
|
7
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
8
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
9
|
+
watch(rspec.spec_files)
|
10
|
+
|
11
|
+
# Ruby files
|
12
|
+
ruby = dsl.ruby
|
13
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
14
|
+
|
15
|
+
#watch(%r{^spec/.+_spec\.rb$})
|
16
|
+
#watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
17
|
+
#watch('spec/spec_helper.rb') { "spec" }
|
18
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 joshweir
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
# Tor Manager
|
2
|
+
|
3
|
+
Ruby gem that provides a Tor interface with functionality:
|
4
|
+
|
5
|
+
- Start and stop and monitor a Tor process.
|
6
|
+
The Tor Process is monitored using [Eye](https://github.com/kostya/eye).
|
7
|
+
- Retrieve the current Tor IP address and get new ip address upon request.
|
8
|
+
- Proxy web client requests through Tor.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Install Tor:
|
13
|
+
|
14
|
+
`sudo apt-get install tor`
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem 'tormanager'
|
20
|
+
```
|
21
|
+
|
22
|
+
And then execute:
|
23
|
+
|
24
|
+
$ bundle
|
25
|
+
|
26
|
+
Or install it yourself as:
|
27
|
+
|
28
|
+
$ gem install tormanager
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
|
32
|
+
### Tor Process
|
33
|
+
|
34
|
+
This section shows how to start, stop and manage Tor processes.
|
35
|
+
|
36
|
+
Start a Tor process with default settings:
|
37
|
+
|
38
|
+
tor_process = TorManager::TorProcess.new
|
39
|
+
tor_process.start
|
40
|
+
|
41
|
+
#you can get the control password for the Tor process if you want:
|
42
|
+
tor_process.settings[:control_password]
|
43
|
+
|
44
|
+
By default, the Tor process will use using port `9050` and control port `50500` and
|
45
|
+
will be spawned using [Eye](https://github.com/kostya/eye) ensuring that the process remains in a healthy state.
|
46
|
+
Eye will generate a config file based on the default [template](https://github.com/joshweir/tormanager/blob/master/lib/tormanager/eye/tor.template.eye.rb).
|
47
|
+
Eye will invoke the Tor process with command which looks like this:
|
48
|
+
|
49
|
+
tor --SocksPort 9050 --ControlPort 50500 --CookieAuthentication 0 --HashedControlPassword "<hashedpassword from the :control_password>" --NewCircuitPeriod 60
|
50
|
+
|
51
|
+
Eye will restart the Tor process if a cpu percentage exceeds 10% for 3 consecutive readings (checked every 30 seconds). Eye will
|
52
|
+
restart the Tor process if its memory exceeds 200MB for 3 consecutive readings (checked every 60 seconds). By default, will do minimal logging and
|
53
|
+
will create a random `:control_password` which is used to generate the `HashedControlPassword` used to change the Tor password on request.
|
54
|
+
More information can be found in the `TorManager::TorProcess` parameters table below.
|
55
|
+
|
56
|
+
You can also spawn a Tor process with control over parameters:
|
57
|
+
|
58
|
+
tor_process =
|
59
|
+
TorManager::TorProcess.new tor_port: 9051,
|
60
|
+
control_port: 50501,
|
61
|
+
pid_dir: '/my/pid/dir',
|
62
|
+
log_dir: '/my/log/dir',
|
63
|
+
tor_data_dir: '/my/tor/datadir',
|
64
|
+
tor_new_circuit_period: 120,
|
65
|
+
max_tor_memory_usage_mb: 400,
|
66
|
+
max_tor_cpu_percentage: 15,
|
67
|
+
control_password: 'mycontrolpass',
|
68
|
+
eye_logging: true,
|
69
|
+
tor_logging: true
|
70
|
+
tor_process.start
|
71
|
+
|
72
|
+
See the table below for more info.
|
73
|
+
|
74
|
+
When done with the Tor process, `stop` it:
|
75
|
+
|
76
|
+
tor_process.stop
|
77
|
+
|
78
|
+
The following table describes the `TorManager::TorProcess` parameters:
|
79
|
+
|
80
|
+
| Parameter | Default Value | Description |
|
81
|
+
| --- | --- | --- |
|
82
|
+
| `:tor_port` | `9050` | The listening port of the Tor process. |
|
83
|
+
| `:control_port` | `50500` | The control port of the Tor process. |
|
84
|
+
| `:pid_dir` | `/tmp` | Eye will create a pid file in this location which stores the pid of the Tor process. The name of the pid file is of format: `tormanager-tor-<tor_port>-<parent_pid>.pid` **_†_**. |
|
85
|
+
| `:log_dir` | `/tmp` | If `:eye_logging` is `true`, Eye will create a log (`tormanager.eye.log`) in this location. If `:tor_logging` is `true`, Tor `stdall` is redirected to log (`tormanager-tor-<tor_port>-<parent_pid>.log` **_†_**) in this location. |
|
86
|
+
| `:tor_data_dir` | `nil` | If specified, Tor will use this directory location as the `--DataDirectory`. |
|
87
|
+
| `:tor_new_circuit_period` | `60` | In seconds, specifies the `--NewCircuitPeriod` that Tor should use. |
|
88
|
+
| `:max_tor_memory_usage_mb` | `200` | In megabytes, Eye will restart the Tor process if its memory exceeds this value for 3 consecutive readings (checked every 60 seconds). |
|
89
|
+
| `:max_tor_cpu_percentage` | `10` | Percentage value, Eye will restart the Tor process if it's cpu percentage exceeds this value for 3 consecutive readings (checked every 30 seconds). |
|
90
|
+
| `:eye_tor_config_template` | `tormanager/eye/tor.template.eye.rb` | Specify your own eye config template, it is recommended to use the default [template](https://github.com/joshweir/tormanager/blob/master/lib/tormanager/eye/tor.template.eye.rb) as your starting point. |
|
91
|
+
| `:control_password` | Randomly generated by default. | By default, will do minimal logging and will create a random `:control_password` which is used to generate the `HashedControlPassword` used to change the Tor password on request. |
|
92
|
+
| `:tor_log_switch` | `nil` | If specified, sets the Tor `--Log` switch, for example a value of `notice syslog` will add `--Log "notice syslog"` to the Tor command. |
|
93
|
+
| `:eye_logging` | `nil` | If set to `true` will enable Eye logging in the `:log_dir` location, eye log: `tormanager.eye.log` |
|
94
|
+
| `:tor_logging` | `nil` | If set to `true`, Tor `stdall` is redirected to log (`tormanager-tor-<tor_port>-<parent_pid>.log` **_†_**) in this location. |
|
95
|
+
| `:dont_remove_tor_config` | `nil` | By default, an eye configuration file is generated for the current Tor instance based on the `:eye_tor_config_template` stored in the `:log_dir` with name `tormanager.tor.<tor_port>.<parent_pid>.eye.rb` **_†_**. This generated file is removed when the Tor process is stopped by default. Setting `:dont_remove_tor_config` to `true` will not remove this file. |
|
96
|
+
|
97
|
+
**_†_** where `<tor_port>` is the `:tor_port` of the Tor process and `<parent_pid>` is the pid of the ruby process spawning the Tor process.
|
98
|
+
|
99
|
+
To stop any Tor instances that have been previously started by Tor Manager but were not stopped (say in the event of a parent process crash) **_††_**:
|
100
|
+
|
101
|
+
TorProcess.stop_obsolete_processes
|
102
|
+
|
103
|
+
Query whether Tor Manager has any Tor processes running on a particular port **_††_**:
|
104
|
+
|
105
|
+
TorProcess.tor_running_on? port: 9050
|
106
|
+
|
107
|
+
Query whether Tor Manager has any Tor processes running on a particular port associated to a particular parent ruby pid **_††_**:
|
108
|
+
|
109
|
+
TorProcess.tor_running_on? port: 9050, parent_pid: 12345
|
110
|
+
|
111
|
+
**_††_** Note that this command applies only to Tor processes that were started by Tor Manager.
|
112
|
+
Tor processes that have been started external to Tor Manager will not be impacted.
|
113
|
+
|
114
|
+
### Proxy Through Tor, Query IP Address, Change IP Address
|
115
|
+
|
116
|
+
Once you have a `TorProcess` started, you can:
|
117
|
+
|
118
|
+
- [Proxy web client requests through Tor.](#proxy-through-tor)
|
119
|
+
- [Query the current Tor endpoint IP address.](#query-ip-address)
|
120
|
+
- [Change the Tor endpoint IP address.](#change-ip-address)
|
121
|
+
|
122
|
+
The remaining examples assume that you have instantiated a `TorProcess` ie:
|
123
|
+
|
124
|
+
tor_process = TorManager::TorProcess.new
|
125
|
+
tor_process.start
|
126
|
+
|
127
|
+
#### Proxy Through Tor
|
128
|
+
|
129
|
+
tor_proxy = TorManager::Proxy.new tor_process: tor_process
|
130
|
+
tor_proxy.proxy do
|
131
|
+
tor_ip = RestClient::Request.execute(
|
132
|
+
method: :get,
|
133
|
+
url: 'http://bot.whatismyipaddress.com').to_str
|
134
|
+
end
|
135
|
+
my_ip = RestClient::Request.execute(
|
136
|
+
method: :get,
|
137
|
+
url: 'http://bot.whatismyipaddress.com').to_str
|
138
|
+
|
139
|
+
Note that in the above code the `RestClient::Request` returning `tor_ip` is routed through the Tor endpoint because it is yielded
|
140
|
+
through the `TorManager::Proxy#proxy` block. The following request returning `my_ip` will not be routed through the Tor endpoint
|
141
|
+
hence returning your current IP address.
|
142
|
+
|
143
|
+
You could route [Capybara](https://github.com/teamcapybara/capybara) requests through the proxy (just make sure you use the `:poltergeist` driver and set the proxy
|
144
|
+
to use the Tor socks proxy):
|
145
|
+
|
146
|
+
Capybara.default_driver = :poltergeist
|
147
|
+
Capybara.register_driver :poltergeist do |app|
|
148
|
+
Capybara::Poltergeist::Driver.new(app, {
|
149
|
+
:phantomjs => Phantomjs.path,
|
150
|
+
:phantomjs_options => ["--proxy-type=socks5", "--proxy=127.0.0.1:9050"]
|
151
|
+
})
|
152
|
+
end
|
153
|
+
tor_proxy = TorManager::Proxy.new tor_process: tor_process
|
154
|
+
tor_proxy.proxy do
|
155
|
+
tor_ip = Capybara.visit('http://bot.whatismyipaddress.com').text
|
156
|
+
end
|
157
|
+
|
158
|
+
#### Query IP Address
|
159
|
+
|
160
|
+
Query the Tor endpoint for the current IP address:
|
161
|
+
|
162
|
+
tor_proxy = TorManager::Proxy.new tor_process: tor_process
|
163
|
+
tor_ip_control =
|
164
|
+
TorManager::IpAddressControl.new tor_process: tor_process,
|
165
|
+
tor_proxy: tor_proxy
|
166
|
+
tor_ip_control.get_ip
|
167
|
+
|
168
|
+
When the `TorManager::IpAddressControl#get_ip` method is called, the IP address is stored in instance variable:
|
169
|
+
|
170
|
+
tor_ip_control.ip
|
171
|
+
|
172
|
+
|
173
|
+
#### Change IP Address
|
174
|
+
|
175
|
+
Get a new IP address:
|
176
|
+
|
177
|
+
tor_proxy = TorManager::Proxy.new tor_process: tor_process
|
178
|
+
tor_ip_control =
|
179
|
+
TorManager::IpAddressControl.new tor_process: tor_process,
|
180
|
+
tor_proxy: tor_proxy
|
181
|
+
tor_ip_control.get_new_ip
|
182
|
+
|
183
|
+
When the `TorManager::IpAddressControl#get_new_ip` method is called, the IP address is stored in instance variable:
|
184
|
+
|
185
|
+
tor_ip_control.ip
|
186
|
+
|
187
|
+
|
188
|
+
## Contributing
|
189
|
+
|
190
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/joshweir/tormanager.
|
191
|
+
|
192
|
+
|
193
|
+
## License
|
194
|
+
|
195
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "tormanager"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'eye'
|
2
|
+
|
3
|
+
# Adding application
|
4
|
+
Eye.application 'tormanager-tor-9250-1234' do
|
5
|
+
# All options inherits down to the config leafs.
|
6
|
+
# except `env`, which merging down
|
7
|
+
|
8
|
+
# uid "user_name" # run app as a user_name (optional) - available only on ruby >= 2.0
|
9
|
+
# gid "group_name" # run app as a group_name (optional) - available only on ruby >= 2.0
|
10
|
+
|
11
|
+
#working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
|
12
|
+
stdall '/tmp/tortrash.log' # stdout,err logs for processes by default
|
13
|
+
#env 'APP_ENV' => 'production' # global env for each processes
|
14
|
+
trigger :flapping, times: 10, within: 1.minute, retry_in: 10.minutes
|
15
|
+
check :cpu, every: 30.seconds, below: 10, times: 3 # global check for all processes
|
16
|
+
check :memory, every: 60.seconds, below: 200.megabytes, times: 3
|
17
|
+
process :sample1 do
|
18
|
+
pid_file '1.pid' # pid_path will be expanded with the working_dir
|
19
|
+
start_command "tor --SocksPort 9250 --ControlPort 52500 " +
|
20
|
+
"--CookieAuthentication 0 --HashedControlPassword \"16:3E49D6163CCA95F2605B339" +
|
21
|
+
"E07F753C8F567DE4200E33FDF4CC6B84E44\" --NewCircuitPeriod " +
|
22
|
+
"60 --DataDirectory /tmp/tor_data/9250/ --Log \"notice syslog\""
|
23
|
+
|
24
|
+
daemonize true
|
25
|
+
#stdall 'sample1.log'
|
26
|
+
end
|
27
|
+
|
28
|
+
=begin
|
29
|
+
group 'samples' do
|
30
|
+
chain grace: 5.seconds # chained start-restart with 5s interval, one by one.
|
31
|
+
|
32
|
+
end
|
33
|
+
=end
|
34
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class Numeric
|
2
|
+
# Public: Units of seconds.
|
3
|
+
def seconds
|
4
|
+
self
|
5
|
+
end
|
6
|
+
|
7
|
+
# Public: Units of seconds.
|
8
|
+
alias :second :seconds
|
9
|
+
|
10
|
+
# Public: Units of minutes (60 seconds).
|
11
|
+
def minutes
|
12
|
+
self * 60
|
13
|
+
end
|
14
|
+
|
15
|
+
# Public: Units of minutes (60 seconds).
|
16
|
+
alias :minute :minutes
|
17
|
+
|
18
|
+
# Public: Units of hours (3600 seconds).
|
19
|
+
def hours
|
20
|
+
self * 3600
|
21
|
+
end
|
22
|
+
|
23
|
+
# Public: Units of hours (3600 seconds).
|
24
|
+
alias :hour :hours
|
25
|
+
|
26
|
+
# Public: Units of days (86400 seconds).
|
27
|
+
def days
|
28
|
+
self * 86400
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Units of days (86400 seconds).
|
32
|
+
alias :day :days
|
33
|
+
|
34
|
+
# Units of kilobytes.
|
35
|
+
def kilobytes
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
# Units of kilobytes.
|
40
|
+
alias :kilobyte :kilobytes
|
41
|
+
|
42
|
+
# Units of megabytes (1024 kilobytes).
|
43
|
+
def megabytes
|
44
|
+
self * 1024
|
45
|
+
end
|
46
|
+
|
47
|
+
# Units of megabytes (1024 kilobytes).
|
48
|
+
alias :megabyte :megabytes
|
49
|
+
|
50
|
+
# Units of gigabytes (1,048,576 kilobytes).
|
51
|
+
def gigabytes
|
52
|
+
self * (1024 ** 2)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Units of gigabytes (1,048,576 kilobytes).
|
56
|
+
alias :gigabyte :gigabytes
|
57
|
+
|
58
|
+
# Units of percent. e.g. 50.percent.
|
59
|
+
def percent
|
60
|
+
self
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
opts = {}
|
4
|
+
opts[:parent_id] = ARGV[0]
|
5
|
+
opts[:tor_port] = ARGV[1] || 9050
|
6
|
+
opts[:control_port] = ARGV[2] || 50500
|
7
|
+
opts[:pid_dir] = ARGV[3] || "/tmp"
|
8
|
+
opts[:log_dir] = ARGV[4] || "/tmp"
|
9
|
+
opts[:tor_data_dir] = ARGV[5] || "/tmp/tor_data/"
|
10
|
+
opts[:tor_new_circuit_period] = ARGV[6] || 60
|
11
|
+
opts[:max_tor_memory_usage_mb] = ARGV[7] || 200
|
12
|
+
opts[:max_tor_cpu_percentage] = ARGV[8] || 10
|
13
|
+
opts[:max_tor_memory_usage_times] = [3,5]
|
14
|
+
opts[:max_tor_cpu_percentage_times] = [3,5]
|
15
|
+
|
16
|
+
raise 'parent_id is required' unless opts[:parent_id]
|
17
|
+
|
18
|
+
module God
|
19
|
+
module Behaviors
|
20
|
+
class WaitBehavior < Behavior
|
21
|
+
attr_accessor :delay
|
22
|
+
|
23
|
+
def after_start
|
24
|
+
sleep delay.to_i if delay.to_i > 0
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
God.watch do |w|
|
31
|
+
the_name = "tormanager-tor-#{opts[:tor_port]}-#{opts[:parent_id]}"
|
32
|
+
w.name = the_name
|
33
|
+
w.start = "tor --SocksPort #{opts[:tor_port]} --ControlPort #{opts[:control_port]} " +
|
34
|
+
"--CookieAuthentication 0 --HashedControlPassword \"16:3E49D6163CCA95F2605B339" +
|
35
|
+
"E07F753C8F567DE4200E33FDF4CC6B84E44\" --NewCircuitPeriod " +
|
36
|
+
"#{opts[:tor_new_circuit_period]} --DataDirectory " +
|
37
|
+
File.join(opts[:tor_data_dir], opts[:tor_port]) + " --Log \"notice syslog\""
|
38
|
+
#w.pid_file = File.join(opts[:pid_dir], "#{the_name}.pid")
|
39
|
+
#w.log = File.join(opts[:log_dir], "#{the_name}.log") if opts[:log_dir].length > 0
|
40
|
+
w.keepalive
|
41
|
+
|
42
|
+
# clean pid files before start if necessary
|
43
|
+
w.behavior(:clean_pid_file)
|
44
|
+
w.behavior(:wait_behavior) do |b|
|
45
|
+
b.delay = 10
|
46
|
+
end
|
47
|
+
|
48
|
+
=begin
|
49
|
+
# determine the state on startup
|
50
|
+
w.transition(:init, { true => :up, false => :start }) do |on|
|
51
|
+
sleep 5
|
52
|
+
on.condition(:process_running) do |c|
|
53
|
+
c.interval = 5
|
54
|
+
c.running = true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# determine when process has finished starting
|
59
|
+
w.transition([:start, :restart], :up) do |on|
|
60
|
+
on.condition(:process_running) do |c|
|
61
|
+
c.interval = 20
|
62
|
+
c.running = true
|
63
|
+
end
|
64
|
+
|
65
|
+
# failsafe
|
66
|
+
on.condition(:tries) do |c|
|
67
|
+
c.interval = 20
|
68
|
+
c.times = 5
|
69
|
+
c.transition = :start
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# start if process is not running
|
74
|
+
#w.transition(:up, :start) do |on|
|
75
|
+
# on.condition(:process_exits) do |c|
|
76
|
+
# c.interval = 20
|
77
|
+
# end
|
78
|
+
#end
|
79
|
+
|
80
|
+
# restart if memory or cpu is too high
|
81
|
+
w.transition(:up, :restart) do |on|
|
82
|
+
on.condition(:memory_usage) do |c|
|
83
|
+
c.interval = 20
|
84
|
+
c.above = opts[:max_tor_memory_usage_mb]
|
85
|
+
c.times = opts[:max_tor_memory_usage_times]
|
86
|
+
end
|
87
|
+
|
88
|
+
on.condition(:cpu_usage) do |c|
|
89
|
+
c.interval = 10
|
90
|
+
c.above = opts[:max_tor_cpu_percentage]
|
91
|
+
c.times = opts[:max_tor_cpu_percentage_times]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# lifecycle
|
96
|
+
w.lifecycle do |on|
|
97
|
+
on.condition(:flapping) do |c|
|
98
|
+
c.to_state = [:start, :restart]
|
99
|
+
c.times = 5
|
100
|
+
c.within = 5.minute
|
101
|
+
c.transition = :unmonitored
|
102
|
+
end
|
103
|
+
end
|
104
|
+
=end
|
105
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'eye'
|
2
|
+
|
3
|
+
if %w(true 1).include?('[[[eye_logging]]]')
|
4
|
+
Eye.config do
|
5
|
+
logger File.join('[[[log_dir]]]', 'tormanager.eye.log')
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
Eye.application 'tormanager-tor-[[[tor_port]]]-[[[parent_pid]]]' do
|
10
|
+
stdall File.join('[[[log_dir]]]', 'tormanager-tor-[[[tor_port]]]-[[[parent_pid]]].log') if %w(true 1).include?('[[[tor_logging]]]')
|
11
|
+
trigger :flapping, times: 10, within: 1.minute, retry_in: 10.minutes
|
12
|
+
check :cpu, every: 30.seconds, below: [[[max_tor_cpu_percentage]]], times: 3
|
13
|
+
check :memory, every: 60.seconds, below: [[[max_tor_memory_usage_mb]]].megabytes, times: 3
|
14
|
+
process :tor do
|
15
|
+
pid_file File.join('[[[pid_dir]]]', 'tormanager-tor-[[[tor_port]]]-[[[parent_pid]]].pid')
|
16
|
+
start_command "tor --SocksPort [[[tor_port]]] --ControlPort [[[control_port]]] " +
|
17
|
+
"--CookieAuthentication 0 --HashedControlPassword \"[[[hashed_control_password]]]\" --NewCircuitPeriod " +
|
18
|
+
"[[[tor_new_circuit_period]]] " +
|
19
|
+
('[[[tor_data_dir]]]'.length > 0 ?
|
20
|
+
"--DataDirectory #{File.join('[[[tor_data_dir]]]',
|
21
|
+
'[[[tor_port]]]')} " :
|
22
|
+
"") +
|
23
|
+
('[[[tor_log_switch]]]'.length > 0 ?
|
24
|
+
"--Log \"[[[tor_log_switch]]]\" " : "")
|
25
|
+
daemonize true
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'tor'
|
2
|
+
require 'rest-client'
|
3
|
+
|
4
|
+
module TorManager
|
5
|
+
class IpAddressControl
|
6
|
+
attr_accessor :ip
|
7
|
+
|
8
|
+
def initialize params={}
|
9
|
+
@tor_process = params.fetch(:tor_process, nil)
|
10
|
+
@tor_proxy = params.fetch(:tor_proxy, nil)
|
11
|
+
@ip = nil
|
12
|
+
@endpoint_change_attempts = 5
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_ip
|
16
|
+
ensure_tor_is_available
|
17
|
+
@ip = tor_endpoint_ip
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_new_ip
|
21
|
+
ensure_tor_is_available
|
22
|
+
get_new_tor_endpoint_ip
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def ensure_tor_is_available
|
28
|
+
raise "Cannot proceed, Tor is not running on port " +
|
29
|
+
"#{@tor_process.settings[:tor_port]}" unless
|
30
|
+
TorProcess.tor_running_on? port: @tor_process.settings[:tor_port],
|
31
|
+
parent_pid: @tor_process.settings[:parent_pid]
|
32
|
+
end
|
33
|
+
|
34
|
+
def tor_endpoint_ip
|
35
|
+
try_getting_endpoint_ip_restart_tor_and_retry_on_fail attempts: 2
|
36
|
+
rescue Exception => ex
|
37
|
+
puts "Error getting ip: #{ex.to_s}"
|
38
|
+
return nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def try_getting_endpoint_ip_restart_tor_and_retry_on_fail params={}
|
42
|
+
ip = nil
|
43
|
+
(params[:attempts] || 2).times do |attempt|
|
44
|
+
begin
|
45
|
+
@tor_proxy.proxy do
|
46
|
+
ip = RestClient::Request
|
47
|
+
.execute(method: :get,
|
48
|
+
url: 'http://bot.whatismyipaddress.com')
|
49
|
+
.to_str
|
50
|
+
end
|
51
|
+
break if ip
|
52
|
+
rescue Exception => ex
|
53
|
+
@tor_process.stop
|
54
|
+
@tor_process.start
|
55
|
+
end
|
56
|
+
end
|
57
|
+
ip
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_new_tor_endpoint_ip
|
61
|
+
@endpoint_change_attempts.times do |i|
|
62
|
+
tor_switch_endpoint
|
63
|
+
new_ip = tor_endpoint_ip
|
64
|
+
if new_ip.to_s.length > 0 && new_ip != @ip
|
65
|
+
@ip = new_ip
|
66
|
+
break
|
67
|
+
end
|
68
|
+
end
|
69
|
+
@ip
|
70
|
+
end
|
71
|
+
|
72
|
+
def tor_switch_endpoint
|
73
|
+
Tor::Controller.connect(:port => @tor_process.settings[:control_port]) do |tor|
|
74
|
+
tor.authenticate("")
|
75
|
+
tor.signal("newnym")
|
76
|
+
sleep 10
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module TorManager
|
4
|
+
class ProcessHelper
|
5
|
+
class << self
|
6
|
+
def query_process query
|
7
|
+
return [] unless query
|
8
|
+
query_process_bash_cmd(query).split("\n").map{ |query_output_line|
|
9
|
+
get_pid_from_query_process_output_line(query_output_line)
|
10
|
+
}.compact
|
11
|
+
end
|
12
|
+
|
13
|
+
def kill_process pids
|
14
|
+
to_array(pids).each do |pid|
|
15
|
+
try_to_kill pid: pid, attempts: 5
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def process_pid_running? pid
|
20
|
+
begin
|
21
|
+
return false if pid.to_s == ''.freeze
|
22
|
+
ipid = pid.to_i
|
23
|
+
return false if ipid <= 0
|
24
|
+
Process.kill(0, ipid)
|
25
|
+
return true
|
26
|
+
rescue
|
27
|
+
return false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def port_is_open? port
|
32
|
+
begin
|
33
|
+
server = TCPServer.new('127.0.0.1', port)
|
34
|
+
server.close
|
35
|
+
return true
|
36
|
+
rescue Errno::EADDRINUSE;
|
37
|
+
return false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def query_process_bash_cmd query
|
44
|
+
`ps -ef | #{query_grep_pipe_chain(query)} | grep -v grep`
|
45
|
+
end
|
46
|
+
|
47
|
+
def query_grep_pipe_chain query
|
48
|
+
to_array(query)
|
49
|
+
.map{|q| "grep '#{q}'"}
|
50
|
+
.join(' | ')
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_pid_from_query_process_output_line query_output_line
|
54
|
+
output_parts = query_output_line.gsub(/\s\s+/, ' ').strip.split
|
55
|
+
output_parts.size >= 3 && output_parts[1].to_i > 0 ?
|
56
|
+
output_parts[1].to_i : nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_array v
|
60
|
+
(v.kind_of?(Array) ? v : [v])
|
61
|
+
end
|
62
|
+
|
63
|
+
def try_to_kill params={}
|
64
|
+
pid = params.fetch(:pid, nil)
|
65
|
+
return unless pid && process_pid_running?(pid)
|
66
|
+
params.fetch(:attempts, 5).times do |k|
|
67
|
+
k < 3 ? Process.kill('TERM', pid) :
|
68
|
+
Process.kill('KILL', pid)
|
69
|
+
sleep 0.5
|
70
|
+
break unless process_pid_running? pid
|
71
|
+
raise "Couldnt kill pid: #{pid}" if k >= 4
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'socksify'
|
2
|
+
|
3
|
+
module TorManager
|
4
|
+
class Proxy
|
5
|
+
#Socksify::debug = true
|
6
|
+
|
7
|
+
def initialize params={}
|
8
|
+
@tor_process = params.fetch(:tor_process, nil)
|
9
|
+
end
|
10
|
+
|
11
|
+
def proxy
|
12
|
+
enable_socks_server
|
13
|
+
yield.tap { disable_socks_server }
|
14
|
+
ensure
|
15
|
+
disable_socks_server
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def enable_socks_server
|
21
|
+
TCPSocket::socks_server = "127.0.0.1"
|
22
|
+
TCPSocket::socks_port = @tor_process.settings[:tor_port]
|
23
|
+
end
|
24
|
+
|
25
|
+
def disable_socks_server
|
26
|
+
TCPSocket::socks_server = nil
|
27
|
+
TCPSocket::socks_port = nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
require 'eye'
|
2
|
+
require 'eyemanager'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'securerandom'
|
5
|
+
|
6
|
+
module TorManager
|
7
|
+
class TorProcess
|
8
|
+
attr_accessor :settings
|
9
|
+
|
10
|
+
def initialize params={}
|
11
|
+
@settings = {}
|
12
|
+
@settings[:tor_port] = params.fetch(:tor_port, 9050)
|
13
|
+
@settings[:control_port] = params.fetch(:control_port, 50500)
|
14
|
+
@settings[:pid_dir] = params.fetch(:pid_dir, '/tmp'.freeze)
|
15
|
+
@settings[:log_dir] = params.fetch(:log_dir, '/tmp'.freeze)
|
16
|
+
@settings[:tor_data_dir] = params.fetch(:tor_data_dir, nil)
|
17
|
+
@settings[:tor_new_circuit_period] = params.fetch(:tor_new_circuit_period, 60)
|
18
|
+
@settings[:max_tor_memory_usage_mb] = params.fetch(:max_tor_memory_usage, 200)
|
19
|
+
@settings[:max_tor_cpu_percentage] = params.fetch(:max_tor_cpu_percentage, 10)
|
20
|
+
@settings[:eye_tor_config_template] =
|
21
|
+
params.fetch(:eye_tor_config_template,
|
22
|
+
File.join(File.dirname(__dir__),'tormanager/eye/tor.template.eye.rb'))
|
23
|
+
@settings[:parent_pid] = Process.pid
|
24
|
+
@settings[:control_password] = params.fetch(:control_password, random_password)
|
25
|
+
@settings[:hashed_control_password] =
|
26
|
+
tor_hash_password_from(@settings[:control_password])
|
27
|
+
@settings[:tor_log_switch] = params.fetch(:tor_log_switch, nil)
|
28
|
+
@settings[:eye_logging] = params.fetch(:eye_logging, nil)
|
29
|
+
@settings[:tor_logging] = params.fetch(:tor_logging, nil)
|
30
|
+
@settings[:dont_remove_tor_config] = params.fetch(:dont_remove_tor_config, nil)
|
31
|
+
end
|
32
|
+
|
33
|
+
def start
|
34
|
+
prepare_tor_start_and_monitor if tor_ports_are_open?
|
35
|
+
end
|
36
|
+
|
37
|
+
def stop
|
38
|
+
EyeManager.stop application: eye_app_name, process: 'tor'
|
39
|
+
remove_eye_tor_config unless @settings[:dont_remove_tor_config]
|
40
|
+
ensure_tor_is_down
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
def stop_obsolete_processes
|
45
|
+
(EyeManager.list_apps || []).each do |app|
|
46
|
+
EyeManager.stop(application: app, process: 'tor') unless
|
47
|
+
ProcessHelper.process_pid_running? pid_of_tor_eye_process(app)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def tor_running_on? params={}
|
52
|
+
is_running = false
|
53
|
+
(EyeManager.list_apps || []).each do |app|
|
54
|
+
if port_and_or_pid_matches_eye_tor_name?(app, params) &&
|
55
|
+
EyeManager.status(application: app,
|
56
|
+
process: 'tor') == 'up'
|
57
|
+
is_running = true
|
58
|
+
break
|
59
|
+
end
|
60
|
+
end
|
61
|
+
is_running
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def port_and_or_pid_matches_eye_tor_name? app, params={}
|
67
|
+
(params[:port] || params[:parent_pid]) &&
|
68
|
+
(!params[:port] || port_of_tor_eye_process(app).to_i == params[:port].to_i) &&
|
69
|
+
(!params[:parent_pid] || pid_of_tor_eye_process(app).to_i == params[:parent_pid].to_i)
|
70
|
+
end
|
71
|
+
|
72
|
+
def pid_of_tor_eye_process app
|
73
|
+
app.to_s.split('-').last
|
74
|
+
end
|
75
|
+
|
76
|
+
def port_of_tor_eye_process app
|
77
|
+
app_name_split = app.to_s.split('-')
|
78
|
+
app_name_split.length >= 3 ?
|
79
|
+
app_name_split[2].to_i : nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def tor_ports_are_open?
|
86
|
+
tor_port_is_open? &&
|
87
|
+
control_port_is_open?
|
88
|
+
end
|
89
|
+
|
90
|
+
def tor_port_is_open?
|
91
|
+
raise "Cannot spawn Tor process as port " +
|
92
|
+
"#{@settings[:tor_port]} is in use" unless
|
93
|
+
ProcessHelper.port_is_open?(@settings[:tor_port])
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
97
|
+
def control_port_is_open?
|
98
|
+
raise "Cannot spawn Tor process as control port " +
|
99
|
+
"#{@settings[:control_port]} is in use" unless
|
100
|
+
ProcessHelper.port_is_open?(@settings[:control_port])
|
101
|
+
true
|
102
|
+
end
|
103
|
+
|
104
|
+
def prepare_tor_start_and_monitor
|
105
|
+
build_eye_config_from_template
|
106
|
+
make_dirs
|
107
|
+
start_tor_and_monitor
|
108
|
+
end
|
109
|
+
|
110
|
+
def build_eye_config_from_template
|
111
|
+
File.open(eye_config_filename, "w") do |file|
|
112
|
+
file.puts read_eye_tor_config_template_and_substitute_keywords
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def eye_config_filename
|
117
|
+
@eye_config_filename || File.join(@settings[:log_dir],
|
118
|
+
"tormanager.tor.#{@settings[:tor_port]}.#{Process.pid}.eye.rb")
|
119
|
+
end
|
120
|
+
|
121
|
+
def eye_app_name
|
122
|
+
@eye_app_name || "tormanager-tor-#{@settings[:tor_port]}-#{Process.pid}"
|
123
|
+
end
|
124
|
+
|
125
|
+
def read_eye_tor_config_template_and_substitute_keywords
|
126
|
+
text = File.read(@settings[:eye_tor_config_template])
|
127
|
+
eye_tor_config_template_substitution_keywords.each do |keyword|
|
128
|
+
text = text.gsub(/\[\[\[#{keyword}\]\]\]/, @settings[keyword.to_sym].to_s)
|
129
|
+
end
|
130
|
+
text
|
131
|
+
end
|
132
|
+
|
133
|
+
def eye_tor_config_template_substitution_keywords
|
134
|
+
remove_settings_that_are_not_eye_tor_config_template_keywords(
|
135
|
+
@settings.keys.map(&:to_s))
|
136
|
+
end
|
137
|
+
|
138
|
+
def remove_settings_that_are_not_eye_tor_config_template_keywords keywords
|
139
|
+
keywords - ['eye_tor_config_template', 'control_password', 'dont_remove_tor_config']
|
140
|
+
end
|
141
|
+
|
142
|
+
def make_dirs
|
143
|
+
[@settings[:log_dir], @settings[:pid_dir],
|
144
|
+
@settings[:tor_data_dir]].each do |path|
|
145
|
+
FileUtils.mkpath(path) if path && !File.exists?(path)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def start_tor_and_monitor
|
150
|
+
EyeManager.start config: eye_config_filename,
|
151
|
+
application: eye_app_name
|
152
|
+
ensure_tor_is_up
|
153
|
+
end
|
154
|
+
|
155
|
+
def ensure_tor_is_up
|
156
|
+
10.times do |i|
|
157
|
+
break if
|
158
|
+
EyeManager.status(
|
159
|
+
application: eye_app_name,
|
160
|
+
process: 'tor') == 'up'
|
161
|
+
sleep 2
|
162
|
+
raise "Tor didnt start up after 20 seconds! See log: " +
|
163
|
+
"#{File.join(@settings[:log_dir],
|
164
|
+
eye_app_name + ".log")}" if i >= 9
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def ensure_tor_is_down
|
169
|
+
10.times do |i|
|
170
|
+
tor_status = EyeManager.status(
|
171
|
+
application: eye_app_name,
|
172
|
+
process: 'tor')
|
173
|
+
break if ['unknown','unmonitored'].include?(tor_status)
|
174
|
+
sleep 2
|
175
|
+
raise "Tor didnt stop after 20 seconds! Last status: #{tor_status} See log: " +
|
176
|
+
"#{File.join(@settings[:log_dir],
|
177
|
+
eye_app_name + ".log")}" if i >= 9
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def remove_eye_tor_config
|
182
|
+
File.delete(eye_config_filename) if File.exists?(eye_config_filename)
|
183
|
+
end
|
184
|
+
|
185
|
+
def random_password
|
186
|
+
SecureRandom.random_number(36**12).to_s(36).rjust(12, "0")
|
187
|
+
end
|
188
|
+
|
189
|
+
def tor_hash_password_from password
|
190
|
+
`tor --quiet --hash-password '#{password}'`.strip
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
data/lib/tormanager.rb
ADDED
data/tormanager.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'tormanager/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "tormanager"
|
8
|
+
spec.version = TorManager::VERSION
|
9
|
+
spec.authors = ["joshweir"]
|
10
|
+
spec.email = ["joshua.weir@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Start, stop, monitor, control and proxy through a Tor process using Ruby.}
|
13
|
+
#spec.description = %q{TODO: Write a longer description or delete this line.}
|
14
|
+
spec.homepage = "https://github.com/joshweir/tormanager"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
27
|
+
spec.add_development_dependency "rspec-rails", "~> 3.5"
|
28
|
+
spec.add_dependency "rest-client"
|
29
|
+
spec.add_dependency "eye"
|
30
|
+
spec.add_dependency "eyemanager"
|
31
|
+
spec.add_dependency "gem-release"
|
32
|
+
spec.add_dependency "socksify"
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tormanager
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- joshweir
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-08-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.14'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.14'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.5'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.5'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rest-client
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: eye
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: eyemanager
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: gem-release
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: socksify
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description:
|
140
|
+
email:
|
141
|
+
- joshua.weir@gmail.com
|
142
|
+
executables: []
|
143
|
+
extensions: []
|
144
|
+
extra_rdoc_files: []
|
145
|
+
files:
|
146
|
+
- ".gitignore"
|
147
|
+
- ".rspec"
|
148
|
+
- ".travis.yml"
|
149
|
+
- Gemfile
|
150
|
+
- Guardfile
|
151
|
+
- LICENSE.txt
|
152
|
+
- README.md
|
153
|
+
- Rakefile
|
154
|
+
- bin/console
|
155
|
+
- bin/setup
|
156
|
+
- lib/tormanager.rb
|
157
|
+
- lib/tormanager/eye/eye.tor.test.rb
|
158
|
+
- lib/tormanager/eye/sugar.rb
|
159
|
+
- lib/tormanager/eye/tor.god.rb
|
160
|
+
- lib/tormanager/eye/tor.template.eye.rb
|
161
|
+
- lib/tormanager/ip_address_control.rb
|
162
|
+
- lib/tormanager/process_helper.rb
|
163
|
+
- lib/tormanager/proxy.rb
|
164
|
+
- lib/tormanager/tor_process.rb
|
165
|
+
- lib/tormanager/version.rb
|
166
|
+
- tormanager.gemspec
|
167
|
+
homepage: https://github.com/joshweir/tormanager
|
168
|
+
licenses:
|
169
|
+
- MIT
|
170
|
+
metadata: {}
|
171
|
+
post_install_message:
|
172
|
+
rdoc_options: []
|
173
|
+
require_paths:
|
174
|
+
- lib
|
175
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
180
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
|
+
requirements:
|
182
|
+
- - ">="
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
version: '0'
|
185
|
+
requirements: []
|
186
|
+
rubyforge_project:
|
187
|
+
rubygems_version: 2.5.1
|
188
|
+
signing_key:
|
189
|
+
specification_version: 4
|
190
|
+
summary: Start, stop, monitor, control and proxy through a Tor process using Ruby.
|
191
|
+
test_files: []
|