Help us improve the AWS re:Post Knowledge Center by sharing your feedback in a brief survey. Your input can influence how we create and update our content to better support your AWS journey.
Restricting IAM User Access by Source IP: SCP Implementation Guide
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
| Factor | SCP (RECOMMENDED) | Identity-Based (FALLBACK) |
|---|---|---|
| Enforcement | Organization-wide | Account-level |
| Default Posture | Deny all IAM users | Allow with IP restriction |
| Best For | Production | Testing |
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:Truetag 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:Truetag cannot access AWS Console OR use access keys - IAM users with
AccessException:Truetag 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_1with 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 rangesaws: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
- Language
- English
Relevant content
- asked 6 months ago
- asked a year ago
