ritm 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ritm.rb +0 -3
- data/lib/ritm/configuration.rb +42 -41
- data/lib/ritm/helpers/patches.rb +5 -0
- data/lib/ritm/interception/handlers.rb +8 -6
- data/lib/ritm/interception/http_forwarder.rb +5 -4
- data/lib/ritm/interception/intercept_utils.rb +6 -6
- data/lib/ritm/interception/request_interceptor_servlet.rb +2 -2
- data/lib/ritm/main.rb +11 -31
- data/lib/ritm/proxy/launcher.rb +26 -34
- data/lib/ritm/proxy/proxy_server.rb +14 -3
- data/lib/ritm/proxy/ssl_reverse_proxy.rb +2 -2
- data/lib/ritm/session.rb +57 -0
- data/lib/ritm/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 96ffb4d3b3d3b382171277d5095c9ba57396b6ee
|
4
|
+
data.tar.gz: ca0cafa22dcd94188702deb3d1ee3c4e3e71ed6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f05d3344d6157f1a2a374818b006019400a30d524d6df3db249abc5c9caaa4a4e494a9637bf784133f1cc92af7d34b47899a67d6ebfb1af1a2dc008747224a1f
|
7
|
+
data.tar.gz: c0a8e290882acde4b170fd788122186b3499e755395411ae3f35d0d80e5c6ba75546c6bc3c4181f25d24018590b3366850a3bf9fcec598d6a4c0e51edb41df50
|
data/lib/ritm.rb
CHANGED
data/lib/ritm/configuration.rb
CHANGED
@@ -4,51 +4,52 @@ require 'set'
|
|
4
4
|
module Ritm
|
5
5
|
# Global Ritm settings
|
6
6
|
class Configuration
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
ssl_reverse_proxy: {
|
14
|
-
bind_address: '127.0.0.1',
|
15
|
-
bind_port: 8081,
|
16
|
-
ca: {
|
17
|
-
pem: nil,
|
18
|
-
key: nil
|
19
|
-
}
|
20
|
-
},
|
7
|
+
def default_settings # rubocop:disable Metrics/MethodLength
|
8
|
+
{
|
9
|
+
proxy: {
|
10
|
+
bind_address: '127.0.0.1',
|
11
|
+
bind_port: 8080
|
12
|
+
},
|
21
13
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
14
|
+
ssl_reverse_proxy: {
|
15
|
+
bind_address: '127.0.0.1',
|
16
|
+
bind_port: 8081,
|
17
|
+
ca: {
|
18
|
+
pem: nil,
|
19
|
+
key: nil
|
20
|
+
}
|
29
21
|
},
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
22
|
+
|
23
|
+
intercept: {
|
24
|
+
enabled: true,
|
25
|
+
request: {
|
26
|
+
add_headers: {},
|
27
|
+
strip_headers: [/proxy-*/],
|
28
|
+
unpack_gzip_deflate: true,
|
29
|
+
update_content_length: true
|
30
|
+
},
|
31
|
+
response: {
|
32
|
+
add_headers: { 'connection' => 'close' },
|
33
|
+
strip_headers: ['strict-transport-security'],
|
34
|
+
unpack_gzip_deflate: true,
|
35
|
+
update_content_length: true
|
36
|
+
},
|
37
|
+
process_chunked_encoded_transfer: true
|
35
38
|
},
|
36
|
-
process_chunked_encoded_transfer: true
|
37
|
-
},
|
38
39
|
|
39
|
-
|
40
|
-
|
40
|
+
misc: {
|
41
|
+
ssl_pass_through: [],
|
42
|
+
upstream_proxy: nil
|
43
|
+
}
|
41
44
|
}
|
45
|
+
end
|
42
46
|
|
43
|
-
|
44
|
-
|
45
|
-
def initialize(settings = {})
|
46
|
-
reset(settings)
|
47
|
+
def initialize
|
48
|
+
reset
|
47
49
|
end
|
48
50
|
|
49
|
-
def reset
|
50
|
-
settings =
|
51
|
-
@settings = settings.to_properties
|
51
|
+
def reset
|
52
|
+
@settings = default_settings.to_properties
|
52
53
|
end
|
53
54
|
|
54
55
|
def method_missing(m, *args, &block)
|
@@ -59,10 +60,6 @@ module Ritm
|
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
62
|
-
def respond_to_missing?(method_name, _include_private = false)
|
63
|
-
@settings.respond_to?(method_name) || super
|
64
|
-
end
|
65
|
-
|
66
63
|
# Re-enable interception
|
67
64
|
def enable
|
68
65
|
@settings.intercept[:enabled] = true
|
@@ -72,5 +69,9 @@ module Ritm
|
|
72
69
|
def disable
|
73
70
|
@settings.intercept[:enabled] = false
|
74
71
|
end
|
72
|
+
|
73
|
+
def respond_to_missing?(method_name, _include_private = false)
|
74
|
+
@settings.respond_to?(method_name) || super
|
75
|
+
end
|
75
76
|
end
|
76
77
|
end
|
data/lib/ritm/helpers/patches.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'openssl'
|
1
2
|
require 'webrick'
|
2
3
|
require 'webrick/httpproxy'
|
3
4
|
require 'ritm/helpers/utils'
|
@@ -5,6 +6,10 @@ require 'ritm/helpers/utils'
|
|
5
6
|
# Patch WEBrick too short max uri length
|
6
7
|
Ritm::Utils.silence_warnings { WEBrick::HTTPRequest::MAX_URI_LENGTH = 4000 }
|
7
8
|
|
9
|
+
# Digest::Digest is deprecated. This has been fixed in certificate_authority master
|
10
|
+
# but the project is no longer maintained and the fixed version was never published
|
11
|
+
Ritm::Utils.silence_warnings { OpenSSL::Digest::Digest = OpenSSL::Digest }
|
12
|
+
|
8
13
|
# Make request method writable
|
9
14
|
WEBrick::HTTPRequest.instance_eval { attr_accessor :request_method, :unparsed_uri }
|
10
15
|
|
@@ -1,10 +1,12 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
def default_request_handler(session)
|
3
|
+
proc do |req|
|
4
|
+
session.dispatcher.notify_request(req) if session.conf.intercept.enabled
|
5
|
+
end
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
def default_response_handler(session)
|
9
|
+
proc do |req, res|
|
10
|
+
session.dispatcher.notify_response(req, res) if session.conf.intercept.enabled
|
11
|
+
end
|
10
12
|
end
|
@@ -16,21 +16,22 @@ module Ritm
|
|
16
16
|
class HTTPForwarder
|
17
17
|
include InterceptUtils
|
18
18
|
|
19
|
-
def initialize(request_interceptor, response_interceptor)
|
19
|
+
def initialize(request_interceptor, response_interceptor, context_config)
|
20
20
|
@request_interceptor = request_interceptor
|
21
21
|
@response_interceptor = response_interceptor
|
22
|
+
@config = context_config
|
22
23
|
# TODO: make SSL verification a configuration setting
|
23
24
|
@client = Faraday.new(ssl: { verify: false }) do |conn|
|
24
25
|
conn.adapter :net_http
|
25
|
-
|
26
|
+
conn.proxy @config.misc.upstream_proxy unless @config.misc.upstream_proxy.nil?
|
26
27
|
end
|
27
28
|
end
|
28
29
|
|
29
30
|
def forward(request, response)
|
30
|
-
intercept_request(@request_interceptor, request)
|
31
|
+
intercept_request(@request_interceptor, request, @config.intercept.request)
|
31
32
|
faraday_response = faraday_forward request
|
32
33
|
to_webrick_response faraday_response, response
|
33
|
-
intercept_response(@response_interceptor, request, response)
|
34
|
+
intercept_response(@response_interceptor, request, response, @config.intercept.response)
|
34
35
|
end
|
35
36
|
|
36
37
|
private
|
@@ -3,18 +3,18 @@ require 'ritm/helpers/encodings'
|
|
3
3
|
module Ritm
|
4
4
|
# Interceptor callbacks calling logic shared by the HTTP Proxy Server and the SSL Reverse Proxy Server
|
5
5
|
module InterceptUtils
|
6
|
-
def intercept_request(handler, request)
|
6
|
+
def intercept_request(handler, request, intercept_request_settings)
|
7
7
|
return if handler.nil?
|
8
|
-
preprocess(request,
|
8
|
+
preprocess(request, intercept_request_settings)
|
9
9
|
handler.call(request)
|
10
|
-
postprocess(request,
|
10
|
+
postprocess(request, intercept_request_settings)
|
11
11
|
end
|
12
12
|
|
13
|
-
def intercept_response(handler, request, response)
|
13
|
+
def intercept_response(handler, request, response, intercept_response_settings)
|
14
14
|
return if handler.nil?
|
15
|
-
preprocess(response,
|
15
|
+
preprocess(response, intercept_response_settings)
|
16
16
|
handler.call(request, response)
|
17
|
-
postprocess(response,
|
17
|
+
postprocess(response, intercept_response_settings)
|
18
18
|
end
|
19
19
|
|
20
20
|
private
|
@@ -4,9 +4,9 @@ require 'ritm/interception/http_forwarder'
|
|
4
4
|
module Ritm
|
5
5
|
# Actual implementation of the SSL Reverse Proxy service (decoupled from the certificate handling)
|
6
6
|
class RequestInterceptorServlet < WEBrick::HTTPServlet::AbstractServlet
|
7
|
-
def initialize(server, request_interceptor, response_interceptor)
|
7
|
+
def initialize(server, request_interceptor, response_interceptor, conf)
|
8
8
|
super server
|
9
|
-
@forwarder = HTTPForwarder.new(request_interceptor, response_interceptor)
|
9
|
+
@forwarder = HTTPForwarder.new(request_interceptor, response_interceptor, conf)
|
10
10
|
end
|
11
11
|
|
12
12
|
def service(request, response)
|
data/lib/ritm/main.rb
CHANGED
@@ -1,38 +1,18 @@
|
|
1
|
+
require 'ritm/session'
|
2
|
+
|
1
3
|
# Main module
|
2
4
|
module Ritm
|
3
|
-
|
4
|
-
def self.configure(&block)
|
5
|
-
conf.instance_eval(&block)
|
6
|
-
end
|
7
|
-
|
8
|
-
# Re-enable fuzzing (if it was disabled)
|
9
|
-
def self.enable
|
10
|
-
conf.enable
|
11
|
-
end
|
12
|
-
|
13
|
-
# Disable fuzzing (if it was enabled)
|
14
|
-
def self.disable
|
15
|
-
conf.disable
|
16
|
-
end
|
17
|
-
|
18
|
-
# Access the current config settings
|
19
|
-
def self.conf
|
20
|
-
@configuration ||= Configuration.new
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.dispatcher
|
24
|
-
@dispatcher ||= Dispatcher.new
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.add_handler(handler)
|
28
|
-
dispatcher.add_handler(handler)
|
29
|
-
end
|
5
|
+
GLOBAL_SESSION = Session.new
|
30
6
|
|
31
|
-
def self.
|
32
|
-
|
7
|
+
def self.method_missing(m, *args, &block)
|
8
|
+
if GLOBAL_SESSION.respond_to?(m)
|
9
|
+
GLOBAL_SESSION.send(m, *args, &block)
|
10
|
+
else
|
11
|
+
super
|
12
|
+
end
|
33
13
|
end
|
34
14
|
|
35
|
-
def self.
|
36
|
-
|
15
|
+
def self.respond_to_missing?(method_name, _include_private = false)
|
16
|
+
GLOBAL_SESSION.respond_to?(method_name) || super
|
37
17
|
end
|
38
18
|
end
|
data/lib/ritm/proxy/launcher.rb
CHANGED
@@ -7,19 +7,10 @@ module Ritm
|
|
7
7
|
module Proxy
|
8
8
|
# Launches the Proxy server and the SSL Reverse Proxy with the given settings
|
9
9
|
class Launcher
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
# ca_crt_path [String]: the path to the certification authority certificate
|
15
|
-
# ca_key_path [String]: the path to the certification authority private key
|
16
|
-
# request_interceptor [Proc |request|]: the handler for request interception
|
17
|
-
# response_interceptor [Proc |request, response|]: the handler for response interception
|
18
|
-
def initialize(**args)
|
19
|
-
build_settings(**args)
|
20
|
-
|
21
|
-
build_reverse_proxy(@ssl_proxy_host, @ssl_proxy_port, @request_interceptor, @response_interceptor)
|
22
|
-
build_proxy(@proxy_host, @proxy_port, @https_forward, @request_interceptor, @response_interceptor)
|
10
|
+
def initialize(session)
|
11
|
+
build_settings(session)
|
12
|
+
build_reverse_proxy
|
13
|
+
build_proxy
|
23
14
|
end
|
24
15
|
|
25
16
|
# Starts the service (non blocking)
|
@@ -36,36 +27,37 @@ module Ritm
|
|
36
27
|
|
37
28
|
private
|
38
29
|
|
39
|
-
def build_settings(
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
@
|
44
|
-
@
|
45
|
-
@
|
46
|
-
@request_interceptor = args[:request_interceptor] || DEFAULT_REQUEST_HANDLER
|
47
|
-
@response_interceptor = args[:response_interceptor] || DEFAULT_RESPONSE_HANDLER
|
30
|
+
def build_settings(session)
|
31
|
+
@conf = session.conf
|
32
|
+
ssl_proxy_host = @conf.ssl_reverse_proxy.bind_address
|
33
|
+
ssl_proxy_port = @conf.ssl_reverse_proxy.bind_port
|
34
|
+
@https_forward = "#{ssl_proxy_host}:#{ssl_proxy_port}"
|
35
|
+
@request_interceptor = default_request_handler(session)
|
36
|
+
@response_interceptor = default_response_handler(session)
|
48
37
|
|
49
|
-
crt_path =
|
50
|
-
key_path =
|
38
|
+
crt_path = @conf.ssl_reverse_proxy.ca.pem
|
39
|
+
key_path = @conf.ssl_reverse_proxy.ca.key
|
51
40
|
@certificate = ca_certificate(crt_path, key_path)
|
52
41
|
end
|
53
42
|
|
54
|
-
def build_proxy
|
55
|
-
@http = Ritm::Proxy::ProxyServer.new(
|
43
|
+
def build_proxy
|
44
|
+
@http = Ritm::Proxy::ProxyServer.new(BindAddress: @conf.proxy.bind_address,
|
45
|
+
Port: @conf.proxy.bind_port,
|
56
46
|
AccessLog: [],
|
57
|
-
BindAddress: host,
|
58
47
|
Logger: WEBrick::Log.new(File.open(File::NULL, 'w')),
|
59
|
-
https_forward:
|
48
|
+
https_forward: @https_forward,
|
60
49
|
ProxyVia: nil,
|
61
|
-
request_interceptor:
|
62
|
-
response_interceptor:
|
50
|
+
request_interceptor: @request_interceptor,
|
51
|
+
response_interceptor: @response_interceptor,
|
52
|
+
ritm_conf: @conf)
|
63
53
|
end
|
64
54
|
|
65
|
-
def build_reverse_proxy
|
66
|
-
@https = Ritm::Proxy::SSLReverseProxy.new(
|
67
|
-
|
68
|
-
|
55
|
+
def build_reverse_proxy
|
56
|
+
@https = Ritm::Proxy::SSLReverseProxy.new(@conf.ssl_reverse_proxy.bind_port,
|
57
|
+
@certificate,
|
58
|
+
@conf,
|
59
|
+
request_interceptor: @request_interceptor,
|
60
|
+
response_interceptor: @response_interceptor)
|
69
61
|
end
|
70
62
|
|
71
63
|
def ca_certificate(pem, key)
|
@@ -30,7 +30,7 @@ module Ritm
|
|
30
30
|
proxy_auth(req, res)
|
31
31
|
|
32
32
|
# Request modifier handler
|
33
|
-
intercept_request(@config[:request_interceptor], req)
|
33
|
+
intercept_request(@config[:request_interceptor], req, @config[:ritm_conf].intercept.request)
|
34
34
|
|
35
35
|
begin
|
36
36
|
send("do_#{req.request_method}", req, res)
|
@@ -41,13 +41,24 @@ module Ritm
|
|
41
41
|
end
|
42
42
|
|
43
43
|
# Response modifier handler
|
44
|
-
intercept_response(@config[:response_interceptor], req, res)
|
44
|
+
intercept_response(@config[:response_interceptor], req, res, @config[:ritm_conf].intercept.response)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Override
|
48
|
+
def proxy_uri(req, _res)
|
49
|
+
if req.request_method == 'CONNECT'
|
50
|
+
# Let the reverse proxy handle upstream proxies for https
|
51
|
+
nil
|
52
|
+
else
|
53
|
+
proxy = @config[:ritm_conf].misc.upstream_proxy
|
54
|
+
proxy.nil? ? nil : URI.parse(proxy)
|
55
|
+
end
|
45
56
|
end
|
46
57
|
|
47
58
|
private
|
48
59
|
|
49
60
|
def ssl_pass_through?(destination)
|
50
|
-
|
61
|
+
@config[:ritm_conf].misc.ssl_pass_through.each do |matcher|
|
51
62
|
case matcher
|
52
63
|
when String
|
53
64
|
return true if destination == matcher
|
@@ -13,7 +13,7 @@ module Ritm
|
|
13
13
|
# @param ca [Ritm::CA]: The certificate authority used to sign fake server certificates
|
14
14
|
# @param request_interceptor [Proc]: If given, it will be invoked before proxying the request
|
15
15
|
# @param response_interceptor [Proc]: If give, it will be invoked before sending back the response
|
16
|
-
def initialize(port, ca, request_interceptor: nil, response_interceptor: nil)
|
16
|
+
def initialize(port, ca, conf, request_interceptor: nil, response_interceptor: nil)
|
17
17
|
@ca = ca
|
18
18
|
default_vhost = 'localhost'
|
19
19
|
@server = CertSigningHTTPSServer.new(Port: port,
|
@@ -22,7 +22,7 @@ module Ritm
|
|
22
22
|
ca: ca,
|
23
23
|
**vhost_settings(default_vhost))
|
24
24
|
|
25
|
-
@server.mount '/', RequestInterceptorServlet, request_interceptor, response_interceptor
|
25
|
+
@server.mount '/', RequestInterceptorServlet, request_interceptor, response_interceptor, conf
|
26
26
|
end
|
27
27
|
|
28
28
|
def start_async
|
data/lib/ritm/session.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'ritm/dispatcher'
|
2
|
+
require 'ritm/proxy/launcher'
|
3
|
+
require 'ritm/configuration'
|
4
|
+
|
5
|
+
module Ritm
|
6
|
+
# Holds the context of a interception session.
|
7
|
+
# Changes in the context configuration should affect only this session
|
8
|
+
class Session
|
9
|
+
attr_reader :conf, :dispatcher
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@conf = Configuration.new
|
13
|
+
@dispatcher = Dispatcher.new
|
14
|
+
@proxy = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
# Define configuration settings
|
18
|
+
def configure(&block)
|
19
|
+
conf.instance_eval(&block)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Re-enable fuzzing (if it was disabled)
|
23
|
+
def enable
|
24
|
+
conf.enable
|
25
|
+
end
|
26
|
+
|
27
|
+
# Disable fuzzing (if it was enabled)
|
28
|
+
def disable
|
29
|
+
conf.disable
|
30
|
+
end
|
31
|
+
|
32
|
+
# Start the proxy service
|
33
|
+
def start
|
34
|
+
raise 'Proxy already started' unless @proxy.nil?
|
35
|
+
@proxy = Proxy::Launcher.new(self)
|
36
|
+
@proxy.start
|
37
|
+
end
|
38
|
+
|
39
|
+
# Shutdown the proxy service
|
40
|
+
def shutdown
|
41
|
+
@proxy.shutdown unless @proxy.nil?
|
42
|
+
@proxy = nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_handler(handler)
|
46
|
+
dispatcher.add_handler(handler)
|
47
|
+
end
|
48
|
+
|
49
|
+
def on_request(&block)
|
50
|
+
dispatcher.on_request(&block)
|
51
|
+
end
|
52
|
+
|
53
|
+
def on_response(&block)
|
54
|
+
dispatcher.on_response(&block)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/ritm/version.rb
CHANGED
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:
|
4
|
+
version: 1.0.0
|
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-
|
11
|
+
date: 2016-12-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -187,6 +187,7 @@ files:
|
|
187
187
|
- lib/ritm/proxy/launcher.rb
|
188
188
|
- lib/ritm/proxy/proxy_server.rb
|
189
189
|
- lib/ritm/proxy/ssl_reverse_proxy.rb
|
190
|
+
- lib/ritm/session.rb
|
190
191
|
- lib/ritm/version.rb
|
191
192
|
homepage: https://github.com/argos83/ritm
|
192
193
|
licenses:
|
@@ -208,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
208
209
|
version: '0'
|
209
210
|
requirements: []
|
210
211
|
rubyforge_project:
|
211
|
-
rubygems_version: 2.
|
212
|
+
rubygems_version: 2.5.1
|
212
213
|
signing_key:
|
213
214
|
specification_version: 4
|
214
215
|
summary: Ruby In The Middle
|