2

I'm developing a simple app that will run on an existing EC2 instance. The AWS account is secured with MFA. We use a main account and assume a role into our target account for personal access.

The app only deals with the MFA when I'm developing locally. I would like to avoid creating a user in the target account just for development, and wrap my local development in --profile like functionality.

My thought was to use aws sts to generate the access key and secrete key, but performing an assume role using the same setup I have in my credentials file gives me an Access Denied error.

My credentials file follows this pattern:

[main-profile]
aws_access_key_id={access-key}
aws_secret_access_key={secret-key}

[target-profile]
mfa_serial=arn:blah
role_arn=arn:blah2
source_profile=main-profile

I tried to use aws sts --role-name arn:blah2 --role-session-name test --profile main-profile. It seems like I would need to reference the MFA device as well but I don't see that as an option.

Is there any way I can do what I'm looking to do?


Ok so I was able to successfully retrieve and cache the credentials, but the access key returned seems to not be valid for setting into the environment variable. Thoughts?

#!/bin/bash
#set -x

###
# Note: This uses jq (sudo apt-get install jq -y)

targetProfile="target-profile"

sessionFile="/tmp/session-$targetProfile.txt"
sessionFile=$(echo $sessionFile)
echo "Looking for $sessionFile"
if [[ -f "$sessionFile" ]]; then
    echo "Found $sessionFile"
    sessionInfo="$(cat $sessionFile)"    

else
    echo "Building $sessionFile"

    roleArn="$(aws configure get role_arn --profile $targetProfile)"
    mfaArn="$(aws configure get mfa_serial --profile $targetProfile)"
    mainProfile="$(aws configure get source_profile --profile $targetProfile)"

    echo MFA Token:
    read mfaToken

    echo "aws sts get-session-token --serial-number $mfaArn --token-code $mfaToken --profile $mainProfile"
    sessionInfo="$(aws sts get-session-token --serial-number $mfaArn --token-code $mfaToken --profile $mainProfile)"

fi
echo "Current session info: $sessionInfo"

expirationDateValue="$(echo $sessionInfo | jq '.Credentials.Expiration' | tr -d '"')"
echo "Expiration value: $expirationDateValue"
expirationDate=$(date -d $expirationDateValue +%s)
echo "Expiration date: $expirationDate"
currentDate=$(date +%s)
echo "Current date: $currentDate"

if [[ $currentDate -ge $expirationDate ]]; then
    rm $sessionFile
    /bin/bash $0
    exit
fi  

echo "$sessionInfo" > $sessionFile

export AWS_ACCESS_KEY_ID=$(echo $sessionInfo | jq '.Credentials.AccessKeyId')
export AWS_SECRET_ACCESS_KEY=$(echo $sessionInfo | jq '.Credentials.SecretAccessKey')
export AWS_SESSION_TOKEN=$(echo $sessionInfo | jq '.Credentials.SessionToken')

#dotnet run
aws s3 ls

When I run this, I get the following error message:

An error occurred (InvalidAccessKeyId) when calling the ListBuckets operation: The AWS Access Key Id you provided does not exist in our records.

It turns out I couldn't re-use the quotes in the values returned from the session JSON. I had to remove the quotes and add new quotes, which actually kind of makes sense. See my answer below for my solution.

John Rotenstein
  • 241,921
  • 22
  • 380
  • 470
Josh Russo
  • 3,080
  • 2
  • 41
  • 62
  • Why not use an EC2 IAM role when the app is running inside AWS? – Mark B Jun 25 '20 at 16:37
  • I will but I need to develop it locally first – Josh Russo Jun 25 '20 at 16:43
  • 1
    Oh I see. Have you looked at this? https://aws.amazon.com/premiumsupport/knowledge-center/authenticate-mfa-cli/ or this? https://stackoverflow.com/questions/34795780/how-to-use-mfa-with-aws-cli – Mark B Jun 25 '20 at 16:48
  • 1
    If I understand what you're trying to do, I would script this. Run `aws sts get-session-token --serial-number arn-of-mfa-device --token-code xyz` that will emit a JSON document with credentials. Parse that with `jq` or other, and write the access key, secret key, and session token into a named profile in your ~/.aws/credentials file. Your script could also initially read the current named profile and check the expiration of the current creds, only getting new creds if needed. – jarmod Jun 25 '20 at 17:05
  • Do you have MFA on your main account or target account? – Azize Jun 26 '20 at 05:23
  • @MarkB and @jarmod I think I have what I need but do either of you have an idea why the `echo $sessionInfo > $sessionFile` might be failing? – Josh Russo Jun 28 '20 at 15:16
  • @Azize the MFA is on the main account – Josh Russo Jun 28 '20 at 15:19

2 Answers2

2

Got it! I finally found the correct Bash spell : o)

#!/bin/bash
#set -x

###
# Note: This uses jq (sudo apt-get install jq -y)

targetProfile="target-profile"

sessionFile="/tmp/session-$targetProfile.txt"
sessionFile=$(echo $sessionFile)
echo "Looking for $sessionFile"
if [[ -f "$sessionFile" ]]; then
    echo "Found $sessionFile"
    sessionInfo="$(cat $sessionFile)"    

else
    echo "Building $sessionFile"

    roleArn="$(aws configure get role_arn --profile $targetProfile)"
    mfaArn="$(aws configure get mfa_serial --profile $targetProfile)"
    mainProfile="$(aws configure get source_profile --profile $targetProfile)"

    echo MFA Token:
    read mfaToken

    sessionInfo="$(aws sts get-session-token --serial-number $mfaArn --token-code $mfaToken --profile $mainProfile)"

fi

expirationDateValue="$(echo $sessionInfo | jq '.Credentials.Expiration' | tr -d '"')"
expirationDate=$(date -d $expirationDateValue +%s)
currentDate=$(date +%s)

if [[ $currentDate -ge $expirationDate ]]; then
    echo "Session expired"
    rm $sessionFile
    /bin/bash $0
    exit
fi  

echo "$sessionInfo" > $sessionFile

export AWS_ACCESS_KEY_ID="$(echo $sessionInfo | jq '.Credentials.AccessKeyId' | tr -d '"')"
export AWS_SECRET_ACCESS_KEY="$(echo $sessionInfo | jq '.Credentials.SecretAccessKey' | tr -d '"')"
export AWS_SESSION_TOKEN="$(echo $sessionInfo | jq '.Credentials.SessionToken' | tr -d '"')"

#dotnet run
aws s3 ls
Josh Russo
  • 3,080
  • 2
  • 41
  • 62
0

AWS CLI can uses two different files to handle config and credentials.

  • The AWS CLI config file (~/.aws/config)
  • The AWS Shared Credential file (~/.aws/credentials)

About shared credentials file, let me quote documentation:

With each section, the three configuration variables shown above can be specified: aws_access_key_id, aws_secret_access_key, aws_session_token. These are the only supported values in the shared credential file.

Reference: https://docs.aws.amazon.com/cli/latest/topic/config-vars.html

So basically you can put your main profile on credentials file, but need to put your target profile on config file.

See an example below:

# In ~/.aws/credentials:
[development]
aws_access_key_id=foo
aws_secret_access_key=bar

# In ~/.aws/config
[profile crossaccount]
role_arn=arn:aws:iam:...
source_profile=development

Also note that the section names are different than the AWS CLI config file (~/.aws/config). In the AWS CLI config file, you create a new profile by creating a section of [profile profile-name]. In the shared credentials file, profiles are not prefixed with profile.

Azize
  • 4,006
  • 2
  • 22
  • 38
  • I do realize this how the cli operates. What I'm trying to do is mimic `--profile` for a custom command line application by way of a Bash script. I updated my question with my current progress. Any thoughts? – Josh Russo Jun 28 '20 at 15:19