-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(aws-lambda) add support for assume role based on metadata creden…
…tials (#8900) This PR adds support for assuming role based on EC2/ECS metadata credentials. Currently, the aws-lambda plugin does not support cross-account invoking. To achieve that we need to add support for assuming roles based on EC2/ECS metadata credentials. After fetching the metadata credentials, it'll make an additional request to AWS's STS service to ask to assume role. Codes in the `iam-sts-credentials.lua` originated from an [old PR](Kong/kong-plugin-aws-lambda#42), I re-arranged it and picked up part of the function that I need. Note that this is a short term plan to support FTI-3291 as it does not modify much of the code. Hoping to catch the last train of Version 3.0. In the long term perspective, I think all the codes related to fetching AWS credentials should be rebuilt based on https://github.com/Kong/lua-resty-aws, the library has complete support for AWS environment credential functions(ENV vars, configs, etc.)
- Loading branch information
Showing
5 changed files
with
232 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
local http = require "resty.http" | ||
local json = require "cjson" | ||
local aws_v4 = require "kong.plugins.aws-lambda.v4" | ||
local utils = require "kong.tools.utils" | ||
local ngx_now = ngx.now | ||
local kong = kong | ||
|
||
local DEFAULT_SESSION_DURATION_SECONDS = 3600 | ||
local DEFAULT_HTTP_CLINET_TIMEOUT = 60000 | ||
local DEFAULT_ROLE_SESSION_NAME = "kong" | ||
|
||
|
||
local function get_regional_sts_endpoint(aws_region) | ||
if aws_region then | ||
return 'sts.' .. aws_region .. '.amazonaws.com' | ||
else | ||
return 'sts.amazonaws.com' | ||
end | ||
end | ||
|
||
|
||
local function fetch_assume_role_credentials(aws_region, assume_role_arn, | ||
role_session_name, access_key, | ||
secret_key, session_token) | ||
if not assume_role_arn then | ||
return nil, "Missing required parameter 'assume_role_arn' for fetching STS credentials" | ||
end | ||
|
||
role_session_name = role_session_name or DEFAULT_ROLE_SESSION_NAME | ||
|
||
kong.log.debug('Trying to assume role [', assume_role_arn, ']') | ||
|
||
local sts_host = get_regional_sts_endpoint(aws_region) | ||
|
||
-- build the url and signature to assume role | ||
local assume_role_request_headers = { | ||
Accept = "application/json", | ||
["Content-Type"] = "application/x-www-form-urlencoded; charset=utf-8", | ||
["X-Amz-Security-Token"] = session_token, | ||
Host = sts_host | ||
} | ||
|
||
local assume_role_query_params = { | ||
Action = "AssumeRole", | ||
Version = "2011-06-15", | ||
RoleArn = assume_role_arn, | ||
DurationSeconds = DEFAULT_SESSION_DURATION_SECONDS, | ||
RoleSessionName = role_session_name, | ||
} | ||
|
||
local assume_role_sign_params = { | ||
region = aws_region, | ||
service = "sts", | ||
access_key = access_key, | ||
secret_key = secret_key, | ||
method = "GET", | ||
host = sts_host, | ||
port = 443, | ||
headers = assume_role_request_headers, | ||
query = utils.encode_args(assume_role_query_params) | ||
} | ||
|
||
local request, err | ||
request, err = aws_v4(assume_role_sign_params) | ||
|
||
if err then | ||
return nil, 'Unable to build signature to assume role [' | ||
.. assume_role_arn .. '] - error :'.. tostring(err) | ||
end | ||
|
||
-- Call STS to assume role | ||
local client = http.new() | ||
client:set_timeout(DEFAULT_HTTP_CLINET_TIMEOUT) | ||
local res, err = client:request_uri(request.url, { | ||
method = request.method, | ||
headers = request.headers, | ||
ssl_verify = false, | ||
}) | ||
|
||
if err then | ||
local err_s = 'Unable to assume role [' .. assume_role_arn .. ']' .. | ||
' due to: ' .. tostring(err) | ||
return nil, err_s | ||
end | ||
|
||
if res.status ~= 200 then | ||
local err_s = 'Unable to assume role [' .. assume_role_arn .. '] due to:' .. | ||
'status [' .. res.status .. '] - ' .. | ||
'reason [' .. res.body .. ']' | ||
return nil, err_s | ||
end | ||
|
||
local credentials = json.decode(res.body).AssumeRoleResponse.AssumeRoleResult.Credentials | ||
local result = { | ||
access_key = credentials.AccessKeyId, | ||
secret_key = credentials.SecretAccessKey, | ||
session_token = credentials.SessionToken, | ||
expiration = credentials.Expiration | ||
} | ||
|
||
return result, nil, result.expiration - ngx_now() | ||
end | ||
|
||
|
||
return { | ||
fetch_assume_role_credentials = fetch_assume_role_credentials, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
72 changes: 72 additions & 0 deletions
72
spec/03-plugins/27-aws-lambda/07-iam-sts-credentials_spec.lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
require "spec.helpers" | ||
|
||
describe("[AWS Lambda] iam-sts", function() | ||
|
||
local fetch_sts_assume_role, http_responses | ||
|
||
before_each(function() | ||
package.loaded["kong.plugins.aws-lambda.iam-sts-credentials"] = nil | ||
package.loaded["resty.http"] = nil | ||
local http = require "resty.http" | ||
-- mock the http module | ||
http.new = function() | ||
return { | ||
set_timeout = function() end, | ||
request_uri = function() | ||
local body = http_responses[1] | ||
table.remove(http_responses, 1) | ||
return { | ||
status = 200, | ||
body = body, | ||
} | ||
end, | ||
} | ||
end | ||
fetch_sts_assume_role = require("kong.plugins.aws-lambda.iam-sts-credentials").fetch_assume_role_credentials | ||
end) | ||
|
||
after_each(function() | ||
end) | ||
|
||
it("should fetch credentials from sts service", function() | ||
http_responses = { | ||
[[ | ||
{ | ||
"AssumeRoleResponse": { | ||
"AssumeRoleResult": { | ||
"SourceIdentity": "kong_session", | ||
"AssumedRoleUser": { | ||
"Arn": "arn:aws:iam::000000000001:role/temp-role", | ||
"AssumedRoleId": "arn:aws:iam::000000000001:role/temp-role" | ||
}, | ||
"Credentials": { | ||
"AccessKeyId": "the Access Key", | ||
"SecretAccessKey": "the Big Secret", | ||
"SessionToken": "the Token of Appreciation", | ||
"Expiration": 1552424170 | ||
}, | ||
"PackedPolicySize": 1000 | ||
}, | ||
"ResponseMetadata": { | ||
"RequestId": "c6104cbe-af31-11e0-8154-cbc7ccf896c7" | ||
} | ||
} | ||
} | ||
]] | ||
} | ||
|
||
local aws_region = "ap-east-1" | ||
local assume_role_arn = "arn:aws:iam::000000000001:role/temp-role" | ||
local role_session_name = "kong_session" | ||
local access_key = "test_access_key" | ||
local secret_key = "test_secret_key" | ||
local session_token = "test_session_token" | ||
local iam_role_credentials, err = fetch_sts_assume_role(aws_region, assume_role_arn, role_session_name, access_key, secret_key, session_token) | ||
|
||
assert.is_nil(err) | ||
assert.equal("the Access Key", iam_role_credentials.access_key) | ||
assert.equal("the Big Secret", iam_role_credentials.secret_key) | ||
assert.equal("the Token of Appreciation", iam_role_credentials.session_token) | ||
assert.equal(1552424170, iam_role_credentials.expiration) | ||
end) | ||
end) |