Skip to content

Restricting IAM User Access by Source IP: SCP Implementation Guide

7 minute read
Content level: Intermediate
0

Prevent credential misuse by restricting IAM User access to approved enterprise networks using Service Control Policies.

1. Overview

AWS best practice recommends using IAM Roles for application authentication. However, legacy applications or applications running outside AWS (on-premises, third-party systems) may not support IAM Roles and require IAM User access keys.

Source IP restriction uses IAM policy conditions to limit AWS API access to requests originating from specific IP address ranges. This approach is effective when applications access AWS resources from predictable, static network locations such as enterprise data centers or cloud provider networks.

2. Use Cases

  • Application access keys from on-premises systems
  • Automated workloads from fixed data centers
  • Third-party integrations from known IPs

3. Implementation Approach

3.1 Deployment Strategy

Two approaches available based on organizational maturity and governance requirements: Service Control Policy with Tag-Based Exceptions and Identity-Based Policy Assignment

3.2 Approach Comparison

FactorSCP (RECOMMENDED)Identity-Based (FALLBACK)
EnforcementOrganization-wideAccount-level
Default PostureDeny all IAM usersAllow with IP restriction
Best ForProductionTesting

Use Identity-Based if:

  • No AWS Organizations
  • Testing phase
  • Simple operational needs

4. Approach 1: Service Control Policy with Tag-Based Exceptions (RECOMMENDED)

For organizations with mature security practices and centralized governance, SCPs provide organization-wide enforcement that cannot be bypassed at the account level. This is the recommended approach for production environments.

Policy Name: RestrictIAMUserAccessWithExceptions

4.1 Use Case - Default Deny with Tag-Based Exception

  • Identity Center and IAM Roles work normally
  • IAM Users blocked by default
  • Requires AccessException:True tag to enable access

4.2 Implementation

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DenyIAMUsersWithoutAccessException",
            "Effect": "Deny",
            "Action": "*",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "aws:PrincipalType": "User"
                },
                "StringNotEquals": {
                    "aws:PrincipalTag/AccessException": "True"
                }
            }
        },
        {
            "Sid": "DenyIAMUsersOutsideApprovedNetwork",
            "Effect": "Deny",
            "Action": "*",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "aws:PrincipalType": "User",
                    "aws:PrincipalTag/AccessException": "True"
                },
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "ENTERPRISE_CIDR_RANGE_1",
                        "ENTERPRISE_CIDR_RANGE_2"
                    ]
                },
                "Bool": {
                    "aws:ViaAWSService": "false"
                }
            }
        }
    ]
}

4.3 How It Works

First Statement: Blocks all IAM users without AccessException:True tag

  • Identity Center and IAM Roles unaffected
  • Requires explicit tagging by infrastructure team

Second Statement: Restricts tagged IAM users to approved IP ranges

  • Defense-in-depth: tag + IP restriction
  • Leaked credentials unusable outside network

4.4 Limitation - Console Access Also Affected

⚠️ Important: This policy affects BOTH programmatic access (access keys) AND console access (password login) for IAM users. The policy applies uniformly to all IAM user authentication methods.

Implications:

  • IAM users without AccessException:True tag cannot access AWS Console OR use access keys
  • IAM users with AccessException:True tag can only access from approved IP ranges (console AND API)
  • Identity Center users are NOT affected and can access from any location
  • IAM Roles are NOT affected and work normally for AWS services and applications

Best Used When:

  • Human users access via IAM Identity Center
  • IAM Roles used for applications/services
  • IAM users only for legacy service accounts

4.5 Tagging IAM Users

# Tag an IAM user to enable access (requires explicit approval)
aws iam tag-user \
    --user-name application-service-account \
    --tags Key=AccessException,Value=True

# List tags on IAM user
aws iam list-user-tags --user-name application-service-account

# Remove tag to disable access
aws iam untag-user \
    --user-name application-service-account \
    --tag-keys AccessException

4.6 Testing Scenario

Step 0: Get your current IP address

curl -s https://checkip.amazonaws.com

Step 1: Create and assign the SCP to your organization or OU

  • Update ENTERPRISE_CIDR_RANGE_1 with your current IP/CIDR
  • Apply SCP to target organizational unit

Step 2: Create test IAM user without tag and grant minimal S3 permissions

# Create user
aws iam create-user --user-name test-service-account

# Create minimal policy for testing (least privilege)
cat > /tmp/test-s3-policy.json <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:ListAllMyBuckets",
            "Resource": "*"
        }
    ]
}
EOF

# Attach minimal policy
aws iam put-user-policy \
    --user-name test-service-account \
    --policy-name TestS3ListPolicy \
    --policy-document file:///tmp/test-s3-policy.json

# Create access key and save the output
aws iam create-access-key --user-name test-service-account

# Configure AWS CLI profile with the credentials
aws configure --profile test-user
# Enter the Access Key ID and Secret Access Key from above

Step 3: Test access (should be denied by SCP despite having S3 permissions)

# Verify you're using the test user
aws sts get-caller-identity --profile test-user
# Should show: arn:aws:iam::ACCOUNT_ID:user/test-service-account

# Verify user has no tags
aws iam list-user-tags --user-name test-service-account

# Test S3 access
aws s3 ls --profile test-user
# Expected: AccessDenied with "explicit deny in a service control policy"

Step 4: Add AccessException tag

# Add the tag (using default/admin profile)
aws iam tag-user \
    --user-name test-service-account \
    --tags Key=AccessException,Value=True

# Verify tag was added
aws iam list-user-tags --user-name test-service-account

Step 5: Test again (should work)

# Test S3 access with test user profile
aws s3 ls --profile test-user
# Expected: Success - lists S3 buckets

Step 6: Verify tag removal revokes access

# Remove the tag
aws iam untag-user \
    --user-name test-service-account \
    --tag-keys AccessException

# Verify tag is removed
aws iam list-user-tags --user-name test-service-account

# Test access (should be denied again)
aws s3 ls --profile test-user
# Expected: AccessDenied with "explicit deny in a service control policy"

Step 7: Cleanup test resources

# List and delete access keys
aws iam list-access-keys --user-name test-service-account
aws iam delete-access-key \
    --user-name test-service-account \
    --access-key-id <ACCESS_KEY_ID_FROM_ABOVE>

# Delete inline policy
aws iam delete-user-policy \
    --user-name test-service-account \
    --policy-name TestS3ListPolicy

# Delete user
aws iam delete-user --user-name test-service-account

# Remove AWS CLI profile
rm ~/.aws/credentials.backup 2>/dev/null
grep -v "test-user" ~/.aws/credentials > ~/.aws/credentials.tmp
mv ~/.aws/credentials.tmp ~/.aws/credentials

5. Approach 2: Identity-Based Policy Assignment (FALLBACK)

Policy Name: EnterpriseNetworkRestriction

Use when:

  • No AWS Organizations
  • Testing before org-wide deployment
  • Simpler operational requirements

5.1 Create the IP Restriction Policy

{
    "Version": "2012-10-17",
    "Statement": {
        "Sid": "DenyAccessOutsideEnterpriseNetwork",
        "Effect": "Deny",
        "Action": "*",
        "Resource": "*",
        "Condition": {
            "NotIpAddress": {
                "aws:SourceIp": [
                    "192.0.2.0/24",
                    "203.0.113.0/24"
                ]
            },
            "Bool": {
                "aws:ViaAWSService": "false"
            }
        }
    }
}

Key Elements:

  • NotIpAddress: Denies access outside CIDR ranges
  • aws:ViaAWSService: Allows AWS service requests
  • Replace example IPs with actual ranges

5.2 Attach to Target Entities

  • IAM users with programmatic access (service accounts)
  • IAM roles assumed by federated identities
  • CI/CD service accounts

Implementation Methods:

  • Managed Policy (recommended)
  • Inline Policy (per-entity)
  • Group Policy (multiple users)

6. Important Considerations

6.1 AWS Service Behavior

aws:SourceIp condition key:

  • Works: Direct API calls, CLI, SDKs
  • Doesn't work: VPC endpoints, service-to-service calls
  • Preserved: Forward Access Sessions (FAS)

6.2 IPv6 Support

Ensure policies include both IPv4 and IPv6 ranges if your network supports dual-stack:

"Condition": {
    "NotIpAddress": {
        "aws:SourceIp": [
            "192.0.2.0/24",
            "2001:db8:1234::/48"
        ]
    }
}

6.3 Emergency Access

For break-glass scenarios:

  • Identity Center users remain accessible (not affected by this policy)
  • Create dedicated break-glass IAM roles without IP restrictions
  • Store break-glass credentials in AWS Secrets Manager with restricted access
  • Enable CloudTrail monitoring and alerting for break-glass usage
  • Document and test emergency procedures regularly

7. Conclusion

Source IP restriction provides an effective security control for legacy applications and external systems that require IAM User access keys. The SCP approach with tag-based exceptions offers organization-wide enforcement with a default deny posture, ensuring IAM Users are only accessible from approved networks after explicit approval. For modern applications, continue migrating to IAM Roles where possible, and use IAM Identity Center for human access to maintain a strong security posture while supporting necessary legacy authentication patterns.


8. References