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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 597a4ac18e086d61da2f12af123772abb29ed477693384a82d1da08118fe416b
4
- data.tar.gz: 3478f6a1432422fddc390f94be0be3f8a3b56dedf4c24522bac07df9a9651218
3
+ metadata.gz: 0dd544ffa6fc2660fc67c058011df5b9d83a8078b48506ab611809166960f501
4
+ data.tar.gz: 1720630a7f1925ba1142208114198cf176a8a3fec5c04be480aa4d2384e03de2
5
5
  SHA512:
6
- metadata.gz: 7487f023f013238558bd36f26fed0ae800b7d720b365870264dc15210a1020f14a97d9c99d019a9774d9884a23485df63482179fba78049f26f47907f2af329e
7
- data.tar.gz: adedcf7725c0d586dae9e230294b424116163b065653c54de473a5685a8a9213762204a8678b7582fdff7a0fb809bf830fd726efe2239aa6bfbd3e917916ab0b
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
- # we try to fetch proper ip based on X-Forwarded-For or Remote-Addr headers in that order
91
- # but sometimes proper ip may be stored in different header or order could be different.
92
- # SDK can extract ip automatically for you, but you must configure which ip_headers you would like to use
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
- # *Note: proxies list can be provided as an array of regular expressions
100
- # *Note: default always marked as trusty list is here: Castle::Configuration::TRUSTED_PROXIES
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
 
@@ -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, :trusted_proxies
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|Regexp>]
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
@@ -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).last
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 whatever ip
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.reject { |ip| proxy?(ip) }
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Castle
4
- VERSION = '4.2.1'
4
+ VERSION = '4.3.0'
5
5
  end
@@ -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,::1,fd00::,localhost,unix,unix:/tmp/sock'
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.2.1
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-04-07 00:00:00.000000000 Z
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.0.6
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