tormanager 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.
- 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: []
|