ritm 0.1.0 → 1.0.0
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.
- 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
|