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.

NameValue
URLhttps://sts.amazonaws.com
Authentication TypeAWS Signature
Amazon AWS Access Key<users-aws-access-key>
Amazon AWS Secret Key<users-aws-secret-key> (Value is encrypted)
Amazon AWS Service TypeCustom
Custom AWS Servicests
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 PathValue
?Version=?Version=
VersionCheck Is replacement variable?
&Action=&Action=
ActionCheck Is replacement variable?
&RoleSessionName=&RoleSessionName=
RoleSessionNameCheck Is replacement variable?
&RoleArn=&RoleArn=
RoleArnCheck Is replacement variable?
&DurationSeconds=&DurationSeconds=
DurationSecondsCheck 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.

NameValue
Version2011-06-15
ActionAssumeRole
RoleSessionNameSet to Unique Value
RoleArnarn:aws:iam::<account-id>:<role-name>
DurationSeconds3600 (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 PropertyJSON Element
DPP_AWS_ACCESS_KEYAccessKeyId
DPP_AWS_SECRET_KEYSecretAccessKey
DPP_AWS_SESSION_TOKENSessionToken

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 NameValue
DDP_AWS_BUCKET<s3-bucket-name>
DDP_FILE_NAME<file-name>
DDP_HTTP_METHODPUT
DDP_CONTENT_TYPEi.e. text/csv
x-amz-security-tokenDPP_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.

NameValue
URLhttps://s3.s3-region.amazonaws.com
Authentication TypeNone (Being set using the Groovy Authentication header script)

Figure 9. S3 HTTP Operation Configuration.

Table 6. S3 HTTP Operation Configuration.

NameValue
Content Type<content-type> (can be set dynamically)
HTTP MethodPUT
Header: x-amz-security-tokenCheck Is replacement variable?
Header: AuthorizationCheck Is replacement variable?
Header: DateCheck Is replacement variable?
Resource Path: DDP_AWS_BUCKETCheck Is replacement variable?
Resource Path: //
Resource Path: DDP_FILE_NAMECheck Is replacement variable?

References

The article was originally posted at Boomi Community.