Skip to content

Commit e0bdca4

Browse files
authored
Merge pull request #43 from LEGO/hafstroem/tests
test: add integration tests
2 parents 9691311 + 8572243 commit e0bdca4

File tree

3 files changed

+246
-2
lines changed

3 files changed

+246
-2
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ jobs:
2020
args: kong spec
2121
unit-tests:
2222
needs: linting
23-
name: Running unit tests.
23+
name: Running tests.
2424
runs-on: ubuntu-latest
2525
steps:
2626
- name: Checkout
2727
uses: actions/checkout@v3
2828
- uses: Kong/kong-pongo-action@v1
2929
with:
30-
kong_version: "2.8.x"
30+
kong_version: "3.5.x"
3131
- run: pongo run
3232

3333
build_rock:
File renamed without changes.

spec/02-integration_spec.lua

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
local helpers = require("spec.helpers")
2+
local cjson = require("cjson.safe")
3+
local openssl_hmac = require "resty.openssl.hmac"
4+
local to_hex = require"resty.string".to_hex
5+
local resty_sha256 = require "resty.sha256"
6+
7+
local PLUGIN_NAME = "aws-request-signing"
8+
9+
-- Create a new DNS mock and add some DNS records
10+
local fixtures = {
11+
http_mock = {},
12+
stream_mock = {},
13+
dns_mock = helpers.dns_mock.new()
14+
}
15+
fixtures.dns_mock:A{
16+
name = "sts.amazonaws.com",
17+
address = "127.0.0.1"
18+
}
19+
fixtures.dns_mock:A{
20+
name = "test2a.com",
21+
address = "127.0.0.1"
22+
}
23+
24+
-- This block is for mocking the call to sts.amazonaws.com
25+
fixtures.http_mock.sts_server_block = [[
26+
server {
27+
listen 443 ssl;
28+
server_name sts.amazonaws.com;
29+
ssl_certificate /kong/spec/fixtures/kong_spec.crt;
30+
ssl_certificate_key /kong/spec/fixtures/kong_spec.key;
31+
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
32+
location "/" {
33+
return 200 '{
34+
"AssumeRoleWithWebIdentityResponse":{
35+
"AssumeRoleWithWebIdentityResult":{
36+
"Credentials":{
37+
"AccessKeyId":"A",
38+
"SecretAccessKey":"B",
39+
"SessionToken":"C",
40+
"Expiration":1726572582
41+
}
42+
}
43+
}
44+
}';
45+
}
46+
}
47+
]]
48+
49+
-- This bloc is for mocking the overrided host specified in one of the tests
50+
fixtures.http_mock.test_server_block = [[
51+
server {
52+
listen 9443 ssl;
53+
server_name test2a.com;
54+
ssl_certificate /kong/spec/fixtures/kong_spec.crt;
55+
ssl_certificate_key /kong/spec/fixtures/kong_spec.key;
56+
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
57+
location /testoverride {
58+
return 200 '{"host": "$http_host", "uri": "$uri", "agent":"$http_user_agent"}';
59+
}
60+
}
61+
]]
62+
63+
-- These functions are used to calculate the signature for the request
64+
local function hmac(secret, data)
65+
return openssl_hmac.new(secret, "sha256"):final(data)
66+
end
67+
68+
local function derive_signing_key(kSecret, date, region, service)
69+
local kDate = hmac("AWS4" .. kSecret, date)
70+
local kRegion = hmac(kDate, region)
71+
local kService = hmac(kRegion, service)
72+
return hmac(kService, "aws4_request")
73+
end
74+
75+
local function hash(str)
76+
local sha256 = resty_sha256:new()
77+
sha256:update(str)
78+
return sha256:final()
79+
end
80+
81+
local function calulate_signature(headers, method, uri)
82+
local canonical_request = method .. "\n" .. uri .. "\n\nhost:" .. headers["host"] .. "\nx-amz-content-sha256:" ..
83+
headers["x-amz-content-sha256"] .. "\nx-amz-date:" .. headers["x-amz-date"] ..
84+
"\nx-amz-security-token:" .. headers["x-amz-security-token"] ..
85+
"\n\nhost;x-amz-content-sha256;x-amz-date;x-amz-security-token\n" ..
86+
headers["x-amz-content-sha256"]
87+
local string_to_sign = "AWS4-HMAC-SHA256\n" .. headers["x-amz-date"] .. "\n" ..
88+
(string.sub(headers["x-amz-date"], 1, 8)) .. "/eu-west-1/lambda/aws4_request" .. "\n" ..
89+
to_hex(hash(canonical_request))
90+
local signing_key = derive_signing_key("B", string.sub(headers["x-amz-date"], 1, 8), "eu-west-1", "lambda")
91+
local signature = to_hex(hmac(signing_key, string_to_sign))
92+
return signature
93+
end
94+
95+
-- Now orchestrate the tests
96+
for _, strategy in helpers.all_strategies() do
97+
98+
describe("Plugin: " .. PLUGIN_NAME .. ": (access) [#" .. strategy .. "]", function()
99+
local proxy_client
100+
101+
lazy_setup(function()
102+
local bp = helpers.get_db_utils(strategy, nil, {PLUGIN_NAME})
103+
104+
-- Assets for test: "should place a valid signature in headers by default"
105+
local route1 = bp.routes:insert({
106+
hosts = {"test1.com"},
107+
name = "route1"
108+
})
109+
bp.plugins:insert{
110+
name = PLUGIN_NAME,
111+
route = {
112+
id = route1.id
113+
},
114+
config = {
115+
aws_region = "eu-west-1",
116+
aws_service = "lambda",
117+
aws_assume_role_name = "test-role-name",
118+
aws_assume_role_arn = "arn:aws:iam::123456789012:role/test-role-name"
119+
}
120+
}
121+
122+
-- Assets for test: "should override host when configured"
123+
local service2 = bp.services:insert({
124+
connect_timeout = 1000,
125+
name = "service2",
126+
url = "https://test2.com:6443",
127+
retries = 0
128+
})
129+
local route2 = bp.routes:insert({
130+
name = "route2",
131+
paths = {"/testoverride"},
132+
service = service2,
133+
strip_path = false
134+
})
135+
bp.plugins:insert{
136+
name = PLUGIN_NAME,
137+
route = {
138+
id = route2.id
139+
},
140+
config = {
141+
aws_region = "eu-west-1",
142+
aws_service = "lambda",
143+
aws_assume_role_name = "test-role-name",
144+
aws_assume_role_arn = "arn:aws:iam::123456789012:role/test-role-name",
145+
override_target_host = "test2a.com",
146+
override_target_port = 9443
147+
}
148+
}
149+
150+
-- Assets for test: "should place signature information in query string when config enables it"
151+
local route3 = bp.routes:insert({
152+
hosts = {"test3.com"},
153+
name = "route3"
154+
})
155+
bp.plugins:insert{
156+
name = PLUGIN_NAME,
157+
route = {
158+
id = route3.id
159+
},
160+
config = {
161+
aws_region = "eu-west-1",
162+
aws_service = "lambda",
163+
aws_assume_role_name = "test-role-name",
164+
aws_assume_role_arn = "arn:aws:iam::123456789012:role/test-role-name",
165+
sign_query = true
166+
}
167+
}
168+
169+
assert(helpers.start_kong({
170+
database = strategy,
171+
nginx_conf = "spec/fixtures/custom_nginx.template",
172+
plugins = "bundled," .. PLUGIN_NAME
173+
}, nil, nil, fixtures))
174+
end)
175+
176+
lazy_teardown(function()
177+
helpers.stop_kong(nil, true)
178+
end)
179+
180+
before_each(function()
181+
proxy_client = helpers.proxy_client()
182+
end)
183+
184+
after_each(function()
185+
if proxy_client then
186+
proxy_client:close()
187+
end
188+
end)
189+
190+
describe("Adding signature to request", function()
191+
192+
it("should place a valid signature in headers by default", function()
193+
local res = assert(proxy_client:send{
194+
method = "GET",
195+
path = "/status/200",
196+
headers = {
197+
["Host"] = "test1.com"
198+
}
199+
})
200+
local body = assert.res_status(200, res)
201+
local json = cjson.decode(body)
202+
assert.is.truthy(json.headers["x-amz-content-sha256"])
203+
assert.is.truthy(json.headers["x-amz-date"])
204+
assert.is.truthy(json.headers["x-amz-security-token"])
205+
local calculated_signature = calulate_signature(json.headers, json.vars.request_method, json.vars.uri)
206+
local _, _, signature_from_header = string.find(json.headers["authorization"], "Signature=(.*)")
207+
assert.match(calculated_signature, signature_from_header)
208+
end)
209+
210+
it("should override host when configured", function()
211+
local res = proxy_client:get("/testoverride", {
212+
headers = {
213+
["Host"] = "test2.com"
214+
}
215+
})
216+
local body = assert.res_status(200, res)
217+
local json = cjson.decode(body)
218+
assert.match("test2a.com", json["host"])
219+
end)
220+
221+
it("should place signature information in query string when config 'sign_query' is true", function()
222+
local res = assert(proxy_client:send{
223+
method = "GET",
224+
path = "/status/200",
225+
headers = {
226+
["Host"] = "test3.com"
227+
}
228+
})
229+
local body = assert.res_status(200, res)
230+
local json = cjson.decode(body)
231+
-- the x-amz-content-sha256 will still be in the header
232+
assert.is.truthy(json.headers["x-amz-content-sha256"])
233+
-- check signature info is in the uri
234+
assert.is.truthy(json.uri_args["X-Amz-Date"])
235+
assert.is.truthy(json.uri_args["X-Amz-Security-Token"])
236+
assert.is.truthy(json.uri_args["X-Amz-Signature"])
237+
-- check signature info is in the headers
238+
assert.is.falsy(json.headers["x-amz-date"])
239+
assert.is.falsy(json.headers["x-amz-security-token"])
240+
end)
241+
242+
end)
243+
end)
244+
end

0 commit comments

Comments
 (0)