MFA AWS VIA TERMINAL
Published: 2024-05-20 10:48:12

 In today's digital landscape, securing access to cloud services is paramount, particularly as cyber threats become increasingly sophisticated. Amazon Web Services (AWS), a leading cloud service provider, offers robust security features to protect sensitive data and infrastructure. One essential security measure is Multi-Factor Authentication (MFA), which enhances the traditional username and password login process by requiring an additional verification step. This essay will explore the process of configuring and using MFA to access AWS from a local terminal, highlighting the steps involved, the benefits of this security approach, and practical tips for seamless integration. By implementing MFA, users can significantly bolster their security posture, ensuring that access to their AWS environments is both secure and efficient.
 In this article I want draw your attention to some point which we can make it happen with ease. First of all we need to do some configuration like installing necessary packages such as `os, subprocess, pyotp, json, base64, binascli, configparser, dotenv`. These packages are prerequisites for making the scripts work . 
As a next step we need to define the environment variables which we'll us it in our `bash` scripts. To do seo we create a .env file to keep all of our environmental variables to call them in the cli commands.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/isjpvcuzixpikj3vh2th.png)
When configuring Multi-Factor Authentication (MFA) to securely access Amazon Web Services (AWS) from a local terminal, one of the crucial steps involves creating a .env file. This file, short for "environment variables," is essential for securely storing and managing sensitive information, such as AWS access keys, secret keys, and MFA device serial numbers. By using a .env file, users can avoid hard-coding these credentials directly into their scripts or applications, which enhances security and simplifies configuration management. The .env file should be placed in the root directory of the project and structured in a way that each variable is declared on a new line.
 When it comes to creating a script for all of these configuration, naturally we need to acquire a snappy script which includes as follows,
1- Imports and Environment Setup:
 * Imports the necessary modules.
 * Loads environment variables from the .env file.

2- Configuration Reading:
 * Reads AWS credentials from the specified source profile in the AWS credentials file.

3-Validation:
 * Verifies that the TOTP secret is valid and base32 encoded.

4- MFA Token Generation:
 * Generates a current MFA token using the TOTP secret.

5- Session Token Retrieval:
 * Uses AWS STS to obtain a session token with the MFA token.

6- Credentials Update:
* Updates the AWS credentials file with the temporary session credentials.

   Additionally, ensure the ~/.aws/profile under the $HOME directory is correctly configured for your AWS CLI.

Here is the one example for script that we may use;
`aws-login.py`

```
import os
import subprocess
import pyotp
import json
import base64
import binascii
import configparser
from dotenv import load_dotenv

# Load environment variables from .env file
load_dotenv()

# Retrieve variables from the environment
mfa_serial_number = os.getenv('MFA_SERIAL_NUMBER')
totp_secret = os.getenv('TOTP_SECRET')
source_profile = os.getenv('SOURCE_PROFILE', 'dev-mfa')
aws_profile = os.getenv('AWS_PROFILE', 'default')

# Read credentials from the source profile
config = configparser.ConfigParser()
credentials_file_path = os.path.expanduser('~/.aws/credentials')
config.read(credentials_file_path)

if source_profile not in config:
    print(f"Profile '{source_profile}' not found in {credentials_file_path}.")
    exit(1)

aws_access_key_id = config[source_profile].get('aws_access_key_id')
aws_secret_access_key = config[source_profile].get('aws_secret_access_key')

if not aws_access_key_id or not aws_secret_access_key:
    print(f"Missing credentials in profile '{source_profile}'.")
    exit(1)

# Ensure TOTP secret is base32 encoded
try:
    base64.b32decode(totp_secret, casefold=True)
except binascii.Error:
    print("The provided TOTP secret is not valid base32 encoded. Please check the secret.")
    exit(1)

# Generate MFA token
totp = pyotp.TOTP(totp_secret)
mfa_token = totp.now()

# Get temporary session token using MFA
try:
    response = subprocess.check_output([
        'aws', 'sts', 'get-session-token',
        '--serial-number', mfa_serial_number,
        '--token-code', mfa_token,
        '--output', 'json',
        '--profile', source_profile
    ])
except subprocess.CalledProcessError as e:
    print(f"Error getting session token: {e}")
    exit(1)

# Parse the response
response_json = json.loads(response)
credentials = response_json['Credentials']

# Update the AWS credentials file with the temporary credentials
if aws_profile not in config.sections():
    config.add_section(aws_profile)

config[aws_profile]['aws_access_key_id'] = credentials['AccessKeyId']
config[aws_profile]['aws_secret_access_key'] = credentials['SecretAccessKey']
config[aws_profile]['aws_session_token'] = credentials['SessionToken']

with open(credentials_file_path, 'w') as configfile:
    config.write(configfile)

print(f"🙌🏻 Welcome to Starlet Technologies AWS Cloud Services. All logins and acitivities are monitoring and recording.")
print("⏰ Your AWS CLi session will expire at:", credentials['Expiration'])

## Verify the identity of the temporary credentials
#try:
#    identity = subprocess.check_output(['aws', 'sts', 'get-caller-identity', '--profile', aws_profile])
#    print("Current Identity:", identity.decode('utf-8'))
#except subprocess.CalledProcessError as e:
#    print(f"Error verifying identity: {e}")
#    exit(1)
#
## Attempt to list S3 buckets
#try:
#    s3_buckets = subprocess.check_output(['aws', 's3', 'ls', '--profile', aws_profile])
#    print("S3 Buckets:", s3_buckets.decode('utf-8'))
#except subprocess.CalledProcessError as e:
#    print(f"Error listing S3 buckets: {e}")
#    exit(1)

```
In order to make above script run, we use 

```
python aws-login.py
```
As a expected output we can see ;

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ho39y4r7hzbiuizhx1r4.png)

 For the troubleshooting purposes we recommend you to check if you have registered profile under `~/.aws/credentials` file.