securevpn-xyz-http-hooks 1.3.3

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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +39 -0
  5. data/LICENSE +19 -0
  6. data/README.md +22 -0
  7. data/bin/openvpn-activate +6 -0
  8. data/bin/openvpn-authenticate +8 -0
  9. data/bin/openvpn-connect +6 -0
  10. data/bin/openvpn-disconnect +7 -0
  11. data/lib/api/activation.rb +37 -0
  12. data/lib/api/authentication.rb +23 -0
  13. data/lib/api/billing.rb +55 -0
  14. data/lib/api/connect.rb +21 -0
  15. data/lib/api/connection.rb +70 -0
  16. data/lib/api/disconnect.rb +21 -0
  17. data/lib/exceptions/option_not_found.rb +2 -0
  18. data/lib/openvpn-http-hooks.rb +19 -0
  19. data/lib/openvpn_password_authenticator.rb +18 -0
  20. data/lib/option/base.rb +30 -0
  21. data/lib/option/i2p.rb +28 -0
  22. data/lib/option/proxy.rb +66 -0
  23. data/lib/option/repository.rb +18 -0
  24. data/lib/signer.rb +7 -0
  25. data/lib/system/openvpn_status.rb +19 -0
  26. data/lib/system/openvpn_status_log_parser.rb +30 -0
  27. data/lib/system/openvpn_status_log_reader.rb +29 -0
  28. data/openvpn-http-hooks.gemspec +16 -0
  29. data/spec/fixtures/active_connections.txt +10 -0
  30. data/spec/fixtures/multiple_connections.txt +11 -0
  31. data/spec/lib/api/activation_spec.rb +30 -0
  32. data/spec/lib/api/authentication_spec.rb +29 -0
  33. data/spec/lib/api/billing_spec.rb +71 -0
  34. data/spec/lib/api/connect_spec.rb +40 -0
  35. data/spec/lib/api/connection_spec.rb +59 -0
  36. data/spec/lib/api/disconnect_spec.rb +40 -0
  37. data/spec/lib/openvpn_password_authenticator_spec.rb +52 -0
  38. data/spec/lib/option/base_spec.rb +26 -0
  39. data/spec/lib/option/i2p_spec.rb +19 -0
  40. data/spec/lib/option/proxy_spec.rb +32 -0
  41. data/spec/lib/option/repository_spec.rb +25 -0
  42. data/spec/lib/signer_spec.rb +21 -0
  43. data/spec/lib/system/openvpn_status_log_parser_spec.rb +25 -0
  44. data/spec/lib/system/openvpn_status_log_reader_spec.rb +33 -0
  45. data/spec/lib/system/openvpn_status_spec.rb +22 -0
  46. data/spec/spec_helper.rb +9 -0
  47. metadata +133 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b44cd788085a4c09799e288dcd99b93276321417
4
+ data.tar.gz: def5da481d4276d9e4734e3c7fa45be26cf05c3b
5
+ SHA512:
6
+ metadata.gz: ba5b11c6e6fe537655872815b1c61139ea26a77ce7045b6b41cd286aa4a880446bb6c22a9eeace2950bd3658a0e7b51100712d9f7beef5f98c58b2e9083b45b2
7
+ data.tar.gz: 0ae7d404cb044d19da4bc3f013620d196a58166a95a760fd7d055ad6d85782d6e708b9631b4bf5d09eb420940b661a87d0c1b9dcf36a1f4c7098db37c5eb3e1e
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,39 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ securevpn-xyz-http-hooks (1.1.0)
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
+ rspec
35
+ securevpn-xyz-http-hooks!
36
+ webmock
37
+
38
+ BUNDLED WITH
39
+ 1.10.6
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2015 securevpn.xyz
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.
data/README.md ADDED
@@ -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
+
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'openvpn-http-hooks'
4
+
5
+ api = Api::Activation.new
6
+ api.activate
@@ -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
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'openvpn-http-hooks'
4
+
5
+ connection = Api::Connect.new
6
+ connection.invoke
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'openvpn-http-hooks'
4
+
5
+ connection = Api::Disconnect.new
6
+ connection.invoke
7
+
@@ -0,0 +1,37 @@
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
+ "hostname=#{hostname}"
31
+ end
32
+
33
+ def action
34
+ "activate"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
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
+ "login=#{@login}&password=#{@password}&hostname=#{hostname}"
17
+ end
18
+
19
+ def action
20
+ "auth"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,55 @@
1
+ require "socket"
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'uri'
5
+ require 'json'
6
+
7
+ require File.expand_path('../../signer', __FILE__)
8
+
9
+ module Api
10
+ class Billing
11
+ API_HOST = "api.securevpn.xyz"
12
+ KEY_PATH = "/etc/openvpn/auth_key"
13
+
14
+ def host_with_port
15
+ "https://#{API_HOST}"
16
+ end
17
+
18
+ def auth_key
19
+ File.read(KEY_PATH)
20
+ end
21
+
22
+ def hostname
23
+ Socket.gethostname
24
+ end
25
+
26
+ def success_api_call?
27
+ api_call_result.code == '200'
28
+ end
29
+
30
+ def response
31
+ JSON.parse(api_call_result.body)
32
+ end
33
+
34
+ def api_call_result
35
+ http = Net::HTTP.new(uri.host, uri.port)
36
+ http.use_ssl = true
37
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
38
+
39
+ request = Net::HTTP::Post.new(uri.path)
40
+ request["content-type"] = 'application/x-www-form-urlencoded'
41
+ request.body = signed_data
42
+
43
+ @api_result ||= http.request(request)
44
+ end
45
+
46
+ def uri
47
+ URI("#{host_with_port}/api/#{action}")
48
+ end
49
+
50
+ def signed_data
51
+ data
52
+ #data + "&signature=#{Signer.sign_hash(data, auth_key)}"
53
+ end
54
+ end
55
+ end
@@ -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_array|
17
+ data[:option_class].activate(common_name, data[:attributes])
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,70 @@
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
+ "hostname=#{hostname}&traffic_in=#{traffic_in}&traffic_out=#{traffic_out}&login=#{response["common_name"]}"
39
+ end
40
+
41
+ def data_array
42
+ {
43
+ hostname: hostname,
44
+ traffic_in: traffic_in,
45
+ traffic_out: traffic_out,
46
+ login: ENV['common_name']
47
+ }
48
+ end
49
+
50
+ def option_codes
51
+ response["options"]
52
+ end
53
+
54
+ def option_attributes
55
+ response["option_attributes"]
56
+ end
57
+
58
+ def attributes_for_option(code)
59
+ option_attributes[code]
60
+ end
61
+
62
+ def traffic_in
63
+ ENV['bytes_received'] || "0"
64
+ end
65
+
66
+ def traffic_out
67
+ ENV['bytes_sent'] || "0"
68
+ end
69
+ end
70
+ end
@@ -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
@@ -0,0 +1,2 @@
1
+ class OptionNotFound < StandardError
2
+ end
@@ -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'
@@ -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,0 +1,30 @@
1
+ module Option
2
+ class Base
3
+ attr_accessor :common_name, :attributes
4
+
5
+ class << self
6
+ def activate(common_name, attributes={})
7
+ option_object = new(common_name, attributes)
8
+ option_object.activate!
9
+ end
10
+
11
+ def deactivate(common_name, attributes={})
12
+ option_object = new(common_name, attributes)
13
+ option_object.deactivate!
14
+ end
15
+ end
16
+
17
+ def initialize(common_name, attributes)
18
+ @common_name = common_name
19
+ @attributes = attributes
20
+ end
21
+
22
+ def virtual_ip
23
+ System::OpenvpnStatus.current_virtual_address
24
+ end
25
+
26
+ def server_virtual_ip
27
+ System::OpenvpnStatus.server_virtual_address
28
+ end
29
+ end
30
+ end