# HG changeset patch # User Matthew Wild # Date 1570552370 -3600 # Node ID 1d719d4ef18f8811e525f319162f9eb47b45ef3e # Parent a07bd12fe554f7ebed1f329053c40cd082cea0dc mod_aws_profile: New module for role-based access to AWS APIs diff -r a07bd12fe554 -r 1d719d4ef18f mod_aws_profile/README.markdown --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_aws_profile/README.markdown Tue Oct 08 17:32:50 2019 +0100 @@ -0,0 +1,37 @@ +# Introduction + +This module adds support for reading AWS IAM access credentials from EC2 instance metadata, +to allow Prosody modules to gain role-based access to AWS services. + +# Configuring + +``` {.lua} +modules_enabled = { + "aws_profile"; +} +``` + +There is no other configuration. + +# Usage in other modules + +Other modules can import the credentials as a shared table: + +``` {.lua} +local aws_credentials = module:shared("/*/aws_profile/credentials"); +do_something(aws_credentials.access_key, aws_credentials.secret_key); +``` + +Note that credentials are time-limited, and will change periodically. The +shared table will automatically be updated. If you need to know when this +happens, you can also hook the `'aws_profile/credentials-refreshed'` event: + +``` {.lua} +module:hook_global("aws_profile/credentials-refreshed", function (new_credentials) + -- do something with new_credentials.access_key/secret_key +end); +``` + +# Compatibility + +Meant for use with Prosody 0.11.x, may work in older versions. diff -r a07bd12fe554 -r 1d719d4ef18f mod_aws_profile/mod_aws_profile.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_aws_profile/mod_aws_profile.lua Tue Oct 08 17:32:50 2019 +0100 @@ -0,0 +1,59 @@ +local http = require "net.http"; +local json = require "util.json"; +local parse_timestamp = require "util.datetime".parse; + +module:set_global(); + +local current_credentials = module:shared("/*/aws_profile/credentials"); + +local function get_role_credentials(role_name, cb) + http.request("http://169.254.169.254/latest/meta-data/iam/security-credentials/"..role_name, nil, function (credentials_json) + local credentials = credentials_json and json.decode(credentials_json); + if not credentials or not (credentials.AccessKeyId and credentials.SecretAccessKey) then + module:log("warn", "Failed to fetch credentials for %q", role_name); + cb(nil); + return; + end + local expiry = parse_timestamp(credentials.Expiration); + local ttl = os.difftime(expiry, os.time()); + cb({ + access_key = credentials.AccessKeyId; + secret_key = credentials.SecretAccessKey; + ttl = ttl; + expiry = expiry; + }); + end); +end + +local function get_credentials(cb) + http.request("http://169.254.169.254/latest/meta-data/iam/security-credentials", nil, function (role_name) + role_name = role_name and role_name:match("%S+"); + if not role_name then + module:log("warn", "Unable to discover role name"); + cb(nil); + return; + end + get_role_credentials(role_name, cb); + end); +end + +function refresh_credentials(force) + if not force and current_credentials.expiry and current_credentials.expiry - os.time() > 300 then + return; + end + get_credentials(function (credentials) + if not credentials then + module:log("warn", "Failed to refresh credentials!"); + return; + end + current_credentials.access_key = credentials.access_key; + current_credentials.secret_key = credentials.secret_key; + current_credentials.expiry = credentials.expiry; + module:timer(credentials.ttl or 240, refresh_credentials); + module:fire_event("aws_profile/credentials-refreshed", current_credentials); + end); +end + +function module.load() + refresh_credentials(true); +end