apigatewayv2_rack 0.1.2 → 0.2.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/CHANGELOG.md +11 -0
- data/Gemfile.lock +2 -2
- data/README.md +7 -0
- data/lib/apigatewayv2_rack/middlewares/cloudfront_verify.rb +33 -0
- data/lib/apigatewayv2_rack/middlewares/cloudfront_xff.rb +43 -0
- data/lib/apigatewayv2_rack/response.rb +3 -1
- data/lib/apigatewayv2_rack/version.rb +1 -1
- data/lib/apigatewayv2_rack.rb +19 -8
- 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: 04c7d7c16ea85d001867d8a248974da31c37a028af40e42ef106ee14ef70a58d
|
4
|
+
data.tar.gz: 8685f08020c4afdf89b62561401234df587ffcb0d2cf626288b615f196139541
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7ad2b28601feed547b70d35a28dbe595e3f233825fa616ac022eb8bee29194914dfd566d39e387835b855acadb2f9f599a9d8ea206dc29f1abc189d7fe65efc
|
7
|
+
data.tar.gz: 73eab07aa3d0c1360e57d41e173458a957c5fae92c41a803be4c259a3d2891af4558363fde38198a39fa500805f4df478c9f8a44aabfc51509de8fb4e0b2cb83
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
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
|
+
|
9
|
+
## [0.1.3] - 2023-03-22
|
10
|
+
|
11
|
+
- Fixed Errno::EACCES from StringIO when a streaming body (body does not respond to `#each`) is returned
|
12
|
+
- Raise error when a response body is nil
|
13
|
+
|
3
14
|
## [0.1.2] - 2023-03-22
|
4
15
|
|
5
16
|
- Fixed Apigatewayv2Rack.handler_from_rack_config_file didn't work well with Rack 3.
|
data/Gemfile.lock
CHANGED
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
|
@@ -33,6 +33,8 @@ module Apigatewayv2Rack
|
|
33
33
|
|
34
34
|
private def consume_body
|
35
35
|
case
|
36
|
+
when body.nil?
|
37
|
+
raise TypeError, "Rack app returned nil body"
|
36
38
|
# FIXME: Rack::CommonLogger uses Rack::BodyProxy, which performs logging when body is closed, is not compatible with #to_ary on Rack 3 specification
|
37
39
|
# when body.respond_to?(:to_ary)
|
38
40
|
# body.to_ary.join
|
@@ -42,7 +44,7 @@ module Apigatewayv2Rack
|
|
42
44
|
body.close if body.respond_to?(:close)
|
43
45
|
buf
|
44
46
|
else
|
45
|
-
stream = StringIO.new(
|
47
|
+
stream = StringIO.new(String.new, 'w')
|
46
48
|
body.call(stream)
|
47
49
|
stream.string
|
48
50
|
end
|
data/lib/apigatewayv2_rack.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
20
|
-
|
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.
|
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-
|
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.
|
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
|