All Articles

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

aws assume role 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)

branch 1 aws sts

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.

aws sts connector

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.

aws sts operation

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.

aws sts parameters

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

aws sts set properties

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.

s3 put set properties

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.

aws s3 session connection

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)

aws s3 session operation

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

The article was originally posted at Boomi Community.

Published Mar 8, 2023

Developing a better world.© All rights reserved.