apigatewayv2_rack 0.1.3 → 0.2.1
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 +10 -0
- data/Gemfile.lock +20 -16
- data/README.md +7 -0
- data/apigatewayv2_rack.gemspec +2 -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/request.rb +3 -1
- data/lib/apigatewayv2_rack/version.rb +1 -1
- data/lib/apigatewayv2_rack.rb +19 -8
- metadata +33 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dcad9826619202901586bc17c2dddd75779e75bbfdb194a461c853c49fc8798d
|
4
|
+
data.tar.gz: c06b602502a62c1e069d3d88c5180afa47660db4344618d73c311a3503a77f3e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d45ecda50b157c99dc848d12c79a8bd9e4dc6d9d5f649f8908facfb3e8854bd9974be14427ab51af5b309070b94030b316a600b669066888a955ee6f1cb9ed7
|
7
|
+
data.tar.gz: 2507eb06ba132526f2e405538f3fd07adedb03c53051798a3e3cccae35b87c9bbc3838c5876f4efb63975b1e9ef3e76e1842da2bf86492e84b224aba58de013b
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.2.1] - 2025-03-07
|
4
|
+
|
5
|
+
- Address `Rack::VERSION` removal.
|
6
|
+
|
7
|
+
## [0.2.0] - 2023-03-24
|
8
|
+
|
9
|
+
- `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.
|
10
|
+
- `Apigatewayv2Rack.generate_handler` and `handler_from_rack_config_file` propagates given block to `handle_request` for the enhancement above.
|
11
|
+
- Introduce `Apigatewayv2Rack::Middlewares::CloudfrontXff` and `Apigatewayv2Rack::Middlewares::CloudfrontVerify` as a helper middleware.
|
12
|
+
|
3
13
|
## [0.1.3] - 2023-03-22
|
4
14
|
|
5
15
|
- Fixed Errno::EACCES from StringIO when a streaming body (body does not respond to `#each`) is returned
|
data/Gemfile.lock
CHANGED
@@ -1,28 +1,32 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
apigatewayv2_rack (0.1
|
4
|
+
apigatewayv2_rack (0.2.1)
|
5
|
+
base64
|
5
6
|
rack
|
7
|
+
stringio
|
6
8
|
|
7
9
|
GEM
|
8
10
|
remote: https://rubygems.org/
|
9
11
|
specs:
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
rspec-
|
16
|
-
rspec-
|
17
|
-
|
18
|
-
|
19
|
-
|
12
|
+
base64 (0.2.0)
|
13
|
+
diff-lcs (1.6.0)
|
14
|
+
rack (3.1.11)
|
15
|
+
rake (13.2.1)
|
16
|
+
rspec (3.13.0)
|
17
|
+
rspec-core (~> 3.13.0)
|
18
|
+
rspec-expectations (~> 3.13.0)
|
19
|
+
rspec-mocks (~> 3.13.0)
|
20
|
+
rspec-core (3.13.3)
|
21
|
+
rspec-support (~> 3.13.0)
|
22
|
+
rspec-expectations (3.13.3)
|
20
23
|
diff-lcs (>= 1.2.0, < 2.0)
|
21
|
-
rspec-support (~> 3.
|
22
|
-
rspec-mocks (3.
|
24
|
+
rspec-support (~> 3.13.0)
|
25
|
+
rspec-mocks (3.13.2)
|
23
26
|
diff-lcs (>= 1.2.0, < 2.0)
|
24
|
-
rspec-support (~> 3.
|
25
|
-
rspec-support (3.
|
27
|
+
rspec-support (~> 3.13.0)
|
28
|
+
rspec-support (3.13.2)
|
29
|
+
stringio (3.1.5)
|
26
30
|
|
27
31
|
PLATFORMS
|
28
32
|
ruby
|
@@ -33,4 +37,4 @@ DEPENDENCIES
|
|
33
37
|
rspec (~> 3.0)
|
34
38
|
|
35
39
|
BUNDLED WITH
|
36
|
-
2.3.
|
40
|
+
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-verify` 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.
|
data/apigatewayv2_rack.gemspec
CHANGED
@@ -31,6 +31,8 @@ Gem::Specification.new do |spec|
|
|
31
31
|
# Uncomment to register a new dependency of your gem
|
32
32
|
# spec.add_dependency "example-gem", "~> 1.0"
|
33
33
|
spec.add_dependency 'rack'
|
34
|
+
spec.add_dependency 'base64'
|
35
|
+
spec.add_dependency 'stringio'
|
34
36
|
|
35
37
|
# For more information and examples about making a new gem, check out our
|
36
38
|
# guide at: https://bundler.io/guides/creating_gem.html
|
@@ -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
|
@@ -81,6 +81,8 @@ module Apigatewayv2Rack
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
+
RACK_VERSION = defined?(Rack::VERSION) ? Rack::VERSION : Rack.release
|
85
|
+
|
84
86
|
def to_h
|
85
87
|
{
|
86
88
|
'SERVER_PROTOCOL' => protocol,
|
@@ -93,7 +95,7 @@ module Apigatewayv2Rack
|
|
93
95
|
'CONTENT_LENGTH' => body.bytesize.to_s,
|
94
96
|
'CONTENT_TYPE' => headers['content-type'] || '',
|
95
97
|
'REMOTE_ADDR' => source_ip,
|
96
|
-
'rack.version' =>
|
98
|
+
'rack.version' => RACK_VERSION,
|
97
99
|
'rack.url_scheme' => (use_x_forwarded_host && headers['x-forwarded-proto']) || 'https',
|
98
100
|
'rack.input' => StringIO.new(body),
|
99
101
|
'rack.errors' => $stderr,
|
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.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sorah Fukumori
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -24,6 +24,34 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: base64
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: stringio
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
27
55
|
description:
|
28
56
|
email:
|
29
57
|
- her@sorah.jp
|
@@ -50,6 +78,8 @@ files:
|
|
50
78
|
- integration/template.jsonnet
|
51
79
|
- lib/apigatewayv2_rack.rb
|
52
80
|
- lib/apigatewayv2_rack/error.rb
|
81
|
+
- lib/apigatewayv2_rack/middlewares/cloudfront_verify.rb
|
82
|
+
- lib/apigatewayv2_rack/middlewares/cloudfront_xff.rb
|
53
83
|
- lib/apigatewayv2_rack/request.rb
|
54
84
|
- lib/apigatewayv2_rack/response.rb
|
55
85
|
- lib/apigatewayv2_rack/version.rb
|
@@ -76,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
106
|
- !ruby/object:Gem::Version
|
77
107
|
version: '0'
|
78
108
|
requirements: []
|
79
|
-
rubygems_version: 3.4.
|
109
|
+
rubygems_version: 3.4.6
|
80
110
|
signing_key:
|
81
111
|
specification_version: 4
|
82
112
|
summary: handle AWS Lambda API Gateway V2 or ALB (ELBv2) lambda request event with
|