apigatewayv2_rack 0.1.3 → 0.2.0

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
  SHA256:
3
- metadata.gz: b9c5a5dcc23b7a6e524d7a8c847befb580b8771839ae013c977991e35db824ef
4
- data.tar.gz: e33794f5ffae56ad06cb14f12fcac3b3a41ad5e33f6119a9c262756801d9f3b6
3
+ metadata.gz: 04c7d7c16ea85d001867d8a248974da31c37a028af40e42ef106ee14ef70a58d
4
+ data.tar.gz: 8685f08020c4afdf89b62561401234df587ffcb0d2cf626288b615f196139541
5
5
  SHA512:
6
- metadata.gz: 6a27469fcbc9c1bc754aca949ed94fce1ec83f4e1cd5909fa70bee771267dd480ef12f78d69508205913f88c23bc4082a149279975ec6e5ed8897c0d5e777fbd
7
- data.tar.gz: 88ff099fa17b319cf2f1cedd08091eeeff14874a7c9fa9eac37430e4ed98b2e51b7e2f1b2bf6146cb215730c637cbc639016ecd1b64e6759e96c26892c541491
6
+ metadata.gz: e7ad2b28601feed547b70d35a28dbe595e3f233825fa616ac022eb8bee29194914dfd566d39e387835b855acadb2f9f599a9d8ea206dc29f1abc189d7fe65efc
7
+ data.tar.gz: 73eab07aa3d0c1360e57d41e173458a957c5fae92c41a803be4c259a3d2891af4558363fde38198a39fa500805f4df478c9f8a44aabfc51509de8fb4e0b2cb83
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2023-03-24
4
+
5
+ - `Apigatewayv2Rack.handle_request` now takes a block and pass rack env and `Apigatewayv2Rack::Request` object to allow final modification before passing env to a Rack app.
6
+ - `Apigatewayv2Rack.generate_handler` and `handler_from_rack_config_file` propagates given block to `handle_request` for the enhancement above.
7
+ - Introduce `Apigatewayv2Rack::Middlewares::CloudfrontXff` and `Apigatewayv2Rack::Middlewares::CloudfrontVerify` as a helper middleware.
8
+
3
9
  ## [0.1.3] - 2023-03-22
4
10
 
5
11
  - Fixed Errno::EACCES from StringIO when a streaming body (body does not respond to `#each`) is returned
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- apigatewayv2_rack (0.1.3)
4
+ apigatewayv2_rack (0.2.0)
5
5
  rack
6
6
 
7
7
  GEM
@@ -33,4 +33,4 @@ DEPENDENCIES
33
33
  rspec (~> 3.0)
34
34
 
35
35
  BUNDLED WITH
36
- 2.3.16
36
+ 2.3.21
data/README.md CHANGED
@@ -43,6 +43,13 @@ p resp.as_json
43
43
 
44
44
  See [./Dockerfile.integration](./Dockerfile.integration) and [./integration](./integration).
45
45
 
46
+ ### Middlewares
47
+
48
+ This gem includes several utility middlewares:
49
+
50
+ - [CloudfrontVerify](./lib/apigatewayv2_rack/middlewares/cloudfront_verify.rb): Verify `x-origin-header` value to protect unwanted direct access.
51
+ - [CloudfrontXff](./lib/apigatewayv2_rack/middlewares/cloudfront_xff.rb): Respect `cloudfront-viewer-address` as `x-forwarded-for` value.
52
+
46
53
  ## Development
47
54
 
48
55
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ require 'rack/utils'
3
+
4
+ module Apigatewayv2Rack
5
+ module Middlewares
6
+ # Compare X-Origin-Verify header matches the expected value and otherwise returns 403.
7
+ # This is useful to use with CloudFront's origin custom request header to protect from direct access to function.
8
+ #
9
+ # See also: https://www.wellarchitectedlabs.com/security/300_labs/300_multilayered_api_security_with_cognito_and_waf/3_prevent_requests_from_accessing_api_directly/
10
+ class CloudfrontVerify
11
+ # +value+ is an expected string value of x-origin-verify.
12
+ def initialize(app, value)
13
+ @app = app
14
+ @value = value
15
+ end
16
+
17
+ def env_name
18
+ 'HTTP_X_ORIGIN_VERIFY'
19
+ end
20
+
21
+ def call(env)
22
+ given = env[env_name]
23
+
24
+ unless given && Rack::Utils.secure_compare(given, @value)
25
+ env['rack.logger']&.warn("#{self.class.name} protected unwanted access from #{env['REMOTE_ADDR'].inspect}")
26
+ return [401, {'Content-Type' => 'text/plain'}, ['Unauthorized']]
27
+ end
28
+
29
+ @app.call(env)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apigatewayv2Rack
4
+ module Middlewares
5
+ # Apigatewayv2Rack::Middlewares::CloudfrontXff transforms cloudfront-viewer-address to x-forwarded-for value.
6
+ # It is recommended to use with Apigatewayv2Rack::Middlewares::CloudfrontVerify.
7
+ class CloudfrontXff
8
+ # When +replace_remote_addr_with+ is set, REMOTE_ADDR will be replaced with given value; this
9
+ # allows Rack::Request#ip to respect xff on its default ip_filter. Default to 127.0.0.1.
10
+ def initialize(app, replace_remote_addr_with: '127.0.0.1')
11
+ @app = app
12
+ @replace_remote_addr_with = replace_remote_addr_with
13
+ end
14
+
15
+ V6_REGEXP = /^([a-f0-9:]+):(\d+)$/
16
+
17
+ def call(env)
18
+ viewer = env['HTTP_CLOUDFRONT_VIEWER_ADDRESS']
19
+ if viewer
20
+ addr,port = if viewer.include?('.')
21
+ viewer.split(?:, 2)
22
+ else
23
+ viewer.downcase.match(V6_REGEXP)&.to_a[1,2]
24
+ end
25
+
26
+ if addr && port
27
+ env['HTTP_X_APIGATEWAYV2RACK_ORIG_X_FORWARDED_FOR'] = env['HTTP_X_FORWARDED_FOR'] if env['HTTP_X_FORWARDED_FOR']
28
+ env['HTTP_X_APIGATEWAYV2RACK_ORIG_X_FORWARDED_PORT'] = env['HTTP_X_FORWARDED_PORT'] if env['HTTP_X_FORWARDED_PORT']
29
+ env['HTTP_X_FORWARDED_FOR'] = addr
30
+ env['HTTP_X_FORWARDED_PORT'] = port
31
+
32
+ if @replace_remote_addr_with
33
+ env['HTTP_X_APIGATEWAYV2RACK_ORIG_REMOTE_ADDR'] = env['REMOTE_ADDR'] if env['REMOTE_ADDR']
34
+ env['REMOTE_ADDR'] = @replace_remote_addr_with
35
+ end
36
+ end
37
+ end
38
+
39
+ @app.call(env)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Apigatewayv2Rack
4
- VERSION = "0.1.3"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -5,30 +5,41 @@ require_relative "apigatewayv2_rack/error"
5
5
  require_relative "apigatewayv2_rack/request"
6
6
  require_relative "apigatewayv2_rack/response"
7
7
 
8
+ require_relative "apigatewayv2_rack/middlewares/cloudfront_xff"
9
+ require_relative "apigatewayv2_rack/middlewares/cloudfront_verify"
10
+
8
11
  module Apigatewayv2Rack
9
12
  # Takes Rack +app+, Lambda +event+ and +context+ of API Gateway V2 event and
10
13
  # returns a HTTP response from +app+ as API Gateway V2 Lambda event format.
11
- def self.handle_request(app:, event:, context:, request_options: {})
14
+ #
15
+ # When block is given, converted Rack env will be passed to make some final
16
+ # modification before passing it to an +app+.
17
+ def self.handle_request(app:, event:, context:, request_options: {}, &block)
12
18
  req = Request.new(event, context, **request_options)
13
- status, headers, body = app.call(req.to_h)
19
+ env = req.to_h
20
+ block&.call(env, req)
21
+ status, headers, body = app.call(env)
14
22
  Response.new(status: status, headers: headers, body: body, elb: req.elb?, multivalued: req.multivalued?).as_json
15
23
  end
16
24
 
17
25
  module Handler
18
- attr_reader :app
19
- def handle(event:, context:)
20
- Apigatewayv2Rack.handle_request(event: event, context: context, app: @app)
26
+ attr_reader :app
27
+ attr_reader :block
28
+ def handle(event:, context:, &givenblock)
29
+ b = givenblock || @block
30
+ Apigatewayv2Rack.handle_request(event: event, context: context, app: @app, &b)
21
31
  end
22
32
  end
23
33
 
24
- def self.generate_handler(app)
34
+ def self.generate_handler(app, &block)
25
35
  m = Module.new
26
36
  m.extend(Handler)
27
37
  m.instance_variable_set(:@app, app)
38
+ m.instance_variable_set(:@block, block)
28
39
  m
29
40
  end
30
41
 
31
- def self.handler_from_rack_config_file(path = './config.ru')
42
+ def self.handler_from_rack_config_file(path = './config.ru', &block)
32
43
  require 'rack'
33
44
  require 'rack/builder'
34
45
  app = if Rack.release[0] == '2'
@@ -36,6 +47,6 @@ module Apigatewayv2Rack
36
47
  else
37
48
  Rack::Builder.load_file(path)
38
49
  end
39
- generate_handler(app)
50
+ generate_handler(app, &block)
40
51
  end
41
52
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apigatewayv2_rack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sorah Fukumori
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-03-21 00:00:00.000000000 Z
11
+ date: 2023-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -50,6 +50,8 @@ files:
50
50
  - integration/template.jsonnet
51
51
  - lib/apigatewayv2_rack.rb
52
52
  - lib/apigatewayv2_rack/error.rb
53
+ - lib/apigatewayv2_rack/middlewares/cloudfront_verify.rb
54
+ - lib/apigatewayv2_rack/middlewares/cloudfront_xff.rb
53
55
  - lib/apigatewayv2_rack/request.rb
54
56
  - lib/apigatewayv2_rack/response.rb
55
57
  - lib/apigatewayv2_rack/version.rb
@@ -76,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
78
  - !ruby/object:Gem::Version
77
79
  version: '0'
78
80
  requirements: []
79
- rubygems_version: 3.4.0.dev
81
+ rubygems_version: 3.1.6
80
82
  signing_key:
81
83
  specification_version: 4
82
84
  summary: handle AWS Lambda API Gateway V2 or ALB (ELBv2) lambda request event with