MCP Server Security · AWS IAM Permissions Boundaries
MCP server AWS permissions boundary security — IAM permissions boundaries for MCP server roles, least-privilege enforcement, and privilege escalation prevention
An AWS IAM permissions boundary is a managed policy attached to an IAM role or user that caps the maximum permissions that identity can ever use — regardless of what permissions its regular attached policies grant. For MCP servers deployed on AWS that call AWS APIs as part of tool execution, a permissions boundary is the critical layer that prevents a compromised server (or an LLM prompt that successfully manipulates tool calls) from escalating to AWS administrator access via iam:CreateRole, iam:AttachRolePolicy, or sts:AssumeRole.
How permissions boundaries differ from identity-based policies
AWS IAM evaluates permissions as the intersection of the identity-based policy (what the role is explicitly granted) and the permissions boundary (the maximum permitted). An action is allowed only if both the identity-based policy allows it AND the permissions boundary allows it. If a role has AdministratorAccess attached but a permissions boundary that only allows s3:GetObject and s3:PutObject, the role can only perform those two S3 actions regardless of the broad attached policy.
| Layer | What it controls | Set by |
|---|---|---|
| Identity-based policy | What the role requests permission to do | Application team / Terraform |
| Permissions boundary | Maximum ceiling — a grant beyond the ceiling is silently denied | Security team / platform team |
| Resource-based policy | What resources allow the role to act on them | Resource owner |
| SCPs (Org-level) | Organization-wide guardrails above all IAM policies | AWS Organizations admin |
Why MCP server IAM roles are high-value targets
MCP tools that interact with AWS — reading S3 buckets, querying DynamoDB, calling Lambda, sending SES emails — run with the permissions of the EC2 instance profile or ECS task role that hosts the MCP server. This role is automatically available via IMDS without any credentials in the code. A prompt-injection attack that convinces the LLM to pass a crafted string to an AWS-calling tool can:
- Call
iam:CreateRolewith a trust policy pointing to an external account if the MCP server role has that permission - Call
iam:AttachRolePolicyto attachAdministratorAccessto the new role - Call
sts:AssumeRoleto receive credentials for the elevated role — now with full AWS account access
IAM privilege escalation chains are the highest-severity AWS finding. If the MCP server's IAM role has any of the ~20 known IAM privilege escalation permissions (iam:CreateRole, iam:AttachRolePolicy, iam:PutRolePolicy, iam:CreatePolicyVersion, lambda:UpdateFunctionCode, etc.), a permissions boundary is the mandatory containment. Without one, a single compromised tool call can result in full AWS account takeover.
Constructing a permissions boundary for an MCP server role
The boundary policy should allow only the AWS API actions the MCP server legitimately needs, plus explicitly deny all IAM write actions and privilege escalation paths.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowMcpServerOperations",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket",
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:Query",
"dynamodb:Scan",
"ses:SendEmail",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
},
{
"Sid": "DenyPrivilegeEscalation",
"Effect": "Deny",
"Action": [
"iam:*",
"sts:AssumeRole",
"organizations:*",
"account:*"
],
"Resource": "*"
},
{
"Sid": "DenyBoundaryModification",
"Effect": "Deny",
"Action": [
"iam:DeleteRolePermissionsBoundary",
"iam:PutRolePermissionsBoundary"
],
"Resource": "*"
}
]
}
Include the DenyBoundaryModification statement. Without it, a role with iam:PutRolePermissionsBoundary can replace its own boundary with a permissive one, then escalate freely. Explicitly denying boundary modification is required to make the boundary self-protecting.
Attaching the boundary in Terraform and AWS CDK
# Terraform: create the boundary policy and attach to the MCP server role
resource "aws_iam_policy" "mcp_server_boundary" {
name = "McpServerPermissionsBoundary"
policy = file("mcp-server-boundary.json")
}
resource "aws_iam_role" "mcp_server" {
name = "mcp-server-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "ec2.amazonaws.com" }
}]
})
# Attach the boundary — caps maximum permissions regardless of attached policies
permissions_boundary = aws_iam_policy.mcp_server_boundary.arn
}
# Attach the actual operational policy (intersection with boundary = effective permissions)
resource "aws_iam_role_policy_attachment" "mcp_server_ops" {
role = aws_iam_role.mcp_server.name
policy_arn = aws_iam_policy.mcp_server_ops.arn # Your specific operational policy
}
// AWS CDK (TypeScript)
import * as iam from 'aws-cdk-lib/aws-iam';
const boundary = new iam.ManagedPolicy(this, 'McpServerBoundary', {
document: iam.PolicyDocument.fromJson(boundaryJson),
});
const mcpRole = new iam.Role(this, 'McpServerRole', {
assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'),
permissionsBoundary: boundary,
});
SCPs vs permissions boundaries — which to use when
Service Control Policies (SCPs) in AWS Organizations apply to every identity in an account or OU — they cannot be set per-role. Permissions boundaries apply to specific roles. For MCP servers:
- Use an SCP to block actions that should never occur in the account at all (e.g., deny all EC2 launch in non-production accounts, deny root user API calls)
- Use a permissions boundary to cap an individual MCP server role to only the AWS API surface it legitimately needs, even when the platform team's broad operational policy grants more
- Both are belt-and-suspenders: an SCP blocks at the account level, a boundary blocks at the role level — privilege escalation requires bypassing both
SkillAudit findings for permissions boundary issues in MCP servers
iam:CreateRole, iam:AttachRolePolicy) without a permissions boundary — privilege escalation to AdministratorAccess possible via a single tool call chainiam:PutRolePermissionsBoundary — boundary can be removed by the role itself, bypassing all containmentsts:AssumeRole without resource constraints — role can assume other roles in the account, potentially escaping the boundary via a higher-privileged roleSkillAudit checks IAM role configurations exported via CloudFormation templates or Terraform plans. Run a free audit to surface privilege escalation paths in your MCP server's AWS configuration.