Connect to AWS IAM API from MuleSoft.
Introduction to AWS IAM
In this blog, we will see, how to access AWS Identity and Access Management API from an external system using the AWS Signature Version 4 signing and then we will access these API from MuleSoft. For this blog, I will only use Create User and Delete User API, however, the process to access any other API should be the same.
AWS Identity and Access Management (IAM) is a web service for securely controlling access to AWS services. IAM consists of Users, Groups (A way to group users and apply policy), Roles, Policy Documents (Saved as JSON). IAM is universal, does not apply to regions, the root account has complete admin access by default. It is important to note that new users have no permission when created, New User has access key, secret key, and also a password.
Prerequisite
- MuleSoft Anypoint Studio 7.1 With Mule Runtime 4.2
- Amazon Web Service Account with Access to IAM
AWS Signature Version 4
AWS suggests that to make any AWS API Call, Requests must be signed using an access key ID and a secret access key. To sign requests, AWS now recommends that we use Signature Version 4.
Signature Version 4 is the process to add authentication information to AWS requests sent by HTTP. For security, most requests to AWS must be signed with an access key, which consists of an access key ID and secret access key.
Creating a Signature with AWS Signature Version 4 comprises of 4 steps. Let us try to go through these steps here.
Task 1: Create a Canonical Request for Signature Version 4
Canonical Request is a standardized string format with the following details. AWS suggests that we should use these exact details otherwise, the request will be declined.
xxxxxxxxxx
CanonicalRequest =
HTTPRequestMethod + '\n' +
CanonicalURI + '\n' +
CanonicalQueryString + '\n' +
CanonicalHeaders + '\n' +
SignedHeaders + '\n' +
HexEncode(Hash(RequestPayload))
Now in our case, we need to make an API call to the AWS IAM API as follows:
xxxxxxxxxx
GET https://iam.amazonaws.com/?Action=CreateUser&UserName=<UserName>&Version=2010-05-08
Hence as per the above details, our request method is GET
xxxxxxxxxx
HTTPRequestMethod = GET
We have to create canonical URI--the part of the URI from domain to query string (we will use / if no path)
xxxxxxxxxx
CanonicalURI = /
We have to create a canonical query string. In our API call, request parameters are in the query string. Query string values must be URL-encoded (space=%20). The parameters must be sorted by name.
xxxxxxxxxx
canonical_querystring = 'Action=CreateUser&UserName=<UserName>&Version=2010-05-08&X-Amz-Algorithm=AWS4-HMAC-&X-Amz-Credential=<URLEncode(access_key + '/' + credential_scope)>&X-Amz-Date=<DateTimeStampInUTC>&X-Amz-Expires=30&X-Amz-SignedHeaders=host
Now there is one unknown variable that is Credential_Scope. This should be created as follows
xxxxxxxxxx
credential_scope = <DateStampInUTC>/<region>/iam/aws4_request
We have to create the canonical headers and signed headers. Header names must be trimmed and lowercase and sorted in code point order from low to high. Note trailing New Line in canonical_headers. signed_headers is the list of headers that are being included as part of the signing process. For requests that use query strings, only "host" is included in the signed headers.
xxxxxxxxxx
canonical_headers = 'host:iam.amazonaws.com<newline>'
signed_headers = 'host'
We have to create a payload hash. For GET requests, the payload is an empty string (""). So, we need to hash this with SHA-256. So in my case, the canonical request is created as
xxxxxxxxxx
GET
/
Action=CreateUser&UserName=TestAPCUser&Version=2010-05-08&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=*****************%2F20200718%2Fus-east-1%2Fiam%2Faws4_request&X-Amz-Date=20200718T211159Z&X-Amz-Expires=30&X-Amz-SignedHeaders=host
host:iam.amazonaws.com
host
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Task 2: Create a String to Sign for Signature Version 4
The string to sign includes meta-information about our request and about the canonical request that you created in the earlier steps. To create the string to sign, we will concatenate the algorithm, date and time, credential scope, and digest of the canonical request, as shown in the following pseudocode:
xxxxxxxxxx
StringToSign =
Algorithm + \n +
RequestDateTime + \n +
CredentialScope + \n +
HashedCanonicalRequest
Here let us again derive each variable.
- As we discussed earlier, we use SHA-256 for our Hash, the Algorithm is defined as algorithm = 'AWS4-HMAC-SHA256'
- RequestDateTime = <DateTimeStampInUTC>
- We have already defined the Credential Scope in our earlier section.
- The final part is again an SHA-256 hashed Canonical Request that we have retrieved earlier.
In my case, this turned out something like this.
xxxxxxxxxx
AWS4-HMAC-SHA256
20200718T211159Z
20200718/us-east-1/iam/aws4_request
a402b5461e3e5893bece467a33f434bf674a20c35f434d9059bd8c97d6cddd44
Task 3: Calculate the signature for AWS Signature Version 4
This is again a four-step process of recursive Hashing with Key. Following is the step that will define this:
xxxxxxxxxx
kSecret = <our AWS secret access key>
kDate = Sign DateStampInUTC with the key "AWS4" + kSecret
kRegion = Sign Region with the key kDate
kService = Sign Service with the key kRegion
kSigning = Sign "aws4_request" with the key kService
This is an example Python function to do this signature:
xxxxxxxxxx
def sign(key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
def getSignatureKey(key, dateStamp, regionName, serviceName):
kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp)
kRegion = sign(kDate, regionName)
kService = sign(kRegion, serviceName)
kSigning = sign(kService, 'aws4_request')
return kSigning
Finally, we will generate a signature by signing the String to Sign generated in Step 2 with the signature key generated in the above step.
For me, the signature got created something like this:
5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7
Task 4: Add the Signature to the HTTP Request
Now consider the canonical query string that we created in our step 1. For my case it was like this:
xxxxxxxxxx
canonical_querystring = 'Action=CreateUser&UserName=<UserName>&Version=2010-05-08&X-Amz-Algorithm=AWS4-HMAC-&X-Amz-Credential=<URLEncode(access_key + '/' + credential_scope)>&X-Amz-Date=<DateTimeStampInUTC>&X-Amz-Expires=30&X-Amz-SignedHeaders=host'
I will add the final Signature to this query string with the key X-Amz-Signature and then add the Host details at the beginning. For me, it turned out something like this:
xxxxxxxxxx
https://iam.amazonaws.com?Action=CreateUser&UserName=<UserName>&Version=2010-05-08&X-Amz-Algorithm=AWS4-HMAC-&X-Amz-Credential=<URLEncode(access_key + '/' + credential_scope)>&X-Amz-Date=<DateTimeStampInUTC>&X-Amz-Expires=30&X-Amz-SignedHeaders=host&X-Amz-Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7
Now, all we need to do is make an API GET call with the above URL and our requested user will be created in the AWS IAM System.
To connect from Mulesoft to this AWS IAM, please visit my blog.
This concludes this blog where we learned how to use AWS Signature Version 4 and also how to access them in MuleSoft using DataWeave. Thank you for reading my blog and do let me know if you were able to get this going. Let me know if you have any other questions as well in the comment section.