All Articles

AWS Assume Role Instance Profile Implementation within Boomi

AWS Assume Role Instance Profile allows a resource with an assigned AWS role 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 an AWS role assigned to an EC2 instance that is running a Boomi Integration Runtime (i.e. Atom). The example integration will execute the 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.

Another article on using AWS Assume Role with User Credentials can be found here

Overview

The reference implementation starts by using a Role that is applied to an EC2 instance. An HTTP API call is made to http://169.254.169.254/latest/api/token to get a token. A second call is made to get session credentials. The second call is to http://169.254.169.254/latest/meta-data/iam/security-credentials/.

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.

There are two steps within the integration: Get Instance Profile Session Token and Send Data to S3 with Session Token.

AWS IAM Role for EC2 Instances

An IAM Role must be applied to the EC2 instance that defines the permissions for the S3 bucket. There are two tabs within the role that we will focus on, which are Permissions and Trust relationships. The Role can be created by creating a new AWS Role and selecting the EC2 use case.

aws role

Figure 2. AWS Role.

Below is the AWS Policy applied to the AWS Role. The name of the AWS S3 bucket being used is ab-ec2-s3-bucket.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::ab-ec2-s3-bucket"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject",
                "s3:PutObjectAcl"
            ],
            "Resource": [
                "arn:aws:s3:::ab-ec2-s3-bucket/*"
            ]
        }
    ]
}

The trust relationships are defined below for the AWS Role.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

Next, the role needs to be applied to the EC2 instance that is running the Boomi Integration Runtime.

ec2 iam role

Figure 3. EC2 IAM Role within Security Settings.

Boomi Integration Configuration

After the EC2 instance has been set up, we will start to configure the Boomi integration.

To obtain the credentials, two HTTP calls must be executed. The first call sends a REST API call to http://169.254.169.254/latest/api/token.

http connetion for instance profile

Figure 4. HTTP Connection for AWS Instance Profile Token.

http operation for instance profile

Figure 5. HTTP HTTP Operation for AWS Instance Profile Token.

*Table 1. HTTP Connection Header from AWS Instance Profile Token.”

Name Value
Header: X-aws-ec2-metadata-token-ttl-seconds 21600
Resource Path: latest/api/token latest/api/token

The first request will return a plain text token that will be used in a subsequent X-aws-ec2-metadata-token header.

The next REST API call will be to http://169.254.169.254/latest/meta-data/iam/security-credentials/\<role-applied-to-EC2>. It will use the same HTTP connection as the first API call and will only need a different HTTP Operation. http://169.254.169.254/latest/meta-data/iam/security-credentials/ab-EC2-S3-Permissions will be used based on the role being used in Figure 2.

aws sts operation

Figure 6. HTTP Operation for AWS Session Token.

Table 2. HTTP Operation headers for AWS Session Token.

Name Value
Header: X-aws-ec2-metadata-token <token-from-initial-rest-call>
Resource Path: latest/meta-data/iam/security-credentials/ab-EC2-S3-Permissions latest/meta-data/iam/security-credentials/

Below is a sample JSON response.

{
  "Code" : "Success",
  "LastUpdated" : "2012-04-26T16:39:16Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIAIOSFODNN7EXAMPLE",
  "SecretAccessKey" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
  "Token" : "token",
  "Expiration" : "2017-05-17T15:09:54Z"
}

Set Property Shape

Finally, a Set Property shape will set three Dynamic Process Properties: DPP_AWS_ACCESS_KEY, DPP_AWS_SECRET_KEY, and DPP_AWS_SESSION_TOKEN from the previous response.

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 7. 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 8. Put Object: Set Properties Shape Configuration.

Table 3. 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 javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;


// 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 9. S3 HTTP Connection Configuration.

Table 4. 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 10. S3 HTTP Operation Configuration.

Table 5. 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 24, 2023

Developing a better world.© All rights reserved.