ritm 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2eeab06c6fd5db111b87a1ff18694f4206f571c9
4
- data.tar.gz: b1537590beb540d93cd9161b7a7fee6d4090f3cb
3
+ metadata.gz: 9f58ad7ec7b60adcb6d5644582b19da348fee831
4
+ data.tar.gz: 2e024c04e5527f91864055141e5cf39cbe18e326
5
5
  SHA512:
6
- metadata.gz: a2e1a3bbb3b8c0b9ac87339152c9a3b726ea2295d3c14c48e67952e89a5b40ea5314446e445078b5e98556550faf8de6609af42d6e438509ae4b87604f98fd1a
7
- data.tar.gz: 9ebf2c4991de1026d05cfd9e6226424f9feb0631b1d22a001819195d9d3e215f779a72feb0b65dc1f653c317d6dab62dac49ed45c29f592e37ed38ac0cc8897d
6
+ metadata.gz: 72464b798fc3fa1ee83ebc4f3a3865a9e1d0f77a4e5ee2161475c4659f392cff87395201090ace9f7006043adefbb30972ac709e130663005dc38a6557fbd031
7
+ data.tar.gz: a0cdb6a6999fee1e099706541f4ace66b2217ea6d005452cb5cf72937e819058f661e55b6b6b3678bc03a8e3e67d0d974d4017aacec533b355785d9d9f0c6a58
data/lib/ritm/certs/ca.rb CHANGED
@@ -7,7 +7,7 @@ module Ritm
7
7
  def self.create(common_name: 'RubyInTheMiddle')
8
8
  super(common_name, serial_number: 1) do |cert|
9
9
  cert.signing_entity = true
10
- cert.sign!(signing_profile)
10
+ cert.sign!(ca_signing_profile)
11
11
  yield cert if block_given?
12
12
  end
13
13
  end
@@ -15,17 +15,26 @@ module Ritm
15
15
  def self.load(crt, private_key)
16
16
  super(crt, private_key) do |cert|
17
17
  cert.signing_entity = true
18
- cert.sign!(signing_profile)
18
+ cert.sign!(ca_signing_profile)
19
19
  yield cert if block_given?
20
20
  end
21
21
  end
22
22
 
23
23
  def sign(certificate)
24
24
  certificate.cert.parent = @cert
25
- certificate.cert.sign!
25
+ certificate.cert.sign!(self.class.signing_profile)
26
26
  end
27
27
 
28
28
  def self.signing_profile
29
+ {
30
+ 'extensions' => {
31
+ 'keyUsage' => { 'usage' => %w(keyEncipherment digitalSignature) },
32
+ 'extendedKeyUsage' => { 'usage' => %w(serverAuth clientAuth) }
33
+ }
34
+ }
35
+ end
36
+
37
+ def self.ca_signing_profile
29
38
  { 'extensions' => { 'keyUsage' => { 'usage' => %w(critical keyCertSign keyEncipherment digitalSignature) } } }
30
39
  end
31
40
  end
@@ -16,8 +16,11 @@ module Ritm
16
16
  def self.create(common_name, serial_number: nil)
17
17
  cert = CertificateAuthority::Certificate.new
18
18
  cert.subject.common_name = common_name
19
+ cert.subject.organization = cert.subject.organizational_unit = 'RubyInTheMiddle'
20
+ cert.subject.country = 'AR'
21
+ cert.not_before = cert.not_before - 3600 * 24 * 30 # Substract 30 days
19
22
  cert.serial_number.number = serial_number || common_name.hash.abs
20
- cert.key_material.generate_key
23
+ cert.key_material.generate_key(1024)
21
24
  yield cert if block_given?
22
25
  new cert
23
26
  end
@@ -37,5 +40,9 @@ module Ritm
37
40
  def pem
38
41
  @cert.to_pem
39
42
  end
43
+
44
+ def x509
45
+ @cert.openssl_body
46
+ end
40
47
  end
41
48
  end
@@ -18,41 +18,30 @@ module Ritm
18
18
  key: nil
19
19
  }
20
20
  },
21
-
22
21
  intercept: {
23
- # Is interception enabled
24
22
  enabled: true,
25
-
26
- # Do not intercept requests whose URLs match/start with the given regex/strings (blacklist)
27
- skip_urls: [],
28
-
29
- # Intercepts requests whose URLs match/start with the given regex/strings (whitelist)
30
- # By default everything will be intercepted.
31
- intercept_urls: []
32
- },
33
-
34
- misc: {
35
- add_request_headers: {},
36
- add_response_headers: { 'connection' => 'clone' },
37
-
38
- strip_request_headers: [/proxy-*/],
39
- strip_response_headers: ['strict-transport-security', 'transfer-encoding'],
40
-
41
- unpack_gzip_deflate_in_requests: true,
42
- unpack_gzip_deflate_in_responses: true,
23
+ request: {
24
+ add_headers: {},
25
+ strip_headers: [/proxy-*/],
26
+ unpack_gzip_deflate: true,
27
+ update_content_length: true
28
+ },
29
+ response: {
30
+ add_headers: { 'connection' => 'close' },
31
+ strip_headers: ['strict-transport-security'],
32
+ unpack_gzip_deflate: true,
33
+ update_content_length: true
34
+ },
43
35
  process_chunked_encoded_transfer: true
44
36
  }
45
37
  }.freeze
46
38
 
47
39
  def initialize(settings = {})
48
- settings = DEFAULT_SETTINGS.merge(settings)
49
- @values = {
50
- dispatcher: Dispatcher.new,
51
-
52
- # Is interception enabled
53
- enabled: true
54
- }
40
+ reset(settings)
41
+ end
55
42
 
43
+ def reset(settings = {})
44
+ settings = DEFAULT_SETTINGS.merge(settings)
56
45
  @settings = settings.to_properties
57
46
  end
58
47
 
@@ -60,18 +49,14 @@ module Ritm
60
49
  @settings.send(m, *args, &block)
61
50
  end
62
51
 
63
- def [](setting)
64
- @values[setting]
65
- end
66
-
67
52
  # Re-enable interception
68
53
  def enable
69
- @values[:enabled] = true
54
+ @settings.intercept[:enabled] = true
70
55
  end
71
56
 
72
57
  # Disable interception
73
58
  def disable
74
- @values[:enabled] = false
59
+ @settings.intercept[:enabled] = false
75
60
  end
76
61
  end
77
62
  end
@@ -2,9 +2,9 @@
2
2
  dispatcher = Ritm.dispatcher
3
3
 
4
4
  DEFAULT_REQUEST_HANDLER = proc do |req|
5
- dispatcher.notify_request(req) if Ritm.conf[:enabled]
5
+ dispatcher.notify_request(req) if Ritm.conf.intercept.enabled
6
6
  end
7
7
 
8
8
  DEFAULT_RESPONSE_HANDLER = proc do |req, res|
9
- dispatcher.notify_response(req, res) if Ritm.conf[:enabled]
9
+ dispatcher.notify_response(req, res) if Ritm.conf.intercept.enabled
10
10
  end
@@ -28,7 +28,6 @@ module Ritm
28
28
 
29
29
  def forward(request, response)
30
30
  intercept_request(@request_interceptor, request)
31
-
32
31
  faraday_response = faraday_forward request
33
32
  to_webrick_response faraday_response, response
34
33
  intercept_response(@response_interceptor, request, response)
@@ -41,38 +40,19 @@ module Ritm
41
40
  @client.send req_method do |req|
42
41
  req.url request.request_uri
43
42
  req.body = request.body
44
- add_request_headers(req, request)
45
- end
46
- end
47
-
48
- def add_request_headers(faraday_request, webrick_request)
49
- webrick_request.header.each do |name, value|
50
- faraday_request.headers[name] = value unless strip?(name, Ritm.conf.misc.strip_request_headers)
43
+ request.header.each do |name, value|
44
+ req.headers[name] = value
45
+ end
51
46
  end
52
- Ritm.conf.misc.add_request_headers.each { |k, v| faraday_request.headers[k] = v }
53
47
  end
54
48
 
55
49
  def to_webrick_response(faraday_response, webrick_response)
56
50
  webrick_response.status = faraday_response.status
57
51
  webrick_response.body = faraday_response.body
58
52
  faraday_response.headers.each do |name, value|
59
- webrick_response[name] = value unless strip?(name, Ritm.conf.misc.strip_response_headers)
53
+ webrick_response[name] = value
60
54
  end
61
- Ritm.conf.misc.add_response_headers.each { |k, v| webrick_response[k] = v }
62
55
  webrick_response
63
56
  end
64
-
65
- def strip?(header, rules)
66
- header = header.to_s.downcase
67
- rules.each do |rule|
68
- case rule
69
- when String
70
- return true if header == rule
71
- when Regexp
72
- return true if header =~ rule
73
- end
74
- end
75
- false
76
- end
77
57
  end
78
58
  end
@@ -2,32 +2,42 @@ require 'ritm/helpers/encodings'
2
2
 
3
3
  module Ritm
4
4
  # Interceptor callbacks calling logic shared by the HTTP Proxy Server and the SSL Reverse Proxy Server
5
- # Passes request
6
5
  module InterceptUtils
7
6
  def intercept_request(handler, request)
8
7
  return if handler.nil?
8
+ preprocess(request, Ritm.conf.intercept.request)
9
9
  handler.call(request)
10
+ postprocess(request, Ritm.conf.intercept.request)
10
11
  end
11
12
 
12
13
  def intercept_response(handler, request, response)
13
14
  return if handler.nil?
14
- # TODO: Disable the automated decoding from config
15
- encoding = content_encoding(response)
16
- decoded(encoding, response) do |decoded_response|
17
- handler.call(request, decoded_response)
18
- end
19
-
20
- response.header.delete('content-length') if chunked?(response)
15
+ preprocess(response, Ritm.conf.intercept.response)
16
+ handler.call(request, response)
17
+ postprocess(response, Ritm.conf.intercept.response)
21
18
  end
22
19
 
23
20
  private
24
21
 
25
- def chunked?(response)
26
- response.header.fetch('transfer-encoding', '').casecmp 'chunked'
22
+ def preprocess(req_res, settings)
23
+ headers = header_obj(req_res)
24
+ decode(req_res) if settings.unpack_gzip_deflate
25
+ req_res.header.delete_if { |name, _v| strip?(name, settings.strip_headers) }
26
+ settings.add_headers.each { |name, value| headers[name] = value }
27
+ req_res.header.delete('transfer-encoding') if chunked?(headers)
28
+ end
29
+
30
+ def postprocess(req_res, settings)
31
+ header_obj(req_res)['content-length'] = (req_res.body || '').size.to_s if settings.update_content_length
32
+ end
33
+
34
+ def chunked?(headers)
35
+ headers['transfer-encoding'] && headers['transfer-encoding'].casecmp('chunked')
27
36
  end
28
37
 
29
- def content_encoding(response)
30
- case response.header.fetch('content-encoding', '').downcase
38
+ def content_encoding(req_res)
39
+ ce = header_obj(req_res)['content-encoding'] || ''
40
+ case ce.downcase
31
41
  when 'gzip', 'x-gzip'
32
42
  :gzip
33
43
  when 'deflate'
@@ -37,14 +47,34 @@ module Ritm
37
47
  end
38
48
  end
39
49
 
40
- def decoded(encoding, res)
41
- res.body = Encodings.decode(encoding, res.body)
42
- _content_encoding = res.header.delete('content-encoding')
43
- yield res
44
- # TODO: should it be re-encoded?
45
- # res.body = Encodings.encode(encoding, res.body)
46
- # res.header['content-encoding'] = content_encoding
47
- res.header['content-length'] = res.body.size.to_s
50
+ def header_obj(req_res)
51
+ case req_res
52
+ when WEBrick::HTTPRequest
53
+ req_res
54
+ when WEBrick::HTTPResponse
55
+ req_res.header
56
+ end
57
+ end
58
+
59
+ def decode(req_res)
60
+ encoding = content_encoding(req_res)
61
+ return if encoding == :identity
62
+ req_res.body = Encodings.decode(encoding, req_res.body)
63
+ _content_encoding = req_res.header.delete('content-encoding')
64
+ header_obj(req_res)['content-length'] = (req_res.body || '').size.to_s
65
+ end
66
+
67
+ def strip?(header, rules)
68
+ header = header.to_s.downcase
69
+ rules.each do |rule|
70
+ case rule
71
+ when String
72
+ return true if header == rule
73
+ when Regexp
74
+ return true if header =~ rule
75
+ end
76
+ end
77
+ false
48
78
  end
49
79
  end
50
80
  end
data/lib/ritm/main.rb CHANGED
@@ -21,7 +21,7 @@ module Ritm
21
21
  end
22
22
 
23
23
  def self.dispatcher
24
- conf[:dispatcher]
24
+ @dispatcher ||= Dispatcher.new
25
25
  end
26
26
 
27
27
  def self.add_handler(handler)
@@ -35,36 +35,4 @@ module Ritm
35
35
  def self.on_response(&block)
36
36
  dispatcher.on_response(&block)
37
37
  end
38
-
39
- # private class methods
40
-
41
- def self.intercept?(request)
42
- return false unless conf[:enabled]
43
- url = request.url.to_s
44
- whitelisted?(url) && !blacklisted?(url)
45
- end
46
-
47
- def self.whitelisted?(url)
48
- conf[:attack_urls].empty? || url_matches_any?(url, conf[:attack_urls])
49
- end
50
-
51
- def self.blacklisted?(url)
52
- url_matches_any? url, conf[:skip_urls]
53
- end
54
-
55
- def self.url_matches_any?(url, matchers)
56
- matchers.each do |matcher|
57
- case matcher
58
- when Regexp
59
- return true if url =~ matcher
60
- when String
61
- return true if url.include? matcher
62
- else
63
- raise 'URL matcher should be a String or Regexp'
64
- end
65
- end
66
- false
67
- end
68
-
69
- private_class_method :intercept?, :whitelisted?, :blacklisted?, :url_matches_any?
70
38
  end
@@ -41,8 +41,8 @@ module Ritm
41
41
 
42
42
  def context_with_cert(original_ctx, cert)
43
43
  ctx = original_ctx.dup
44
- ctx.key = OpenSSL::PKey::RSA.new(cert.private_key)
45
- ctx.cert = OpenSSL::X509::Certificate.new(cert.pem)
44
+ ctx.key = cert.private_key
45
+ ctx.cert = cert.x509
46
46
  ctx
47
47
  end
48
48
  end
data/lib/ritm/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # Ritm version
2
2
  module Ritm
3
- VERSION = '0.0.1'.freeze
3
+ VERSION = '0.0.2'.freeze
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ritm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastián Tello
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-30 00:00:00.000000000 Z
11
+ date: 2016-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0.40'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.11'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.11'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: sinatra
99
113
  requirement: !ruby/object:Gem::Requirement