Mitigating Risk in the Cloud with Authorization of Amazon Web Services Apps

How to Protect AWS Application Access With Open Source Secrets Management

In this article, we’re going to talk about the problems surrounding authorization for your AWS account. We’ll define what those problems are and why they matter to you and your organization, and then we’ll talk about a system which can mitigate these risks. Finally, we’ll walk through the process of securing your AWS account using CyberArk’s Conjur. 

An Exponential Problem

Many of us started our foray into the world of Amazon Web Services (AWS) by creating a trial account using the free tier. Perhaps one of our first actions was to create a long-term access key and store it in ~/.aws/credentials. 

[default] 
aws_access_key_id=AKIAIOSWINANDR4EXAMPLE 
aws_secret_access_key=JabbaTheWalrus/WKRP/CNOH/bPxRfiCYEXAMPLEKEY 

This approach works well for a single account, but as the number of users in an account increases, the risk associated with long-term keys grows exponentially. Long-term keys don’t have an expiration date and need to be manually expired or disabled. Unfortunately, in large organizations, processes don’t always get triggered if an employee leaves the organization, shares their keys, or accidentally checks them into a public repository. 

Wishlist for a Secure and Scalable System

Considering the risky proposition of using long-term keys, let’s construct a list of the characteristics of an ideal solution. Instead of long-term tokens, ideally, we’d like a system which can issue temporary credentials in the form of a temporary token which expires within a few hours or less.  

Each token request should be validated, and the permissions extended by the token limited to the scope required by the requester. We want each of these token requests and the actions performed using the token to be auditable. We also need the system to be scalable and secure from threats, both internal and external. 

Architecting the Solution

The core element of this solution is the AWS Secure Token Service (STS). One of the functions of STS is to provide temporary credentials for users which you authenticate. We’ll be using Conjur from CyberArk to authenticate users and provide the AWS access key and token to make our request. 

The STS service should be enabled on your account by default, and you can validate this by logging into your AWS Console, navigating to the IAM Dashboard, and selecting Account Settings. 

Once on the Account Settings page, you can scroll down to see which regions are enabled to use the Security Token Service. 

Next, we’ll look at how we’ll be using Conjur to authenticate users, and then obtain STS credentials programmatically. STS is a versatile service, and this is just one possible way which you can leverage it for temporary credentials.  

Implementing the Solution with Conjur

Conjur uses role-based access control (or RBAC) to create and maintain authorization rules. Conjur can be configured as a stand-alone service, or integrated with existing resource management systems through LDAP. Conjur enables you to create users and assign those users to roles. You then define the relationships between roles and resources. Conjur determines which resources each role can access, and the nature of that access, such as read-only or update. 

One of the resources which you can configure in Conjur is called variable or secret. Policies define variables, and API calls are used to insert and update the values for each variable. For this demonstration, we’ll need an IAM role in our account which has rights to sts:AssumeRole. An example of the policy for this access is shown below. 

{ 
    "Version": "2012-10-17", 
    "Statement": { 
        "Effect": "Allow", 
        "Action": "sts:AssumeRole", 
        "Resource": "arn:aws:iam::662010642094:role/User*" 
    } 
} 

Once you’ve created a user with that policy, you’ll need to copy the AWS Access Key and AWS Secret Access Key. After obtaining an access token from Conjur, we can define the following  secrets in our Conjur instance: 

  • root/aws/sts/accessKey 
  • root/aws/sts/secretAccessKey

We could set them with calls similar to those shown below, with the access token identified by  <<<ACCESS_TOKEN>>>  

If you need an authentication token from Conjur:

Following the detialed instructions in the Authentication Documentation.

 

After obtaining an authentication token, we can define the secrets in our Conjur instance:

Follow the Add a SecretAPI format, replacing variables with the values appropriate to your Conjur configuration. It should be similar to the examples bellow.

root@8f2250eace10:/# curl --request POST 
--url http://cyberark_conjur_1/secrets/demo/variable/root%2Faws%2Fsts%2FaccessKey   --header 'authorization: Token token="<<>>"' --data "replace_with_aws_access_key" 
root@8f2250eace10:/# curl --request POST 
--url http://cyberark_conjur_1/secrets/demo/variable/root%2Faws%2Fsts%2FsecretAccessKey   --header 'authorization: Token token="<<>>"' --data "replace_with_aws_secret_access_key"Fig

Note: If you are receiving a 401 error, try reauthenticating and using the new authentication token. Conjur authentication tokens expire after 8 minutes.

Retrieving An STS Token In Code

I’ll be using the Conjur Java API, which you can download from the GitHub repository. You’ll need to build the library and install it in your local repository. You can then include it as a dependency in your project and access the Conjur API programmatically. APIs also exist for:  

After requesting the user for their Conjur username and password, we can connect to our Conjur instance and retrieve the AWS Credentials for the IAM role with the STS:AssumeRole policy. You’ll need to set environment variables for the Conjur endpoint and the Conjur account name on the host machine. 

export CONJUR_ACCOUNT=<account specified during Conjur setup> 

export CONJUR_APPLIANCE_URL=<Conjur HTTPS endpoint> 

The getSTSCreds function below returns a BasicSessionCredentials object, which contains the necessary credentials to connect to AWS resources for two hours. 

 

public BasicSessionCredentials getSTSCreds(String conjurUserNameString conjurPassword) { 
    Conjur conjur = new Conjur(conjurUserNameconjurPassword); 
    String awsAccessKey =  conjur.variables().retrieveSecret("aws/accessKey"); 
    String awsSecretAccessKey =  conjur.variables().retrieveSecret("aws/secretKey");  

    AWSCredentials awsCredentials = new BasicAWSCredentials(awsAccessKey,  awsSecretAccessKey);  

    AWSSecurityTokenService stsClient = AWSSecurityTokenServiceClientBuilder.standard() 
        .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)) 
        .withRegion(Regions.DEFAULT_REGION.getName()) 
        .build();  

    GetSessionTokenRequest session_token_request = new GetSessionTokenRequest(); 
    session_token_request.setDurationSeconds(7200)//2 Hours  

    GetSessionTokenResult session_token_result = stsClient.getSessionToken(session_token_request);  

    Credentials session_creds = session_token_result.getCredentials(); 

    BasicSessionCredentials sessionCredentials = new BasicSessionCredentials( 
        session_creds.getAccessKeyId(), 
        session_creds.getSecretAccessKey(), 
        session_creds.getSessionToken()); 
       return sessionCredentials; 
} 

Wrapping Up

As security continues to grow in importance, it’s imperative that you safeguard resources, and who from your organization can access them. Using AWS STS in partnership with CyberArk’s Conjur is one important step which you can take to manage credentials and access better. You can implement similar patterns for other cloud providers like Microsoft Azure and Google Cloud.