AWS Assume Role Implementation within Boomi
AWS Assume Roles allows a user to create a temporary set of credentials to be used to perform specific tasks that the assumed role has the privilege to execute. The following article outlines how to implement AWS Assume Roles with S3 within Boomi. The implementation will be for users without MFA enabled and executing S3 Put Object (Upsert). Additional S3 Actions can be executed by configuring the stringToSign variable within the Groovy script. Each AWS operation does have multiple URI request parameters that can be set. Please review AWS’s documentation for additional configuration options.
Overview
The reference implementation starts by using AWS’s Security Token Service (STS) API to request temporary credentials. AWS STS allows a user to assume a role. One of the parameters on the initial HTTP call is the Amazon Resource Name (ARN) of the role to be assumed. The response contains a temporary Access Key, a temporary Secret Access Key, and a Session Token. All three will be used in the following HTTP call to load data into S3.
The next step will set a few Dynamic Document Properties that will be used within the AWS Signing Key script. The AWS Signing Key script created a string to sign, which is made of up the HTTP Method, Canonical Uri, Canonical Query String, Canonical Headers, Signed Headers, and Hashed Payload. The output of the script is the Authorization header to be used for the HTTP calls that execute the Put Object S3 Action.
Boomi Configuration
Process Overview
Figure 1. Process Overview.
The integration has two steps: Get Assume Role Session Token and Send Data to S3 with Session Token.
AWS STS (Security Token Service)
Figure 2. Branch 1: Obtain Temporary Credentials.
Connection Configuration
First, configure an HTTP connection to obtain temporary credentials. The user’s AWS Access key and Secret Key should be populated. The rest of the configuration is constant.
Figure 3. HTTP Connection Configuration.
Table 1. HTTP Connection Configuration.
Name | Value |
---|---|
URL | https://sts.amazonaws.com |
Authentication Type | AWS Signature |
Amazon AWS Access Key | <users-aws-access-key> |
Amazon AWS Secret Key | <users-aws-secret-key> (Value is encrypted) |
Amazon AWS Service Type | Custom |
Custom AWS Service | sts |
Amazon AWS Region | <aws-region> |
Operation Configuration
The URL that will be used to get a session token will be:
https://sts.amazonaws.com/?Version=2011-06-15&Action=AssumeRole&RoleSessionName=<random-number>&RoleArn=<role-to-assume>&DurationSeconds=3600
There are additional parameters that can be added to the resource path. Those additional parameters can be found in AWS Security Token Service Documentation.
Figure 4. HTTP Operation Configuration.
Table 2. HTTP Operation Configuration.
Resource Path | Value |
---|---|
?Version= | ?Version= |
Version | Check Is replacement variable? |
&Action= | &Action= |
Action | Check Is replacement variable? |
&RoleSessionName= | &RoleSessionName= |
RoleSessionName | Check Is replacement variable? |
&RoleArn= | &RoleArn= |
RoleArn | Check Is replacement variable? |
&DurationSeconds= | &DurationSeconds= |
DurationSeconds | Check Is replacement variable? |
Parameter values are to be set on the HTTP Connector after the Operation has been created. The parameters are set within the process canvas on the Build Tab.
Figure 5. HTTP Connector Parameters.
Table 3. HTTP Connector Parameters.
Name | Value |
---|---|
Version | 2011-06-15 |
Action | AssumeRole |
RoleSessionName | Set to Unique Value |
RoleArn | arn:aws:iam::<account-id>:<role-name> |
DurationSeconds | 3600 (can be set to other values) |
Sample Response:
{
"AssumeRoleResponse": {
"AssumeRoleResult": {
"AssumedRoleUser": {
"Arn": "<assume-role>",
"AssumedRoleId": "<assume-role-id>"
},
"Credentials": {
"AccessKeyId": "<temp-access-key>",
"Expiration": 1.678127671E9,
"SecretAccessKey": "<temp-access-secret>",
"SessionToken": "<temp-session-token>
},
"PackedPolicySize": null,
"SourceIdentity": null
},
"ResponseMetadata": {
"RequestId": "e492f504-4b0f-4c78-8c3a-123123123"
}
}
}
Set Property Shape
Finally, a Set Property shape will set three Dynamic Document Properties: DPP_AWS_ACCESS_KEY, DPP_AWS_SECRET_KEY, and DPP_AWS_SESSION_TOKEN.
Dynamic Process Property | JSON Element |
---|---|
DPP_AWS_ACCESS_KEY | AccessKeyId |
DPP_AWS_SECRET_KEY | SecretAccessKey |
DPP_AWS_SESSION_TOKEN | SessionToken |
Figure 6. Set Property Shape for Session Credentials.
S3 Put Object
Once the temporary credentials have been obtained, an Authorization header can be created to Put an object in S3. A few Dynamic Document Properties are set and then a Groovy script is executed to create the S3 Authorization header.
Figure 7. Put Object: Set Properties Shape Configuration.
Table 4. Put Object: Set Properties Configuration.
Dynamic Document Property Name | Value |
---|---|
DDP_AWS_BUCKET | <s3-bucket-name> |
DDP_FILE_NAME | <file-name> |
DDP_HTTP_METHOD | PUT |
DDP_CONTENT_TYPE | i.e. text/csv |
x-amz-security-token | DPP_AWS_SESSION_TOKEN (from AWS STS request) |
S3 Authorization Header: Groovy Script
A Groovy script will create the Authorization header for sending data to S3. The values from the previous step will be used to build AWS’s string to sign and will be encrypted with the temporary AWS Secret Key. The following script does not require any additional custom libraries.
The stringToSign variable will be slightly different for each S3 Action. Refer to AWS’s documentation for the exact format of each stringToSign. Links to AWS’s documentation are within the Reference section. This Groovy script is designed only for S3 PUT.
Additional, S3 Actions can be found here:
// Groovy 2.4
// https://docs.aws.amazon.com/AmazonS3/latest/userguide/RESTAuthentication.html
/*-------------------------------------------------------
Required inputs:
DPP: DPP_AWS_ACCESS_KEY
DPP: DPP_AWS_SECRET_KEY
DPP: DPP_AWS_SESSION_TOKEN // Only required if assuming role
DDP: DDP_AWS_BUCKET
DDP: DDP_FILE_NAME
DDP: DDP_HTTP_METHOD
DDP: DDP_CONTENT_TYPE //Only required for PUT requests
outputs:
DDP: Authorization
DDP: Date
------------------------------------------------------*/
import com.boomi.execution.ExecutionUtil;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
// Set global variables
String accessKey = ExecutionUtil.getDynamicProcessProperty("DPP_AWS_ACCESS_KEY");
String secretKey = ExecutionUtil.getDynamicProcessProperty("DPP_AWS_SECRET_KEY");
String securityToken = ExecutionUtil.getDynamicProcessProperty("DPP_AWS_SESSION_TOKEN");
for (int i = 0; i < dataContext.getDataCount(); i++) {
InputStream is = dataContext.getStream(i);
Properties props = dataContext.getProperties(i);
String bucketName = props.getProperty("document.dynamic.userdefined.DDP_AWS_BUCKET");
String keyName = props.getProperty("document.dynamic.userdefined.DDP_FILE_NAME");
String method = props.getProperty("document.dynamic.userdefined.DDP_HTTP_METHOD");
String contentType = props.getProperty("document.dynamic.userdefined.DDP_CONTENT_TYPE");
contentType = contentType ?: "";
// Get Current Date
DateFormat longDateFormat = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss Z");
Date date = new Date();
String longDate = longDateFormat.format(date);
// Set String to sign
String stringToSign = method + "\n" +
"\n" +
contentType + "\n" + // contentType will be blank unless it's a PUT request
longDate + "\n" +
"x-amz-security-token:" + securityToken + "\n" +
"/" + bucketName + "/" + keyName;
String signature = Base64.getEncoder().encodeToString(getHMAC1(secretKey.getBytes(), stringToSign));
String authorization = "AWS " + accessKey + ":" + signature;
// Create Authorized URL
props.setProperty("document.dynamic.userdefined.Authorization", authorization);
props.setProperty("document.dynamic.userdefined.Date", longDate);
dataContext.storeStream(is, props);
}
static byte[] getHMAC1(byte[] key, String input) {
byte[] hash;
Mac sha1HMAC = Mac.getInstance("HmacSHA1");
SecretKeySpec secretKey = new SecretKeySpec(key, "HmacSHA1");
sha1HMAC.init(secretKey);
hash = sha1HMAC.doFinal(input.getBytes(StandardCharsets.UTF_8));
return hash;
}
Put Object HTTP Configuration
The HTTP connection will be configured to send data to S3. The HTTP connection will be different than the one used to Assume Roles.
Figure 8. S3 HTTP Connection Configuration.
Table 5. S3 HTTP Connection Configuration.
Name | Value |
---|---|
URL | https://s3.s3-region.amazonaws.com |
Authentication Type | None (Being set using the Groovy Authentication header script) |
Figure 9. S3 HTTP Operation Configuration.
Table 6. S3 HTTP Operation Configuration.
Name | Value |
---|---|
Content Type | <content-type> (can be set dynamically) |
HTTP Method | PUT |
Header: x-amz-security-token | Check Is replacement variable? |
Header: Authorization | Check Is replacement variable? |
Header: Date | Check Is replacement variable? |
Resource Path: DDP_AWS_BUCKET | Check Is replacement variable? |
Resource Path: / | / |
Resource Path: DDP_FILE_NAME | Check Is replacement variable? |
References
- AWS S3 AssumeRoles Parameters and Examples
- AWS S3 Authenticating Request AWS Signature Version 4
- AWS S3 REST API Documentation
- AWS S3 REST API DeleteObject
- AWS S3 REST API GetObject
- AWS S3 REST API PutObject
The article was originally posted at Boomi Community.