Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions docs/aws-session.md
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
138 changes: 138 additions & 0 deletions scripts/aws-session.sh
Original file line number Diff line number Diff line change
@@ -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 ""
2 changes: 1 addition & 1 deletion src/rds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading