diff --git a/docs/modules/resty.aws.utils.html b/docs/modules/resty.aws.utils.html index 78d2af5..41d9f5d 100644 --- a/docs/modules/resty.aws.utils.html +++ b/docs/modules/resty.aws.utils.html @@ -84,6 +84,10 @@

Functions

Utils.getIDMSMetadata (subpath, version) Fetches IDMS Metadata (EC2 and EKS). + + Utils.getInstanceSignature () + Fetches the instance identity document and signature from IDMS. +
@@ -190,6 +194,22 @@

Returns:

+ + +
+ + Utils.getInstanceSignature () +
+
+ Fetches the instance identity document and signature from IDMS. + Will make a call to the AWS identity endpoint and hence might timeout if ran on anything + else than an EC2-instance. +

Returns:

+
    + body & content-type (if json, the body will be decoded to a Lua table), or nil+err +
+
+ diff --git a/spec/01-generic/04-utils_spec.lua b/spec/01-generic/04-utils_spec.lua new file mode 100644 index 0000000..cdc87b9 --- /dev/null +++ b/spec/01-generic/04-utils_spec.lua @@ -0,0 +1,58 @@ +local utils = require "resty.aws.utils" + +describe("Utils:getInstanceSignature", function() + local mock_token = "mock-imds-token-12345" + local mock_signature = "-----BEGIN PKCS7-----\nMIAGCSqGSIb3DQEHAqCAMIACAQExCzAJ\n-----END PKCS7-----\n" + local expected_signature = "-----BEGIN PKCS7-----MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJ-----END PKCS7-----" + + local http = { + new = function() + return { + connect = function() return true end, + set_timeouts = function() return true end, + close = function() end, + request_uri = function(self, uri, opts) + if string.find(uri, "/latest/api/token") then + ngx.log(ngx.ERR, "Returning mock IMDSv2 token") + return { + status = 200, + headers = { + ["Content-Type"] = "application/xml", + }, + body = mock_token + } + elseif string.find(uri, "/rsa2048") then + return { + status = 200, + headers = { + ["Content-Type"] = "application/xml", + }, + body = mock_signature + } + end + end, + } + end + } + + setup(function() + package.loaded["resty.luasocket.http"] = http + end) + + teardown(function() + package.loaded["resty.luasocket.http"] = nil + end) + + before_each(function() + package.loaded["resty.luasocket.http"] = http + package.loaded["resty.aws.utils"] = nil + utils = require "resty.aws.utils" + end) + + it("fetches instance signature successfully with IMDSv2", function() + local signature, err = utils.getInstanceSignature() + + assert.is_nil(err) + assert.equals(expected_signature, signature) + end) +end) diff --git a/src/resty/aws/utils.lua b/src/resty/aws/utils.lua index 8e078e9..ba5df23 100644 --- a/src/resty/aws/utils.lua +++ b/src/resty/aws/utils.lua @@ -294,6 +294,26 @@ do -- getCurrentRegion return detected_region, detected_error end + + + --- Retrieves the RSA2048 signature from EC2 instance metadata. + -- @return signature string, or nil+err + function Utils.getInstanceSignature() + local headers = { ["X-aws-ec2-metadata-token-ttl-seconds"] = "21600" } + local token, err = make_request(IDMS_URI .. "/latest/api/token", "PUT", headers) + if not token then + return nil, "failed getting IDMSToken: " .. tostring(err) + end + + headers = { ["X-aws-ec2-metadata-token"] = token } + local signature, err = make_request(IDMS_URI .. "/latest/dynamic/instance-identity/rsa2048", "GET", headers) + if not signature then + return nil, "failed getting instance signature: " .. tostring(err) + end + + signature = signature:gsub("\n", "") + return signature + end end return Utils