Skip to content

Commit 2019fed

Browse files
committed
Move to opensource
0 parents  commit 2019fed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1161
-0
lines changed

.rspec

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--color

Gemfile

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
source 'https://rubygems.org'
2+
3+
gemspec

Gemfile.lock

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
PATH
2+
remote: .
3+
specs:
4+
openvpn-http-hooks (1.0.4)
5+
6+
GEM
7+
remote: https://rubygems.org/
8+
specs:
9+
addressable (2.3.5)
10+
crack (0.4.2)
11+
safe_yaml (~> 1.0.0)
12+
diff-lcs (1.2.5)
13+
metaclass (0.0.4)
14+
mocha (1.0.0)
15+
metaclass (~> 0.0.1)
16+
rspec (2.14.1)
17+
rspec-core (~> 2.14.0)
18+
rspec-expectations (~> 2.14.0)
19+
rspec-mocks (~> 2.14.0)
20+
rspec-core (2.14.8)
21+
rspec-expectations (2.14.5)
22+
diff-lcs (>= 1.1.3, < 2.0)
23+
rspec-mocks (2.14.6)
24+
safe_yaml (1.0.1)
25+
webmock (1.17.4)
26+
addressable (>= 2.2.7)
27+
crack (>= 0.3.2)
28+
29+
PLATFORMS
30+
ruby
31+
32+
DEPENDENCIES
33+
mocha
34+
openvpn-http-hooks!
35+
rspec
36+
webmock

LICENSE

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2015 SmartVPN.biz
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in
11+
all copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

README.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#### openvpn-http-hooks
2+
3+
This ruby gem is wrapper for OpenVPN server daemon.
4+
5+
The main purpose of this gem - is to interact with remote API, located at billing system.
6+
7+
This allows to authenticate user, track his connects/disconnects, apply hooks on connect/disconnect.
8+
9+
### Hooks
10+
11+
Built-in hooks allow to implement following features for specific user:
12+
13+
* Automatic routing of I2P sites through I2P router
14+
* Automatic routing of TOR sites through TOR router
15+
* Applying selected proxy for all HTTP traffic of specific user
16+
17+
### Disclaimer
18+
19+
This is OpenSource, Free software. You may use it anyway you want. It is provided AS IS.
20+
21+
Check out LICENSE file for more info.
22+

bin/openvpn-activate

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/ruby
2+
3+
require 'openvpn-http-hooks'
4+
5+
api = Api::Activation.new
6+
api.activate

bin/openvpn-authenticate

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/ruby
2+
3+
require 'openvpn-http-hooks'
4+
5+
api_adapter_class = Api::Authentication
6+
7+
authenticator = OpenvpnPasswordAuthenticator.new(ARGV, api_adapter_class)
8+
authenticator.authenticate

bin/openvpn-connect

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/ruby
2+
3+
require 'openvpn-http-hooks'
4+
5+
connection = Api::Connect.new
6+
connection.invoke

bin/openvpn-disconnect

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/ruby
2+
3+
require 'openvpn-http-hooks'
4+
5+
connection = Api::Disconnect.new
6+
connection.invoke
7+

lib/api/activation.rb

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
require File.expand_path('../billing', __FILE__)
2+
3+
module Api
4+
class Activation < Billing
5+
6+
def activate
7+
if activated_successfully?
8+
save_auth_key
9+
else
10+
raise "Can't activate server at billing"
11+
end
12+
end
13+
14+
private
15+
16+
def activated_successfully?
17+
success_api_call?
18+
end
19+
20+
def save_auth_key
21+
key = JSON.parse(api_call_result.body)["auth_key"]
22+
File.open('/etc/openvpn/auth_key', 'w') { |file| file.write(key) }
23+
end
24+
25+
def signed_data
26+
data
27+
end
28+
29+
def data
30+
{
31+
hostname: hostname
32+
}
33+
end
34+
35+
def action
36+
"activate"
37+
end
38+
end
39+
end

lib/api/authentication.rb

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
require File.expand_path('../billing', __FILE__)
2+
3+
module Api
4+
class Authentication < Billing
5+
def initialize(login, password)
6+
@login, @password = login, password
7+
end
8+
9+
def valid_credentials?
10+
success_api_call?
11+
end
12+
13+
private
14+
15+
def data
16+
{
17+
login: @login,
18+
password: @password,
19+
hostname: hostname
20+
}
21+
end
22+
23+
def action
24+
"auth"
25+
end
26+
end
27+
end

lib/api/billing.rb

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
require "socket"
2+
require 'net/http'
3+
require 'json'
4+
require File.expand_path('../../signer', __FILE__)
5+
6+
module Api
7+
class Billing
8+
API_HOST = "api.smartvpn.biz"
9+
KEY_PATH = "/etc/openvpn/auth_key"
10+
11+
def host_with_port
12+
"http://#{API_HOST}"
13+
end
14+
15+
def auth_key
16+
File.read(KEY_PATH)
17+
end
18+
19+
def hostname
20+
Socket.gethostname
21+
end
22+
23+
def success_api_call?
24+
api_call_result.code == '200'
25+
end
26+
27+
def response
28+
JSON.parse(api_call_result.body)
29+
end
30+
31+
def api_call_result
32+
@api_result ||= Net::HTTP.post_form(uri, signed_data)
33+
end
34+
35+
def uri
36+
URI("#{host_with_port}/api/#{action}")
37+
end
38+
39+
def signed_data
40+
data.merge!({ signature: Signer.sign_hash(data, auth_key) })
41+
end
42+
end
43+
end

lib/api/connect.rb

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module Api
2+
class Connect < Connection
3+
def invoke
4+
invoke_if_valid_api_call do
5+
activate_options
6+
end
7+
end
8+
9+
def action
10+
'connect'
11+
end
12+
13+
private
14+
15+
def activate_options
16+
options.each do |code, data|
17+
data[:option_class].activate(common_name, data[:attributes])
18+
end
19+
end
20+
end
21+
end

lib/api/connection.rb

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
require File.expand_path('../billing', __FILE__)
2+
3+
module Api
4+
class Connection < Billing
5+
def invoke_if_valid_api_call(&block)
6+
yield if success_api_call?
7+
trigger_script_return
8+
end
9+
10+
def trigger_script_return
11+
if success_api_call?
12+
exit 0
13+
else
14+
exit 1
15+
end
16+
end
17+
18+
def common_name
19+
response["common_name"]
20+
end
21+
22+
def options
23+
result = {}
24+
option_codes.reduce(result) do |options_with_codes, option_code|
25+
options_with_codes[option_code] =
26+
{
27+
option_class: Option::Repository.find_by_code(option_code),
28+
attributes: attributes_for_option(option_code)
29+
}
30+
options_with_codes
31+
end
32+
result
33+
end
34+
35+
private
36+
37+
def data
38+
{
39+
hostname: hostname,
40+
traffic_in: traffic_in,
41+
traffic_out: traffic_out,
42+
login: ENV['common_name']
43+
}
44+
end
45+
46+
def option_codes
47+
response["options"]
48+
end
49+
50+
def option_attributes
51+
response["option_attributes"]
52+
end
53+
54+
def attributes_for_option(code)
55+
option_attributes[code]
56+
end
57+
58+
def traffic_in
59+
ENV['bytes_received'] || "0"
60+
end
61+
62+
def traffic_out
63+
ENV['bytes_sent'] || "0"
64+
end
65+
end
66+
end

lib/api/disconnect.rb

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module Api
2+
class Disconnect < Connection
3+
def invoke
4+
invoke_if_valid_api_call do
5+
deactivate_options
6+
end
7+
end
8+
9+
def action
10+
'disconnect'
11+
end
12+
13+
private
14+
15+
def deactivate_options
16+
options.each do |code, data|
17+
data[:option_class].deactivate(common_name, data[:attributes])
18+
end
19+
end
20+
end
21+
end

lib/exceptions/option_not_found.rb

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class OptionNotFound < StandardError
2+
end

lib/openvpn-http-hooks.rb

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
require 'api/activation'
2+
require 'api/authentication'
3+
require 'api/connection'
4+
require 'api/connect'
5+
require 'api/disconnect'
6+
7+
require 'option/base'
8+
require 'option/i2p'
9+
require 'option/proxy'
10+
require 'option/repository'
11+
12+
require 'system/openvpn_status'
13+
require 'system/openvpn_status_log_reader'
14+
require 'system/openvpn_status_log_parser'
15+
16+
require 'exceptions/option_not_found'
17+
18+
require 'openvpn_password_authenticator'
19+
require 'signer'

lib/openvpn_password_authenticator.rb

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class OpenvpnPasswordAuthenticator
2+
attr_accessor :api
3+
4+
def initialize(args, api_adapter_class)
5+
content = File.read(args[0])
6+
@login, @password = content.split("\n")
7+
@api = api_adapter_class.new(@login, @password)
8+
end
9+
10+
def authenticate
11+
if @api.valid_credentials?
12+
exit 0
13+
else
14+
exit 1
15+
end
16+
end
17+
18+
end

0 commit comments

Comments
 (0)