Public and private EC2 instances share an IAM role

Separate effective IAM permissions for EC2 workloads with different exposure and responsibilities.

Description

Sharing an IAM role between an internet-exposed EC2 instance and an internal workload can give the exposed instance unnecessary access to internal data. Compromise can then affect resources within that role’s permissions.

A public subnet alone does not establish reachability. Check actual addressing, routes and security groups, and separate roles according to workload permissions rather than network names alone.

Potential impact

  • Compromise of an exposed instance can affect data accessible through the shared role.
  • Sharing across distinct workloads makes permission changes and attribution harder to assess.

Remediation

  • Use separate roles and instance profiles where workload permission requirements differ. Different profile names with the same role or permissions are not sufficient.
  • Restrict AWS operations and resources, and test required access and rejection of unnecessary access after profile changes.

Examples

These excerpts require the AMI, VPC module, roles and separate private-workload profile to be supplied. The after-example reference aws_iam_instance_profile.test_profile3 must attach a separate role for the internal workload.

Before

hcl
resource "aws_iam_instance_profile" "example" {
  name = "test_profile"
  role = aws_iam_role.test_role.name
}

resource "aws_instance" "public_instance" {
  ami                  = data.aws_ami.ubuntu.id
  instance_type        = "t2.micro"
  subnet_id            = module.vpc.public_subnets[0]
  iam_instance_profile = aws_iam_instance_profile.example.name
}

resource "aws_instance" "private_instance" {
  ami                  = data.aws_ami.ubuntu.id
  instance_type        = "t2.micro"
  subnet_id            = module.vpc.private_subnets[0]
  iam_instance_profile = aws_iam_instance_profile.example.name
}

After

hcl
resource "aws_iam_instance_profile" "public_profile" {
  name = "test_profile"
  role = aws_iam_role.test_role2.name
}

resource "aws_instance" "public_instance" {
  ami                  = data.aws_ami.ubuntu.id
  instance_type        = "t2.micro"
  subnet_id            = module.vpc.public_subnets[0]
  iam_instance_profile = aws_iam_instance_profile.public_profile.name
}

resource "aws_instance" "private_instance" {
  ami                  = data.aws_ami.ubuntu.id
  instance_type        = "t2.micro"
  subnet_id            = module.vpc.private_subnets[0]
  iam_instance_profile = aws_iam_instance_profile.test_profile3.name
}

Explanation:

  • Before: Both instances share one profile and role.
  • After: Separates the profiles. Verify that each actually uses a distinct, least-privilege role.

References