2
votes

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.

2
Why not use an EC2 IAM role when the app is running inside AWS?Mark B
I will but I need to develop it locally firstJosh Russo
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
Do you have MFA on your main account or target account?Azize

2 Answers

0
votes

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.

0
votes

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