castle-rb 4.2.1 → 4.3.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/README.md +21 -5
- data/lib/castle/configuration.rb +11 -3
- data/lib/castle/extractors/ip.rb +22 -4
- data/lib/castle/version.rb +1 -1
- data/spec/lib/castle/api/session_spec.rb +47 -0
- data/spec/lib/castle/extractors/ip_spec.rb +29 -1
- data/spec/lib/castle/headers_filter_spec.rb +3 -2
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0dd544ffa6fc2660fc67c058011df5b9d83a8078b48506ab611809166960f501
|
4
|
+
data.tar.gz: 1720630a7f1925ba1142208114198cf176a8a3fec5c04be480aa4d2384e03de2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71320c8ff0a5dd2a137723efc76c5530449bdf4635eed38f4c5fcad8bcb9253c5b2557b9113071cd606528eb58d3d66921fa1a728e6ea123d65ab2173e71ad59
|
7
|
+
data.tar.gz: d2f57e05bd976a294385808757b148248f01b2319cd99e88277bc18e104d4a2fabbcd2d1aa55057d7fc5a12e4ab7e3ec66086502c74ba6d8e5f6b8e51acfba01
|
data/README.md
CHANGED
@@ -87,17 +87,33 @@ Castle.configure do |config|
|
|
87
87
|
config.blacklisted = ['HTTP-X-header']
|
88
88
|
|
89
89
|
# Castle needs the original IP of the client, not the IP of your proxy or load balancer.
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
90
|
+
# The SDK will only trust the proxy chain as defined in the configuration.
|
91
|
+
# We try to fetch the client IP based on X-Forwarded-For or Remote-Addr headers in that order,
|
92
|
+
# but sometimes the client IP may be stored in a different header or order.
|
93
|
+
# The SDK can be configured to look for the client IP address in headers that you specify.
|
94
|
+
# If the specified header or X-Forwarded-For default contains a proxy chain with public IP addresses,
|
95
|
+
# then one of the following must be set
|
96
|
+
# 1. The trusted_proxies value must match the known proxy IP's
|
97
|
+
# 2. The trusted_proxy_depth value must be set to the number of known trusted proxies in the chain (see below)
|
93
98
|
configuration.ip_headers = []
|
94
99
|
|
95
100
|
# Additionally to make X-Forwarded-For and other headers work better discovering client ip address,
|
96
101
|
# and not the address of a reverse proxy server, you can define trusted proxies
|
97
102
|
# which will help to fetch proper ip from those headers
|
103
|
+
|
104
|
+
# In order to extract the client IP of the X-Forwarded-For header
|
105
|
+
# and not the address of a reverse proxy server, you must define all trusted public proxies
|
106
|
+
# you can achieve this by listing all the proxies ip defined by string or regular expressions
|
107
|
+
# in trusted_proxies setting
|
98
108
|
configuration.trusted_proxies = []
|
99
|
-
#
|
100
|
-
|
109
|
+
# or by providing number of trusted proxies used in the chain
|
110
|
+
configuration.trusted_proxy_depth = 0
|
111
|
+
|
112
|
+
# If there is no possibility to define options above and there is no other header which can have client ip
|
113
|
+
# then you may set trust_proxy_chain = true to trust all of the proxy IP's in X-Forwarded-For
|
114
|
+
configuration.trust_proxy_chain = false
|
115
|
+
|
116
|
+
# *Note: default list of proxies which is always marked as trusted: Castle::Configuration::TRUSTED_PROXIES
|
101
117
|
end
|
102
118
|
```
|
103
119
|
|
data/lib/castle/configuration.rb
CHANGED
@@ -44,8 +44,9 @@ module Castle
|
|
44
44
|
X-Castle-Client-Id
|
45
45
|
].freeze
|
46
46
|
|
47
|
-
attr_accessor :host, :port, :request_timeout, :url_prefix
|
48
|
-
attr_reader :api_secret, :whitelisted, :blacklisted, :failover_strategy, :ip_headers,
|
47
|
+
attr_accessor :host, :port, :request_timeout, :url_prefix, :trust_proxy_chain
|
48
|
+
attr_reader :api_secret, :whitelisted, :blacklisted, :failover_strategy, :ip_headers,
|
49
|
+
:trusted_proxies, :trusted_proxy_depth
|
49
50
|
|
50
51
|
def initialize
|
51
52
|
@formatter = Castle::HeadersFormatter
|
@@ -63,6 +64,8 @@ module Castle
|
|
63
64
|
self.api_secret = ENV.fetch('CASTLE_API_SECRET', '')
|
64
65
|
self.ip_headers = [].freeze
|
65
66
|
self.trusted_proxies = [].freeze
|
67
|
+
self.trust_proxy_chain = false
|
68
|
+
self.trusted_proxy_depth = nil
|
66
69
|
end
|
67
70
|
|
68
71
|
def api_secret=(value)
|
@@ -86,13 +89,18 @@ module Castle
|
|
86
89
|
end
|
87
90
|
|
88
91
|
# sets trusted proxies
|
89
|
-
# @param value [Array<String
|
92
|
+
# @param value [Array<String,Regexp>]
|
90
93
|
def trusted_proxies=(value)
|
91
94
|
raise Castle::ConfigurationError, 'trusted proxies must be an Array' unless value.is_a?(Array)
|
92
95
|
|
93
96
|
@trusted_proxies = value
|
94
97
|
end
|
95
98
|
|
99
|
+
# @param value [String,Number,NilClass]
|
100
|
+
def trusted_proxy_depth=(value)
|
101
|
+
@trusted_proxy_depth = value.to_i
|
102
|
+
end
|
103
|
+
|
96
104
|
def valid?
|
97
105
|
!api_secret.to_s.empty? && !host.to_s.empty? && !port.to_s.empty?
|
98
106
|
end
|
data/lib/castle/extractors/ip.rb
CHANGED
@@ -6,6 +6,8 @@ module Castle
|
|
6
6
|
class IP
|
7
7
|
# ordered list of ip headers for ip extraction
|
8
8
|
DEFAULT = %w[X-Forwarded-For Remote-Addr].freeze
|
9
|
+
# list of header which are used with proxy depth setting
|
10
|
+
DEPTH_RELATED = %w[X-Forwarded-For].freeze
|
9
11
|
|
10
12
|
private_constant :DEFAULT
|
11
13
|
|
@@ -14,6 +16,8 @@ module Castle
|
|
14
16
|
@headers = headers
|
15
17
|
@ip_headers = Castle.config.ip_headers.empty? ? DEFAULT : Castle.config.ip_headers
|
16
18
|
@proxies = Castle.config.trusted_proxies + Castle::Configuration::TRUSTED_PROXIES
|
19
|
+
@trust_proxy_chain = Castle.config.trust_proxy_chain
|
20
|
+
@trusted_proxy_depth = Castle.config.trusted_proxy_depth
|
17
21
|
end
|
18
22
|
|
19
23
|
# Order of headers:
|
@@ -26,13 +30,14 @@ module Castle
|
|
26
30
|
|
27
31
|
@ip_headers.each do |ip_header|
|
28
32
|
ips = ips_from(ip_header)
|
29
|
-
ip_value = remove_proxies(ips)
|
33
|
+
ip_value = remove_proxies(ips)
|
34
|
+
|
30
35
|
return ip_value if ip_value
|
31
36
|
|
32
37
|
all_ips.push(*ips)
|
33
38
|
end
|
34
39
|
|
35
|
-
# fallback to first
|
40
|
+
# fallback to first listed ip
|
36
41
|
all_ips.first
|
37
42
|
end
|
38
43
|
|
@@ -41,7 +46,9 @@ module Castle
|
|
41
46
|
# @param ips [Array<String>]
|
42
47
|
# @return [Array<String>]
|
43
48
|
def remove_proxies(ips)
|
44
|
-
ips.
|
49
|
+
return ips.first if @trust_proxy_chain
|
50
|
+
|
51
|
+
ips.reject { |ip| proxy?(ip) }.last
|
45
52
|
end
|
46
53
|
|
47
54
|
# @param ip [String]
|
@@ -57,7 +64,18 @@ module Castle
|
|
57
64
|
|
58
65
|
return [] unless value
|
59
66
|
|
60
|
-
value.strip.split(/[,\s]+/)
|
67
|
+
ips = value.strip.split(/[,\s]+/)
|
68
|
+
|
69
|
+
limit_proxy_depth(ips, header)
|
70
|
+
end
|
71
|
+
|
72
|
+
# @param ips [Array<String>]
|
73
|
+
# @param ip_header [String]
|
74
|
+
# @return [Array<String>]
|
75
|
+
def limit_proxy_depth(ips, ip_header)
|
76
|
+
ips.pop(@trusted_proxy_depth) if DEPTH_RELATED.include?(ip_header)
|
77
|
+
|
78
|
+
ips
|
61
79
|
end
|
62
80
|
end
|
63
81
|
end
|
data/lib/castle/version.rb
CHANGED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Castle::API::Session do
|
4
|
+
describe '#get' do
|
5
|
+
it { expect(described_class.get).to eql(described_class.get) }
|
6
|
+
it { expect(described_class.get).to eql(described_class.instance.http) }
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#initialize' do
|
10
|
+
subject(:session) { described_class.get }
|
11
|
+
|
12
|
+
after do
|
13
|
+
described_class.instance.reset
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'when ssl false' do
|
17
|
+
before do
|
18
|
+
Castle.config.host = 'localhost'
|
19
|
+
Castle.config.port = 3002
|
20
|
+
described_class.instance.reset
|
21
|
+
end
|
22
|
+
|
23
|
+
after do
|
24
|
+
Castle.config.host = Castle::Configuration::HOST
|
25
|
+
Castle.config.port = Castle::Configuration::PORT
|
26
|
+
end
|
27
|
+
|
28
|
+
it { expect(session).to be_instance_of(Net::HTTP) }
|
29
|
+
it { expect(session.address).to eq('localhost') }
|
30
|
+
it { expect(session.port).to eq(3002) }
|
31
|
+
it { expect(session.use_ssl?).to be false }
|
32
|
+
it { expect(session.verify_mode).to be_nil }
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when ssl true' do
|
36
|
+
before do
|
37
|
+
described_class.instance.reset
|
38
|
+
end
|
39
|
+
|
40
|
+
it { expect(session).to be_instance_of(Net::HTTP) }
|
41
|
+
it { expect(session.address).to eq(Castle.config.host) }
|
42
|
+
it { expect(session.port).to eq(Castle.config.port) }
|
43
|
+
it { expect(session.use_ssl?).to be true }
|
44
|
+
it { expect(session.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -44,7 +44,7 @@ describe Castle::Extractors::IP do
|
|
44
44
|
|
45
45
|
context 'with all the trusted proxies' do
|
46
46
|
let(:http_x_header) do
|
47
|
-
'127.0.0.1,10.0.0.1,172.31.0.1,192.168.0.1
|
47
|
+
'127.0.0.1,10.0.0.1,172.31.0.1,192.168.0.1'
|
48
48
|
end
|
49
49
|
|
50
50
|
let(:headers) { { 'Remote-Addr' => '127.0.0.1', 'X-Forwarded-For' => http_x_header } }
|
@@ -54,6 +54,34 @@ describe Castle::Extractors::IP do
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
+
context 'with trust_proxy_chain option' do
|
58
|
+
let(:http_x_header) do
|
59
|
+
'6.6.6.6, 2.2.2.3, 6.6.6.5'
|
60
|
+
end
|
61
|
+
|
62
|
+
let(:headers) { { 'Remote-Addr' => '6.6.6.4', 'X-Forwarded-For' => http_x_header } }
|
63
|
+
|
64
|
+
before { Castle.config.trust_proxy_chain = true }
|
65
|
+
|
66
|
+
it 'selects first available header' do
|
67
|
+
expect(extractor.call).to eql('6.6.6.6')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with trusted_proxy_depth option' do
|
72
|
+
let(:http_x_header) do
|
73
|
+
'6.6.6.6, 2.2.2.3, 6.6.6.5'
|
74
|
+
end
|
75
|
+
|
76
|
+
let(:headers) { { 'Remote-Addr' => '6.6.6.4', 'X-Forwarded-For' => http_x_header } }
|
77
|
+
|
78
|
+
before { Castle.config.trusted_proxy_depth = 1 }
|
79
|
+
|
80
|
+
it 'selects first available header' do
|
81
|
+
expect(extractor.call).to eql('2.2.2.3')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
57
85
|
context 'when list of not trusted ips provided in X_FORWARDED_FOR' do
|
58
86
|
let(:headers) do
|
59
87
|
{
|
@@ -4,18 +4,19 @@ describe Castle::HeadersFilter do
|
|
4
4
|
subject(:headers) { described_class.new(request).call }
|
5
5
|
|
6
6
|
let(:env) do
|
7
|
-
Rack::MockRequest.env_for(
|
7
|
+
result = Rack::MockRequest.env_for(
|
8
8
|
'/',
|
9
9
|
'Action-Dispatch.request.content-Type' => 'application/json',
|
10
10
|
'HTTP_AUTHORIZATION' => 'Basic 123456',
|
11
11
|
'HTTP_COOKIE' => '__cid=abcd;other=efgh',
|
12
|
-
'http-ok' => 'OK',
|
13
12
|
'HTTP_ACCEPT' => 'application/json',
|
14
13
|
'HTTP_X_FORWARDED_FOR' => '1.2.3.4',
|
15
14
|
'HTTP_USER_AGENT' => 'Mozilla 1234',
|
16
15
|
'TEST' => '1',
|
17
16
|
'REMOTE_ADDR' => '1.2.3.4'
|
18
17
|
)
|
18
|
+
result[:HTTP_OK] = 'OK'
|
19
|
+
result
|
19
20
|
end
|
20
21
|
let(:filtered) do
|
21
22
|
{
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: castle-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Johan Brissmyr
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: appraisal
|
@@ -75,6 +75,7 @@ files:
|
|
75
75
|
- spec/integration/rails/support/home_controller.rb
|
76
76
|
- spec/lib/castle/api/request_spec.rb
|
77
77
|
- spec/lib/castle/api/response_spec.rb
|
78
|
+
- spec/lib/castle/api/session_spec.rb
|
78
79
|
- spec/lib/castle/api_spec.rb
|
79
80
|
- spec/lib/castle/client_spec.rb
|
80
81
|
- spec/lib/castle/command_spec.rb
|
@@ -123,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
123
124
|
- !ruby/object:Gem::Version
|
124
125
|
version: '0'
|
125
126
|
requirements: []
|
126
|
-
rubygems_version: 3.
|
127
|
+
rubygems_version: 3.1.3
|
127
128
|
signing_key:
|
128
129
|
specification_version: 4
|
129
130
|
summary: Castle
|
@@ -148,6 +149,7 @@ test_files:
|
|
148
149
|
- spec/lib/castle/utils/merger_spec.rb
|
149
150
|
- spec/lib/castle/command_spec.rb
|
150
151
|
- spec/lib/castle/headers_formatter_spec.rb
|
152
|
+
- spec/lib/castle/api/session_spec.rb
|
151
153
|
- spec/lib/castle/api/request_spec.rb
|
152
154
|
- spec/lib/castle/api/response_spec.rb
|
153
155
|
- spec/lib/castle/commands/review_spec.rb
|