🔒 AWS Secure Configuration Guidance

FedRAMP Rev5 Recommended Secure Configuration (RSC) Requirements

Guidance Home Downloads

FRR-RSC-03: Privileged Accounts

Applies to: Low, Moderate, High
Last Updated: 2025-11-25
Version: 1.0.0

Overview

Guidance for securing IAM users and roles with administrative or privileged access.

Privileged Account Management

Identify Privileged Users

Description:

Find IAM users with administrative access

How It Works:

Query IAM for users with AdministratorAccess or other privileged policies attached.

Configuration Items:

  • admin_policies: ['AdministratorAccess - Full AWS access', 'PowerUserAccess - All except IAM/Organizations', 'SecurityAudit - Read-only security access', 'Custom admin policies']
  • validation_checks: ['List users with admin policies attached', 'Check MFA status for each privileged user', 'Verify access key age < 90 days', 'Review last activity date']

Example Cli:

# List users with AdministratorAccess policy
aws iam list-entities-for-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess

# Check user MFA status
aws iam get-credential-report --query 'Content' --output text | base64 -d

Example Boto3:

import boto3
import csv, base64, time

iam = boto3.client('iam')

# Get users with admin policy
admin_policy = 'arn:aws:iam::aws:policy/AdministratorAccess'
entities = iam.list_entities_for_policy(PolicyArn=admin_policy)

print("Users with AdministratorAccess:")
for user in entities['PolicyUsers']:
    print(f"  - {user['UserName']}")

# Check MFA status
iam.generate_credential_report()
time.sleep(5)
report = base64.b64decode(iam.get_credential_report()['Content']).decode()

print("\nPrivileged Users MFA Status:")
for row in csv.DictReader(report.splitlines()):
    if row['user'] in [u['UserName'] for u in entities['PolicyUsers']]:
        mfa = 'true' in row['mfa_active']
        print(f"  {row['user']}: {'✓ MFA' if mfa else '✗ NO MFA'}")

Access Key Rotation

Description:

Ensure access keys are rotated every 90 days

How It Works:

Check creation date of all access keys. Keys older than 90 days should be rotated.

Configuration Items:

  • rotation_process: ['Create new access key', 'Update applications with new key', 'Test new key functionality', 'Deactivate old key', 'Monitor for errors', 'Delete old key after validation']

Example Cli:

# List all access keys with age
aws iam list-users --query 'Users[].UserName' --output text | \
while read user; do
  aws iam list-access-keys --user-name $user
done

Example Boto3:

import boto3
from datetime import datetime, timedelta

iam = boto3.client('iam')
threshold = datetime.now() - timedelta(days=90)

users = iam.list_users()
print("Access Keys Older Than 90 Days:")

for user in users['Users']:
    keys = iam.list_access_keys(UserName=user['UserName'])
    for key in keys['AccessKeyMetadata']:
        if key['CreateDate'].replace(tzinfo=None) < threshold:
            age = (datetime.now() - key['CreateDate'].replace(tzinfo=None)).days
            print(f"  {user['UserName']}: {key['AccessKeyId']} ({age} days old)")

Audit Privileged Roles

Description:

Review IAM roles with administrative permissions

How It Works:

Check roles for admin policies and review trust relationships to ensure only authorized principals can assume them.

Configuration Items:

  • role_checks: ['List roles with admin policies', 'Review trust policy (who can assume)', 'Check for cross-account access', 'Verify session duration limits', 'Review role tags for ownership']

Example Cli:

# List all roles
aws iam list-roles

# Get role details
aws iam get-role --role-name MyAdminRole

# List attached policies
aws iam list-attached-role-policies --role-name MyAdminRole

Example Boto3:

import boto3
import json

iam = boto3.client('iam')

roles = iam.list_roles()
print("Roles with Administrative Permissions:")

for role in roles['Roles']:
    # Check attached policies
    attached = iam.list_attached_role_policies(RoleName=role['RoleName'])
    
    for policy in attached['AttachedPolicies']:
        if 'Admin' in policy['PolicyName']:
            print(f"\n{role['RoleName']}:")
            print(f"  Policy: {policy['PolicyName']}")
            
            # Get trust policy
            trust = role['AssumeRolePolicyDocument']
            principals = []
            for statement in trust['Statement']:
                if 'Principal' in statement:
                    principals.append(statement['Principal'])
            print(f"  Can be assumed by: {principals}")

Automated Compliance

Automated Privileged Account Compliance Scan

Description:

Complete scan of all privileged accounts

How It Works:

Checks all privileged users and roles for MFA, key age, and policy compliance.

Example Boto3:

#!/usr/bin/env python3
import boto3, csv, base64, time
from datetime import datetime, timedelta

def scan_privileged_accounts():
    iam = boto3.client('iam')
    findings = []
    
    # Generate credential report
    iam.generate_credential_report()
    time.sleep(5)
    report = base64.b64decode(iam.get_credential_report()['Content']).decode()
    
    # Check each user
    for row in csv.DictReader(report.splitlines()):
        user = row['user']
        if user == '':
            continue
        
        # Check for admin access
        try:
            policies = iam.list_attached_user_policies(UserName=user)
            is_admin = any('Admin' in p['PolicyName'] for p in policies['AttachedPolicies'])
            
            if is_admin:
                # Check MFA
                if row['mfa_active'] != 'true':
                    findings.append(f"CRITICAL: {user} has admin access without MFA")
                
                # Check key age
                keys = iam.list_access_keys(UserName=user)
                for key in keys['AccessKeyMetadata']:
                    age = (datetime.now() - key['CreateDate'].replace(tzinfo=None)).days
                    if age > 90:
                        findings.append(f"WARNING: {user} has access key {age} days old")
        except:
            pass
    
    return findings

if __name__ == '__main__':
    findings = scan_privileged_accounts()
    print(f"Found {len(findings)} issues:")
    for f in findings:
        print(f"  - {f}")

References