smartvpn-http-hooks 1.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +39 -0
  6. data/LICENSE +19 -0
  7. data/README.md +22 -0
  8. data/bin/smartvpn-activate +6 -0
  9. data/bin/smartvpn-authenticate +8 -0
  10. data/bin/smartvpn-connect +6 -0
  11. data/bin/smartvpn-disconnect +7 -0
  12. data/lib/api/activation.rb +39 -0
  13. data/lib/api/authentication.rb +27 -0
  14. data/lib/api/billing.rb +44 -0
  15. data/lib/api/connect.rb +21 -0
  16. data/lib/api/connection.rb +66 -0
  17. data/lib/api/disconnect.rb +21 -0
  18. data/lib/exceptions/option_not_found.rb +2 -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/smartvpn-http-hooks.rb +19 -0
  26. data/lib/system/openvpn_status.rb +19 -0
  27. data/lib/system/openvpn_status_log_parser.rb +30 -0
  28. data/lib/system/openvpn_status_log_reader.rb +29 -0
  29. data/smartvpn-http-hooks.gemspec +17 -0
  30. data/spec/fixtures/active_connections.txt +10 -0
  31. data/spec/fixtures/multiple_connections.txt +11 -0
  32. data/spec/lib/api/activation_spec.rb +30 -0
  33. data/spec/lib/api/authentication_spec.rb +29 -0
  34. data/spec/lib/api/billing_spec.rb +71 -0
  35. data/spec/lib/api/connect_spec.rb +40 -0
  36. data/spec/lib/api/connection_spec.rb +59 -0
  37. data/spec/lib/api/disconnect_spec.rb +40 -0
  38. data/spec/lib/openvpn_password_authenticator_spec.rb +52 -0
  39. data/spec/lib/option/base_spec.rb +26 -0
  40. data/spec/lib/option/i2p_spec.rb +19 -0
  41. data/spec/lib/option/proxy_spec.rb +32 -0
  42. data/spec/lib/option/repository_spec.rb +25 -0
  43. data/spec/lib/signer_spec.rb +21 -0
  44. data/spec/lib/system/openvpn_status_log_parser_spec.rb +25 -0
  45. data/spec/lib/system/openvpn_status_log_reader_spec.rb +33 -0
  46. data/spec/lib/system/openvpn_status_spec.rb +22 -0
  47. data/spec/spec_helper.rb +9 -0
  48. metadata +135 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 452d36d90e1e0b42c1115a570b99d67a73aad680
4
+ data.tar.gz: 3bac4bacc5059ac0045b4c73517eceaf0e0b9e1c
5
+ SHA512:
6
+ metadata.gz: e3d5f961cdea917deb8860d5e2132cafc713f36e8ff128fb25dbc0216fd4ae35059a452cfb0dfdcfda37bf22411487d03f2b31690be43981b426853a8a6e9b85
7
+ data.tar.gz: a4cb8b5fd0f11ce8955eb0633742de163a437ec91bea0d542868936d07665d8ae0bc2615062040501a91e2489a7640d2553bf2a1f29312d83fe2f92d9e00474b
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .idea/
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
+ smartvpn-http-hooks (1.0.7)
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
+ smartvpn-http-hooks!
36
+ webmock
37
+
38
+ BUNDLED WITH
39
+ 1.17.1
data/LICENSE ADDED
@@ -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.
data/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # smartvpn-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 'smartvpn-http-hooks'
4
+
5
+ api = Api::Activation.new
6
+ api.activate
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'smartvpn-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 'smartvpn-http-hooks'
4
+
5
+ connection = Api::Connect.new
6
+ connection.invoke
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'smartvpn-http-hooks'
4
+
5
+ connection = Api::Disconnect.new
6
+ connection.invoke
7
+
@@ -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
@@ -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
@@ -0,0 +1,44 @@
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 = '/etc/openvpn/API_HOST'
9
+ KEY_PATH = '/etc/openvpn/auth_key'
10
+
11
+ def host_with_port
12
+ host = File.read(API_HOST)
13
+ "http://#{host.strip}"
14
+ end
15
+
16
+ def auth_key
17
+ File.read(KEY_PATH)
18
+ end
19
+
20
+ def hostname
21
+ Socket.gethostname
22
+ end
23
+
24
+ def success_api_call?
25
+ api_call_result.code == '200'
26
+ end
27
+
28
+ def response
29
+ JSON.parse(api_call_result.body)
30
+ end
31
+
32
+ def api_call_result
33
+ @api_result ||= Net::HTTP.post_form(uri, signed_data)
34
+ end
35
+
36
+ def uri
37
+ URI("#{host_with_port}/api/#{action}")
38
+ end
39
+
40
+ def signed_data
41
+ data.merge!({ signature: Signer.sign_hash(data, auth_key) })
42
+ end
43
+ end
44
+ 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|
17
+ data[:option_class].activate(common_name, data[:attributes])
18
+ end
19
+ end
20
+ end
21
+ end
@@ -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
@@ -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,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
data/lib/option/i2p.rb ADDED
@@ -0,0 +1,28 @@
1
+ module Option
2
+ class I2p < Base
3
+ def activate!
4
+ add_firewall_routes
5
+ end
6
+
7
+ def deactivate!
8
+ remove_firewall_routes
9
+ end
10
+
11
+ private
12
+
13
+ def add_firewall_routes
14
+ system "/sbin/iptables -t filter -D INPUT -p tcp -m tcp --dport 8118 -j DROP"
15
+ system "/sbin/iptables -t filter -A INPUT -s #{virtual_ip} -p tcp -m tcp --dport 8118 -j ACCEPT"
16
+ system "/sbin/iptables -t filter -A INPUT -p tcp -m tcp --dport 8118 -j DROP"
17
+ system "/sbin/iptables -t nat -A PREROUTING -p udp -m udp -s #{virtual_ip} --dport 53 -j DNAT --to-destination #{server_virtual_ip}:53"
18
+ system "/sbin/iptables -t nat -A PREROUTING -p tcp -m tcp -s #{virtual_ip} --dport 53 -j DNAT --to-destination #{server_virtual_ip}:53"
19
+ true
20
+ end
21
+
22
+ def remove_firewall_routes
23
+ system "/sbin/iptables -D INPUT -s #{virtual_ip} -p tcp -m tcp --dport 8118 -j ACCEPT"
24
+ system "/sbin/iptables -t nat -D PREROUTING -p udp -m udp -s #{virtual_ip} --dport 53 -j DNAT --to-destination #{server_virtual_ip}:53"
25
+ system "/sbin/iptables -t nat -D PREROUTING -p tcp -m tcp -s #{virtual_ip} --dport 53 -j DNAT --to-destination #{server_virtual_ip}:53"
26
+ end
27
+ end
28
+ end