IAM Security: Understanding IAM Policies and Network Security
IAM (Identity and Access Management) policies define who can access what resources under which conditions. Combined with network security controls, they form a powerful defense-in-depth strategy. This tutorial covers how to write effective IAM policies, enforce network-level restrictions, and avoid common pitfalls that lead to data exposure.
What is an IAM Policy?
An IAM policy is a JSON document that grants or denies permissions to identities (users, groups, roles) or resources. Every policy follows a consistent structure consisting of an Effect, Action, Resource, and optional Condition block.
Here is a minimal policy that allows read-only access to an S3 bucket:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::example-bucket",
"arn:aws:s3:::example-bucket/*"
]
}
]
}
Key elements:
- Version – Policy language version (always "2012-10-17" for current AWS IAM)
- Statement – One or more permission rules
- Effect –
AlloworDeny(explicit Deny always overrides Allow) - Action – The API operations being permitted or denied
- Resource – ARN(s) of the resources the statement applies to
- Condition – (Optional) Circumstances under which the policy grants access
Why IAM Policies and Network Security Matter
Without tight IAM policies, a single compromised credential can expose your entire infrastructure. Network security adds another layer: even with valid credentials, access can be blocked unless the request originates from an approved IP range, VPC endpoint, or secure network path. This dual control prevents many common attacks:
- Credential theft (phished keys used from outside your corporate network)
- Insider threats moving laterally from unmanaged devices
- Accidental exposure of private resources to the public internet
- API calls leaking data over untrusted networks
IAM policies are not just about "who" – they enforce how access happens. Network-aware conditions transform IAM from a static permission model into a dynamic, context-aware authorization system.
How to Write and Attach IAM Policies
Policies can be attached directly to users, groups, or roles (identity-based), or to resources like S3 buckets, Lambda functions, or KMS keys (resource-based). In a typical cloud environment, you'll work with identity-based policies most often.
Step 1: Start with a minimal, explicit policy
Begin by granting only what is absolutely required. Use the IAM policy simulator or a dry-run API call to validate. For example, a policy allowing a developer to read/write only a specific DynamoDB table:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem"
],
"Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/AppUsers"
}
]
}
Step 2: Add network conditions
To restrict access to only requests originating from a specific VPC or IP range, use the Condition block with condition keys like aws:SourceIp, aws:SourceVpc, or aws:SourceVpce (VPC endpoint). This example requires the caller to be coming from your corporate VPN IP range:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:*"],
"Resource": ["arn:aws:s3:::confidential-data/*"],
"Condition": {
"IpAddress": {
"aws:SourceIp": "203.0.113.0/24"
}
}
}
]
}
For VPC-based restrictions, use the StringEquals operator with aws:SourceVpc:
"Condition": {
"StringEquals": {
"aws:SourceVpc": "vpc-0abc123def456"
}
}
Step 3: Combine with other security controls
Network conditions are often paired with MFA requirements and time-based restrictions. Here's a policy that demands both MFA authentication and a specific IP range for sensitive administrative actions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:CreateUser",
"iam:DeleteUser",
"iam:UpdateAccessKey"
],
"Resource": "*",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
},
"IpAddress": {
"aws:SourceIp": "10.0.0.0/16"
}
}
}
]
}
Network Security Integration Patterns
Beyond IP-based conditions, cloud providers offer deeper network-IAM integration:
- VPC endpoints – Attach endpoint policies to limit which IAM principals can use a given endpoint, and add
aws:SourceVpceconditions to IAM policies to restrict access to specific endpoints. - PrivateLink / endpoint services – IAM policies can require that traffic flows over a particular endpoint, effectively binding network path and identity.
- Network Firewall rules – While not IAM, these can block unauthorized IPs at the network layer, reducing the blast radius before IAM even evaluates the request.
- Security groups & NACLs – These act as the first line of defense; IAM policies then enforce identity-based rules for traffic that passes the network filter.
Practical Code Examples
1. Restricting Lambda invocation to a VPC-originated request
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:us-east-1:123456789012:function:my-private-function",
"Condition": {
"StringEquals": {
"aws:SourceVpc": "vpc-0def456abc789"
}
}
}
]
}
2. S3 bucket policy that allows reads only from a specific VPC endpoint
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"StringEquals": {
"aws:SourceVpce": "vpce-0a1b2c3d4e5f6"
}
}
}
]
}
3. Deny all access if the request doesn't come from a trusted corporate network (use as a boundary)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"NotIpAddress": {
"aws:SourceIp": [
"192.0.2.0/24",
"198.51.100.0/24"
]
},
"Bool": {
"aws:ViaAWSService": "false"
}
}
}
]
}
This "Deny if not in allowed IPs" policy can be attached to an IAM role or user as a permissions boundary, ensuring any access (even through role assumption) is blocked when originating from outside the corporate network. The aws:ViaAWSService exclusion prevents breaking AWS service-linked roles that act on your behalf from internal AWS networks.
Best Practices
- Grant least privilege – Start with nothing, add actions and resources incrementally based on actual need.
- Use permissions boundaries – Define a maximum allowed scope for roles and users to prevent privilege escalation.
- Combine network conditions with identity – Always pair IP/VPC conditions with a principal restriction; never rely on network alone.
- Validate with IAM policy simulator – Test policies before deployment to confirm they behave as expected under different IP scenarios.
- Monitor with policy analysis tools – Use IAM Access Analyzer or similar to detect policies that grant overly broad access.
- Apply network controls in layers – Security groups, NACLs, firewall rules, and IAM network conditions should all enforce the same network boundaries for defense-in-depth.
- Avoid wildcard actions on sensitive resources –
"Action": "*"on a production S3 bucket is a red flag; restrict to specific operations. - Use explicit Deny statements for critical restrictions – Deny is absolute; use it for "must never happen" scenarios like access from the open internet.
Testing and Troubleshooting
When a request is denied due to a network condition, the error message often includes the condition key that failed. To debug:
- Use the IAM Policy Simulator and specify the source IP, VPC endpoint, or MFA state manually.
- Check CloudTrail logs for the exact
sourceIPAddressandvpcEndpointIdvalues in denied events. - Temporarily add a less restrictive condition to verify the base permission works, then layer the network condition back.
Conclusion
IAM policies and network security are inseparable parts of a modern access control strategy. A well-crafted policy doesn't just say "this role can read that bucket" – it says "this role can read that bucket, but only when connected from our private subnet and authenticated with multi-factor authentication." By embedding network context into IAM, you drastically reduce the risk of credential misuse and build an authorization model that is both granular and resilient. Start with least privilege, add network conditions as a mandatory gate, and continuously validate with simulation tools to keep your cloud environment secure.