AgentSkillsCN

IAM Least Privilege Patterns

为 Kubernetes 实施最小权限 IAM 策略,通过作用域权限、权限边界与 IRSA 进行精细化权限管理

SKILL.md
--- frontmatter
name: IAM Least Privilege Patterns
description: Implement least privilege IAM policies with scoped permissions, permission boundaries, and IRSA for Kubernetes

IAM Least Privilege Patterns

Overview

The principle of least privilege ensures that IAM entities (users, roles, services) have only the permissions necessary to perform their tasks—nothing more. This reduces the blast radius of security incidents and meets compliance requirements.

mermaid
flowchart TB
    subgraph "Permission Layers"
        PB[Permission Boundary]
        RP[Role Policy]
        RP2[Resource Policy]
    end
    
    subgraph "Effective Permissions"
        EP[Intersection of All Layers]
    end
    
    PB --> EP
    RP --> EP
    RP2 --> EP
    
    style EP fill:#90EE90

Key Concepts

Permission Boundaries

A permission boundary sets the maximum permissions a role can have, regardless of its attached policies. Think of it as a "ceiling" on permissions.

Service-Linked Roles

AWS-managed roles with predefined permissions for specific services. Use these when available instead of creating custom roles.

IRSA (IAM Roles for Service Accounts)

For Kubernetes workloads on EKS, IRSA allows pods to assume IAM roles without embedding credentials. This is the recommended approach for EKS.

Best Practices

  1. Start with zero permissions - Add only what's needed
  2. Use conditions - Restrict by IP, time, tags, or resource attributes
  3. Scope to resources - Avoid Resource: "*" when possible
  4. Use permission boundaries - Prevent privilege escalation
  5. Prefer managed policies - For common use cases
  6. Implement IRSA - For Kubernetes workloads on EKS
  7. Regular access reviews - Use IAM Access Analyzer
  8. Use service control policies (SCPs) - For organization-wide guardrails

Anti-Patterns to Avoid

❌ Using AdministratorAccess for applications
❌ Hardcoding credentials in code
❌ Overly permissive Resource: "*" statements
❌ Sharing IAM roles across unrelated services
❌ Long-lived access keys instead of temporary credentials


Example 1: Terraform - Lambda with Scoped Permissions + Permission Boundary

This example creates a Lambda function with:

  • A scoped IAM role limited to specific S3 bucket and DynamoDB table
  • A permission boundary to prevent privilege escalation
  • CloudWatch Logs permissions for the function's log group only

📁 Location: terraform/examples/iam-least-privilege/

Key Features

hcl
# Permission boundary - the ceiling for this role
resource "aws_iam_policy" "permission_boundary" {
  name = "${local.name_prefix}-boundary"
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect   = "Allow"
        Action   = ["s3:*", "dynamodb:*", "logs:*"]
        Resource = "*"
        Condition = {
          StringEquals = {
            "aws:RequestedRegion" = var.aws_region
          }
        }
      },
      {
        Effect   = "Deny"
        Action   = ["iam:*", "organizations:*", "sts:AssumeRole"]
        Resource = "*"
      }
    ]
  })
}

# Role with scoped policy
resource "aws_iam_role_policy" "lambda_policy" {
  role = aws_iam_role.lambda.id
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect   = "Allow"
        Action   = ["s3:GetObject", "s3:PutObject"]
        Resource = "${aws_s3_bucket.data.arn}/*"  # Scoped to specific bucket
      }
    ]
  })
}

Example 2: CDK - IRSA for EKS Pod with Scoped Permissions

This example creates:

  • An EKS service account with IRSA
  • A scoped IAM role for S3 and Secrets Manager access
  • Permission boundary attached to the role

📁 Location: cdk/examples/iam-least-privilege/

Key Features

typescript
// Create IRSA role with scoped permissions
const serviceAccountRole = new iam.Role(this, 'PodRole', {
  assumedBy: new iam.FederatedPrincipal(
    cluster.openIdConnectProvider.openIdConnectProviderArn,
    {
      StringEquals: {
        [`${cluster.clusterOpenIdConnectIssuer}:sub`]: 
          `system:serviceaccount:${namespace}:${serviceAccountName}`,
        [`${cluster.clusterOpenIdConnectIssuer}:aud`]: 'sts.amazonaws.com'
      }
    },
    'sts:AssumeRoleWithWebIdentity'
  ),
  permissionsBoundary: permissionBoundary,
});

// Add scoped S3 policy
serviceAccountRole.addToPolicy(new iam.PolicyStatement({
  effect: iam.Effect.ALLOW,
  actions: ['s3:GetObject', 's3:PutObject'],
  resources: [`${dataBucket.bucketArn}/*`],
  conditions: {
    StringEquals: {
      's3:x-amz-acl': 'bucket-owner-full-control'
    }
  }
}));

Validation Checklist

  • No Resource: "*" without conditions
  • Permission boundary attached to all custom roles
  • IRSA used for EKS workloads (no static credentials)
  • IAM Access Analyzer enabled
  • No inline policies with * actions
  • All roles have meaningful descriptions
  • Tags applied for ownership tracking

Related Skills