diff --git a/docs/aws-session.md b/docs/aws-session.md new file mode 100644 index 0000000..0095fed --- /dev/null +++ b/docs/aws-session.md @@ -0,0 +1,167 @@ +# AWS Session + +A command-line tool for obtaining temporary AWS credentials with optional MFA support. This script simplifies the process of getting session tokens for both regular IAM users and assumed roles. + +## Installation + +The script is available as a command-line tool when the package is installed: + +```bash +npm install @pixelfusion/aws-scripts +``` + +## Usage + +### Basic Usage + +```bash +# Using npx (recommended) +npx aws-session [profile] [mfa-serial-number] + +# If installed globally +aws-session [profile] [mfa-serial-number] +``` + +### Examples + +```bash +# Use default profile (AWS_PROFILE environment variable) +npx aws-session + +# Use specific profile +npx aws-session my-profile + +# Use specific profile with MFA serial ARN +npx aws-session my-profile arn:aws:iam::123456789012:mfa/my-user + +# For assumed role profiles, the script automatically detects the source profile +npx aws-session my-assumed-role-profile +``` + +**Note:** The MFA_SERIAL argument should be the **serial ARN** (like `arn:aws:iam::123456789012:mfa/my-user`), not the token value from your MFA generator (like `123456`). + +## Features + +### Automatic MFA Detection + +The script can automatically detect MFA serial numbers from your AWS profile configuration: + +```bash +# In your ~/.aws/config +[profile my-profile] +mfa_serial = arn:aws:iam::123456789012:mfa/my-user +``` + +**MFA Serial vs MFA Token:** +- **MFA Serial**: The ARN of your MFA device (e.g., `arn:aws:iam::123456789012:mfa/my-user`) +- **MFA Token**: The 6-digit code from your MFA generator (e.g., `123456`) + +The script uses the MFA serial to identify which device to authenticate with, then prompts you for the current MFA token. + +### Assumed Role Support + +For profiles that use assumed roles, the script will: + +1. First get a session token from the source profile +2. Then assume the role using those temporary credentials +3. Export the final assumed role credentials + +```bash +# In your ~/.aws/config +[profile my-assumed-role] +source_profile = my-source-profile +role_arn = arn:aws:iam::123456789012:role/MyRole +``` + +### Environment Variables + +The script exports the following environment variables: + +- `AWS_ACCESS_KEY_ID` - Temporary access key +- `AWS_SECRET_ACCESS_KEY` - Temporary secret key +- `AWS_SESSION_TOKEN` - Session token + +## Configuration + +### AWS Profiles + +The script works with standard AWS profiles configured in `~/.aws/config` or `~/.aws/credentials`. + +#### Regular IAM User Profile + +```ini +[profile my-user] +aws_access_key_id = AKIAIOSFODNN7EXAMPLE +aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY +mfa_serial = arn:aws:iam::123456789012:mfa/my-user +``` + +#### Assumed Role Profile + +```ini +[profile my-role] +source_profile = my-user +role_arn = arn:aws:iam::123456789012:role/MyRole +mfa_serial = arn:aws:iam::123456789012:mfa/my-user +``` + +### Environment Variables + +- `AWS_PROFILE` - Default profile to use if none specified +- `AWS_ACCESS_KEY_ID` - Access key (for direct credentials) +- `AWS_SECRET_ACCESS_KEY` - Secret key (for direct credentials) + +## Error Handling + +The script handles various error conditions: + +- **No profile specified**: Prompts to set AWS_PROFILE or pass as argument +- **Temporary credentials detected**: Warns that GetSessionToken requires long-term credentials +- **MFA token required**: Prompts for MFA token input +- **Invalid credentials**: Shows clear error messages for authentication failures +- **Role assumption failure**: Provides specific error messages for role issues + +## Security + +- Session tokens expire after 1 hour (3600 seconds) +- MFA tokens are prompted securely via stdin +- No credentials are logged or stored +- Works with AWS SSO and other credential providers + +## Troubleshooting + +### Common Issues + +1. **"You are using temporary credentials"** + - Use a profile that points directly to an IAM user, not an assumed role + - The source profile must have long-term credentials + +2. **"No MFA serial detected"** + - Add `mfa_serial` to your AWS profile configuration + - Or pass the MFA serial as the second argument + +3. **"Failed to get session token"** + - Check your AWS credentials are valid + - Ensure you have the `sts:GetSessionToken` permission + - Verify MFA token is correct (if using MFA) + +### Debug Mode + +To see more detailed output, you can modify the script to add debug logging or run with bash debug mode: + +```bash +bash -x aws-session.sh [profile] [mfa-serial] +``` + +## Version + +Current version: 1.0.1 + +## Authors + +- Damian Mooyman +- Andrew Watkins + +## License + +ISC License \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index bf605f6..4ca70e9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -14,6 +14,7 @@ npm install github:pixelfusion/aws-scripts#master - [Configuration](./configuration.md) - [Bootstrapping CDK](./bootstrap.md) +- [AWS Session Tool](./aws-session.md) ## List of components diff --git a/package.json b/package.json index e5d4820..149ad62 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,9 @@ ], "main": "dist/index.js", "typings": "dist/index.d.ts", + "bin": { + "aws-session": "scripts/aws-session.sh" + }, "author": "Pixelfusion", "license": "ISC", "private": true, diff --git a/scripts/aws-session.sh b/scripts/aws-session.sh new file mode 100755 index 0000000..a59ef3e --- /dev/null +++ b/scripts/aws-session.sh @@ -0,0 +1,138 @@ +#!/bin/bash +# Version: 1.0.1 +# Author: Damian Mooyman, Andrew Watkins +# Date: 2025-07-04 +# Description: Get temporary AWS credentials with (optional)MFA +# Usage: source ./bin/get-session-token.sh [profile] [mfa-serial-number] + +PROFILE=${1:-$AWS_PROFILE} +MFA_SERIAL=${2:-""} + +if [ -z "$PROFILE" ]; then + echo "Error: AWS profile not specified. Set AWS_PROFILE or pass as first argument." + exit 1 +fi + +# Check if this profile uses an assumed role +SOURCE_PROFILE=$(aws configure get source_profile --profile="$PROFILE" 2>/dev/null) +ROLE_ARN=$(aws configure get role_arn --profile="$PROFILE" 2>/dev/null) +PROFILE_MFA_SERIAL=$(aws configure get mfa_serial --profile="$PROFILE" 2>/dev/null) + +if [ -n "$SOURCE_PROFILE" ] && [ -n "$ROLE_ARN" ]; then + echo "Detected assumed role profile. Will get session token from source profile '$SOURCE_PROFILE' first." + + # Use MFA serial from profile config if available + if [ -z "$MFA_SERIAL" ] && [ -n "$PROFILE_MFA_SERIAL" ]; then + MFA_SERIAL="$PROFILE_MFA_SERIAL" + echo "Using MFA serial from profile: $MFA_SERIAL" + fi + + USE_PROFILE="$SOURCE_PROFILE" +else + # Regular profile, try to auto-detect MFA + if [ -z "$MFA_SERIAL" ]; then + echo "Attempting to auto-detect MFA serial number..." + CALLER_ARN=$(aws sts get-caller-identity --profile="$PROFILE" --query 'Arn' --output text 2>/dev/null) + + # Check if this is an assumed role (temporary credentials) + if [[ "$CALLER_ARN" == *":assumed-role/"* ]]; then + echo "Error: You are using temporary credentials from an assumed role." + echo "GetSessionToken requires long-term IAM user credentials." + echo "Please use a profile that points directly to an IAM user, not an assumed role." + echo "Current ARN: $CALLER_ARN" + exit 1 + fi + + # Convert user ARN to MFA ARN + MFA_SERIAL=$(echo "$CALLER_ARN" | sed 's/:user\//:mfa\//') + fi + + USE_PROFILE="$PROFILE" +fi + +if [ -z "$MFA_SERIAL" ] || [[ "$MFA_SERIAL" == *"None"* ]]; then + echo "No MFA serial detected. Proceeding without MFA." + MFA_REQUIRED=false +else + MFA_REQUIRED=true +fi + +if [ "$MFA_REQUIRED" = true ]; then + # Prompt for MFA token + read -p "Enter MFA token for $MFA_SERIAL: " MFA_TOKEN + if [ -z "$MFA_TOKEN" ]; then + echo "Error: MFA token cannot be empty." + exit 1 + fi + echo "Getting session token with MFA..." + # Get temporary credentials with MFA + CREDENTIALS=$(aws sts get-session-token \ + --profile="$USE_PROFILE" \ + --serial-number="$MFA_SERIAL" \ + --token-code="$MFA_TOKEN" \ + --duration-seconds=3600 \ + --output text \ + --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]') +else + echo "Getting session token without MFA..." + # Get temporary credentials without MFA + CREDENTIALS=$(aws sts get-session-token \ + --profile="$USE_PROFILE" \ + --duration-seconds=3600 \ + --output text \ + --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]') +fi + +if [ $? -ne 0 ]; then + echo "Error: Failed to get session token. Check your credentials and permissions." + exit 1 +fi + +# Parse credentials +TEMP_ACCESS_KEY_ID=$(echo $CREDENTIALS | awk '{print $1}') +TEMP_SECRET_ACCESS_KEY=$(echo $CREDENTIALS | awk '{print $2}') +TEMP_SESSION_TOKEN=$(echo $CREDENTIALS | awk '{print $3}') + +# If this is an assumed role profile, assume the role using the session token +if [ -n "$SOURCE_PROFILE" ] && [ -n "$ROLE_ARN" ]; then + echo "Now assuming role: $ROLE_ARN" + + # Generate a session name + SESSION_NAME="get-session-token-$(date +%s)" + + # Assume the role using the temporary credentials + ASSUME_ROLE_CREDENTIALS=$(AWS_ACCESS_KEY_ID="$TEMP_ACCESS_KEY_ID" \ + AWS_SECRET_ACCESS_KEY="$TEMP_SECRET_ACCESS_KEY" \ + AWS_SESSION_TOKEN="$TEMP_SESSION_TOKEN" \ + aws sts assume-role \ + --role-arn="$ROLE_ARN" \ + --role-session-name="$SESSION_NAME" \ + --output text \ + --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]') + + if [ $? -ne 0 ]; then + echo "Error: Failed to assume role. Check your role ARN and permissions." + exit 1 + fi + + # Parse and export assumed role credentials + AWS_ACCESS_KEY_ID=$(echo $ASSUME_ROLE_CREDENTIALS | awk '{print $1}') + AWS_SECRET_ACCESS_KEY=$(echo $ASSUME_ROLE_CREDENTIALS | awk '{print $2}') + AWS_SESSION_TOKEN=$(echo $ASSUME_ROLE_CREDENTIALS | awk '{print $3}') + + echo "Successfully assumed role!" +else + # Use the session token credentials directly + AWS_ACCESS_KEY_ID="$TEMP_ACCESS_KEY_ID" + AWS_SECRET_ACCESS_KEY="$TEMP_SECRET_ACCESS_KEY" + AWS_SESSION_TOKEN="$TEMP_SESSION_TOKEN" +fi + +export AWS_ACCESS_KEY_ID +export AWS_SECRET_ACCESS_KEY +export AWS_SESSION_TOKEN + +echo "✅ Temporary credentials set successfully!" +echo "AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:0:10}..." +echo "Session expires in 1 hour." +echo "" \ No newline at end of file diff --git a/src/rds.ts b/src/rds.ts index ccd7753..19526b3 100644 --- a/src/rds.ts +++ b/src/rds.ts @@ -150,7 +150,7 @@ export class PostgresInstanceWithBastion extends PostgresInstance { const keyPair = ec2.KeyPair.fromKeyPairName( this, stack.getResourceID('BastionKeyPairRef'), - key.keyName + key.keyName, ) // Create the bastion host