- Knowledge Base
- Amazon Web Services
- Amazon EC2
- EC2 Instance Not In Public Subnet
Ensure that no backend Amazon EC2 instances are provisioned in public subnets in order to protect them from exposure to the Internet. Backend instances are EC2 instances that do not require direct access to the public internet such as database, API, or caching servers. To follow cloud security best practices, all Amazon EC2 instances that are not Internet-facing should run within a private subnet, behind a NAT gateway that allows downloading software updates and implementing security patches or accessing other AWS services like SQS and SNS.
This rule resolution is part of the Conformity Security & Compliance tool for AWS.
By provisioning Amazon EC2 instances within a private subnet (logically isolated section of VPC) you will prevent your instances from receiving inbound traffic initiated by someone on the Internet, therefore have the guarantee that no malicious requests can reach your backend instances.
Note: For this rule, Trend Cloud One™ – Conformity assumes that your Amazon EC2 instances are running within a VPC network that has both public and private subnets.
Audit
To determine if your backend Amazon EC2 instances are running within VPC public subnets, perform the following operations:
Using AWS Console
01 Sign in to the AWS Management Console.
02 Navigate to Amazon EC2 console available at https://console.aws.amazon.com/ec2/.
03 In the left navigation panel, under Instances, choose Instances.
04 Select the Amazon EC2 instance that you want to examine.
05 Choose the Details tab from the console split panel to access the instance configuration information.
06 In the Instance summary section, click on the ID (link) of the associated VPC subnet, listed under Subnet ID.
07 Select the VPC subnet, choose the Route table tab from the console split panel, and check the network routes configured for the associated route table. If the route table contains any entries with the Destination set to 0.0.0.0/0 or the Target set to an Internet Gateway (e.g., igw-01234567abcabcabc), the selected Amazon EC2 instance was launched inside a public subnet. As a result, the backend instance is not running within a logically isolated environment that provides full protection from the Internet.
08 Repeat steps no. 4 – 7 to determine the subnet type for other backend Amazon EC2 instances available within the current AWS region.
09 Change the AWS cloud region from the navigation bar and repeat the Audit process for other regions.
Using AWS CLI
01 Run describe-instances command (OSX/Linux/UNIX) with custom output filters to list the IDs of the Amazon EC2 instances provisioned in the selected AWS cloud region:
aws ec2 describe-instances --region us-east-1 --output table --query 'Reservations[*].Instances[*].InstanceId'
02 The command output should return a table with the requested EC2 instance identifiers (IDs):
------------------------- | DescribeInstances | +-----------------------+ | i-01234abcd1234abcd | | i-0abcdabcdabcdabcd | | i-0abcd1234abcd1234 | +-----------------------+
03 Run describe-instances command (OSX/Linux/UNIX) with the ID of the backend Amazon EC2 instance that you want to examine as the identifier parameter and custom output filters to describe the ID of the VPC subnet associated with the selected EC2 instance:
aws ec2 describe-instances --region us-east-1 --instance-ids i-01234abcd1234abcd --query 'Reservations[*].Instances[*].SubnetId[]'
04 The command output should return the ID of the associated VPC subnet:
[ "subnet-0abcd1234abcd1234" ]
05 Run describe-route-tables command (OSX/Linux/UNIX) with the ID of the Amazon VPC subnet returned at the previous step as the identifier parameter, to describe the network routes of the route table configured for the selected VPC subnet:
aws ec2 describe-route-tables --region us-east-1 --filters "Name=association.subnet-id,Values=subnet-0abcd1234abcd1234" --query 'RouteTables[*].Routes[]'
06 The command output should return the requested route table routes:
[
{
"GatewayId": "local",
"DestinationCidrBlock": "172.31.0.0/16",
"State": "active",
"Origin": "CreateRouteTable"
},
{
"GatewayId": "igw-01234567abcabcabc",
"DestinationCidrBlock": "0.0.0.0/0",
"State": "active",
"Origin": "CreateRoute"
}
]
Check the "GatewayId" and "DestinationCidrBlock" configuration attributes returned by the describe-route-tables command output. If the route table contains any entries with the "GatewayId" attribute value set to "igw-0xxxxxxxxxxxxxxxx" or the "DestinationCidrBlock" value set to "0.0.0.0/0", as shown in the output example above, the selected Amazon EC2 instance was provisioned within a public subnet. As a result, the backend instance is not running inside a logically isolated environment that provides full protection from the Internet.
07 Repeat steps no. 3 – 6 to determine the VPC subnet type for other backend EC2 instances available in the selected AWS region.
08 Change the AWS cloud region by updating the --region command parameter value and repeat the Audit process for other regions.
Remediation / Resolution
To move your backend Amazon EC2 instances from public subnets to private subnets, you must re-create these instances within private VPC subnets. To implement the migration process, perform the following operations:
Using AWS CloudFormation
01 CloudFormation template (JSON):
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"SSHKeyName": {
"Type": "AWS::EC2::KeyPair::KeyName",
"Description": "Instance SSH key"
},
"SecurityGroupId": {
"Type": "AWS::EC2::SecurityGroup::Id",
"Description": "Security group ID"
}
},
"Resources": {
"EC2Instance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"ImageId": "ami-0123456789abcdefa",
"InstanceType": "c5.xlarge",
"KeyName": {
"Ref": "SSHKeyName"
},
"SubnetId": "subnet-0123456789abcdef0",
"SecurityGroupIds": [
{
"Ref": "SecurityGroupId"
}
],
"BlockDeviceMappings": [
{
"DeviceName": "/dev/xvda",
"Ebs": {
"VolumeSize": "30",
"VolumeType": "gp2"
}
}
],
"NetworkInterfaces": [
{
"DeviceIndex": 0,
"SubnetId": "subnet-0123456789abcdef0",
"DeleteOnTermination": true,
"AssociatePublicIpAddress": false
}
]
}
}
}
}
02 CloudFormation template (YAML):
AWSTemplateFormatVersion: '2010-09-09' Parameters: SSHKeyName: Type: AWS::EC2::KeyPair::KeyName Description: Instance SSH key SecurityGroupId: Type: AWS::EC2::SecurityGroup::Id Description: Security group ID Resources: EC2Instance: Type: AWS::EC2::Instance Properties: ImageId: ami-0123456789abcdefa InstanceType: c5.xlarge KeyName: !Ref 'SSHKeyName' SubnetId: subnet-0123456789abcdef0 SecurityGroupIds: - !Ref 'SecurityGroupId' BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeSize: '30' VolumeType: gp2 NetworkInterfaces: - DeviceIndex: 0 SubnetId: subnet-0123456789abcdef0 DeleteOnTermination: true AssociatePublicIpAddress: false
Using Terraform (AWS Provider)
01 Terraform configuration file (.tf):
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
required_version = ">= 0.14.9"
}
provider "aws" {
profile = "default"
region = "us-east-1"
}
resource "aws_instance" "ec2-instance" {
ami = "ami-0123456789abcdefa"
instance_type = "c5.xlarge"
key_name = "ssh-key"
subnet_id = "subnet-0123456789abcdef0"
vpc_security_group_ids = [ "sg-0123456789abcdefa" ]
ebs_block_device {
device_name = "/dev/xvda"
volume_size = 30
volume_type = "gp2"
}
associate_public_ip_address = false
}
Using AWS Console
01 Sign in to the AWS Management Console.
02 Navigate to Amazon EC2 console available at https://console.aws.amazon.com/ec2/.
03 In the left navigation panel, under Instances, choose Instances.
04 Select the Amazon EC2 instance that you want to move to a private VPC subnet.
05 Choose Actions from the top-right menu, select Image and templates, and choose Create image.
06 On the Create image setup page, provide the following information:
- For Image name, type a unique name for your new Amazon Machine Image (AMI).
- (Optional) For Image description - optional, provide a short description that reflects the usage of the selected EC2 instance.
- Select the Reboot instance setting checkbox to ensure data consistency. When this option is selected, Amazon EC2 reboots the instance so that data is at rest when snapshots of the attached volumes are taken.
- (Optional) For Tags - optional, choose Tag image and snapshots together, and use the Add new tag button to create and apply user-defined tags to the new image. Tags can be used to search and filter your cloud resources or track your AWS costs.
- Choose Create image to create your new AMI.
07 In the left navigation panel, under Images, select AMIs, and check the Status column to determine the state of your new AMI. Once the Status is set to Available, the image is ready to be used to relaunch your Amazon EC2 instance with the correct subnet type.
08 In the left navigation panel, under Instances, select Instances, choose Launch instances, and perform the following actions to launch your new EC2 instance:
- For Name and tags, provide a name tag for your instance in the Name box. (Optional) Choose Add additional tags to apply user-defined tags to your new EC2 instance. You can track compute cost and other criteria by tagging your instance.
- For Application and OS Images (Amazon Machine Image), select My AMIs tab, choose Owned by me, and select the name of the AMI created in step no. 6 from the Amazon Machine Image (AMI) dropdown list.
- For Instance type, select the required instance type from the Instance type dropdown list (must match the hardware configuration of the source instance).
- For Key pair (login), you can select the same key pair as the source instance from the Key pair name - required dropdown list or choose Create new key pair to create a new key pair for your instance.
- For Network settings, provide the following network configuration:
- Choose Select existing security group under Firewall (security groups), and select the appropriate security group(s) from the Common security groups dropdown list (must match the security group configuration of the source instance).
- Choose Edit and perform the following actions:
- Select the private VPC subnet that you want to use for your instance from the Subnet dropdown list or choose Create new subnet to create a new private subnet.
- Choose Disable from the Auto-assign public IP dropdown list to launch the new EC2 instance without a public IP address.
- For Configure storage, configure the storage device settings (must match the storage configuration of the source instance).
- For Advanced details, configure the advanced settings supported by your EC2 instance.
- For Summary, review the instance details, and choose Launch instance to deploy your new, compliant Amazon EC2 instance.
- Choose View all instances to view your new EC2 instance. Once the Instance State is set to Running, your new instance is ready to use.
09 (Optional) To stop incurring any charges for your non-compliant (source) instance, you must terminate it. To shut down the instance, perform the following actions:
- In the left navigation panel, under Instances, choose Instances.
- Select the Amazon EC2 instance that you want to terminate.
- Choose Instance state and select Terminate (delete) instance.
- In the Terminate (delete) instance confirmation box, review the instance details, then choose Terminate (delete) to terminate the selected EC2 instance.
10 Repeat steps no. 2 – 9 for each Amazon EC2 instance that you want to re-create, available within the current AWS cloud region.
11 Change the AWS cloud region from the console navigation bar and repeat the Remediation process for other regions.
Using AWS CLI
01 Run describe-instances command (OSX/Linux/UNIX) to list the configuration information for the Amazon EC2 instance that you want to re-create (i.e., source instance):
aws ec2 describe-instances --region us-east-1 --instance-ids i-01234abcd1234abcd --query 'Reservations[*].Instances[]'
02 The command output should return the configuration information necessary for re-creating your Amazon EC2 instance:
[
{
"Architecture": "x86_64",
"BlockDeviceMappings": [
{
"DeviceName": "/dev/xvda",
"Ebs": {
"AttachTime": "2025-07-01T11:00:32+00:00",
"DeleteOnTermination": true,
"Status": "attached",
"VolumeId": "vol-0abcd1234abcd1234"
}
}
],
"EbsOptimized": false,
"EnaSupport": true,
"Hypervisor": "xen",
"NetworkInterfaces": [
{
"Association": {
"IpOwnerId": "amazon",
"PublicDnsName": "ec2-xxx-xxx-xxx-xxx.compute-1.amazonaws.com",
"PublicIp": "xxx.xxx.xxx.xxx"
},
"Attachment": {
"AttachTime": "2025-07-01T11:00:31+00:00",
"AttachmentId": "eni-attach-01234abcd1234abcd",
"DeleteOnTermination": true,
"DeviceIndex": 0,
"Status": "attached",
"NetworkCardIndex": 0
},
"Description": "",
"Groups": [
{
"GroupId": "sg-0abcd1234abcd1234",
"GroupName": "cc-project5-security-group"
}
],
"Ipv6Addresses": [],
"NetworkInterfaceId": "eni-01234abcd1234abcd",
"OwnerId": "123456789012",
"PrivateDnsName": "ip-172-10-20-30.ec2.internal",
"PrivateIpAddress": "172.10.20.30",
"PrivateIpAddresses": [
{
"Association": {
"IpOwnerId": "amazon",
"PublicDnsName": "ec2-xxx-xxx-xxx-xxx.compute-1.amazonaws.com",
"PublicIp": "xxx.xxx.xxx.xxx"
},
"Primary": true,
"PrivateDnsName": "ip-172-10-20-30.ec2.internal",
"PrivateIpAddress": "172.10.20.30"
}
],
"SourceDestCheck": true,
"Status": "in-use",
"SubnetId": "subnet-01234abcd1234abcd",
"VpcId": "vpc-0abcd1234abcd1234",
"InterfaceType": "interface",
"Operator": {
"Managed": false
}
}
],
"RootDeviceName": "/dev/xvda",
"RootDeviceType": "ebs",
"SecurityGroups": [
{
"GroupId": "sg-0abcd1234abcd1234",
"GroupName": "cc-project5-security-group"
}
],
"SourceDestCheck": true,
"Tags": [
{
"Key": "Name",
"Value": "cc-project5-prod-instance"
}
],
"VirtualizationType": "hvm",
"CpuOptions": {
"CoreCount": 1,
"ThreadsPerCore": 1
},
"CapacityReservationSpecification": {
"CapacityReservationPreference": "open"
},
"HibernationOptions": {
"Configured": false
},
"MetadataOptions": {
"State": "applied",
"HttpTokens": "required",
"HttpPutResponseHopLimit": 2,
"HttpEndpoint": "enabled",
"HttpProtocolIpv6": "disabled",
"InstanceMetadataTags": "disabled"
},
"EnclaveOptions": {
"Enabled": false
},
"BootMode": "uefi-preferred",
"PlatformDetails": "Linux/UNIX",
"UsageOperation": "RunInstances",
"UsageOperationUpdateTime": "2025-07-01T11:00:31+00:00",
"PrivateDnsNameOptions": {
"HostnameType": "ip-name",
"EnableResourceNameDnsARecord": true,
"EnableResourceNameDnsAAAARecord": false
},
"MaintenanceOptions": {
"AutoRecovery": "default",
"RebootMigration": "default"
},
"CurrentInstanceBootMode": "legacy-bios",
"NetworkPerformanceOptions": {
"BandwidthWeighting": "default"
},
"Operator": {
"Managed": false
},
"InstanceId": "i-01234abcd1234abcd",
"ImageId": "ami-0abcd1234abcd1234",
"State": {
"Code": 16,
"Name": "running"
},
"PrivateDnsName": "ip-172-10-20-30.ec2.internal",
"PublicDnsName": "ec2-xxx-xxx-xxx-xxx.compute-1.amazonaws.com",
"StateTransitionReason": "",
"KeyName": "cc-project5-ssh-key",
"AmiLaunchIndex": 0,
"ProductCodes": [],
"InstanceType": "t2.micro",
"LaunchTime": "2025-07-01T10:01:31+00:00",
"Placement": {
"GroupName": "",
"Tenancy": "default",
"AvailabilityZone": "us-east-1a"
},
"Monitoring": {
"State": "disabled"
},
"SubnetId": "subnet-01234abcd1234abcd",
"VpcId": "vpc-0abcd1234abcd1234",
"PrivateIpAddress": "172.10.20.30",
"PublicIpAddress": "xxx.xxx.xxx.xxx"
}
]
03 Run create-image command (OSX/Linux/UNIX) to create an Amazon Machine Image (AMI) from the source Amazon EC2 instance described in the previous step. Include the --no-reboot command parameter to ensure data consistency. When this parameter is included, Amazon EC2 reboots the instance so that data is at rest when snapshots of the attached volumes are taken:
aws ec2 create-image --region us-east-1 --instance-id i-01234abcd1234abcd --name "Project5 Prod Instance AMI" --description "Production Stack AMI version 2.0" --no-reboot
04 The command output should return the ID of the new Amazon Machine Image (AMI):
{
"ImageId": "ami-0abcdabcdabcdabcd"
}
05 Perform run-instances command (OSX/Linux/UNIX) to launch a new Amazon EC2 instance from the AMI created in the previous steps. Use the information returned in step no. 2 to configure your new EC2 instance. Configure the --subnet-id command parameter with the ID of your private VPC subnet and include the --no-associate-public-ip-address parameter in the command request to bypass assigning automatically a public IPv4 address to the new EC2 instance:
aws ec2 run-instances
--region us-east-1
--image-id ami-0abcdabcdabcdabcd
--count 1
--instance-type t2.micro
--key-name cc-project5-ssh-key
--security-group-ids sg-0abcd1234abcd1234
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=cc-project5-prod-instance}]'
--0abcd1234abcd1234-id subnet-0abcd1234abcd1234
--no-associate-public-ip-address
--query 'Instances[]'
06 The command output should return the configuration information for the newly created EC2 instance:
[
{
"Architecture": "x86_64",
"BlockDeviceMappings": [],
"EbsOptimized": false,
"EnaSupport": true,
"Hypervisor": "xen",
"NetworkInterfaces": [
{
"Attachment": {
"AttachTime": "2025-07-01T11:50:48+00:00",
"AttachmentId": "eni-attach-01234abcd1234abcd",
"DeleteOnTermination": true,
"DeviceIndex": 0,
"Status": "attaching",
"NetworkCardIndex": 0
},
"Description": "",
"Groups": [
{
"GroupId": "sg-0abcd1234abcd1234",
"GroupName": "cc-project5-security-group"
}
],
"Ipv6Addresses": [],
"NetworkInterfaceId": "eni-01234abcd1234abcd",
"OwnerId": "123456789012",
"PrivateDnsName": "ip-172-20-30-40.ec2.internal",
"PrivateIpAddress": "172.20.30.40",
"PrivateIpAddresses": [
{
"Primary": true,
"PrivateDnsName": "ip-172-20-30-40.ec2.internal",
"PrivateIpAddress": "172.20.30.40"
}
],
"SourceDestCheck": true,
"Status": "in-use",
"SubnetId": "subnet-0abcd1234abcd1234",
"VpcId": "vpc-0abcd1234abcd1234",
"InterfaceType": "interface",
"Operator": {
"Managed": false
}
}
],
"RootDeviceName": "/dev/xvda",
"RootDeviceType": "ebs",
"SecurityGroups": [
{
"GroupId": "sg-0abcd1234abcd1234",
"GroupName": "cc-project5-security-group"
}
],
"SourceDestCheck": true,
"StateReason": {
"Code": "pending",
"Message": "pending"
},
"Tags": [
{
"Key": "Name",
"Value": "cc-project5-prod-instance"
}
],
"VirtualizationType": "hvm",
"CpuOptions": {
"CoreCount": 1,
"ThreadsPerCore": 1
},
"CapacityReservationSpecification": {
"CapacityReservationPreference": "open"
},
"MetadataOptions": {
"State": "pending",
"HttpTokens": "required",
"HttpPutResponseHopLimit": 2,
"HttpEndpoint": "enabled",
"HttpProtocolIpv6": "disabled",
"InstanceMetadataTags": "disabled"
},
"EnclaveOptions": {
"Enabled": false
},
"BootMode": "uefi-preferred",
"PrivateDnsNameOptions": {
"HostnameType": "ip-name",
"EnableResourceNameDnsARecord": false,
"EnableResourceNameDnsAAAARecord": false
},
"MaintenanceOptions": {
"AutoRecovery": "default",
"RebootMigration": "default"
},
"CurrentInstanceBootMode": "legacy-bios",
"Operator": {
"Managed": false
},
"InstanceId": "i-0abcd1234abcd1234",
"ImageId": "ami-0abcdabcdabcdabcd",
"State": {
"Code": 0,
"Name": "pending"
},
"PrivateDnsName": "ip-172-20-30-40.ec2.internal",
"PublicDnsName": "",
"StateTransitionReason": "",
"KeyName": "cc-project5-ssh-key",
"AmiLaunchIndex": 0,
"ProductCodes": [],
"InstanceType": "t2.micro",
"LaunchTime": "2025-07-01T11:50:48+00:00",
"Placement": {
"GroupName": "",
"Tenancy": "default",
"AvailabilityZone": "us-east-1a"
},
"Monitoring": {
"State": "disabled"
},
"SubnetId": "subnet-0abcd1234abcd1234",
"VpcId": "vpc-0abcd1234abcd1234",
"PrivateIpAddress": "172.20.30.40"
}
]
07 (Optional) You can terminate the source (non-compliant) EC2 instance in order to stop incurring charges for it. To shut down the instance, run terminate-instances command (OSX/Linux/UNIX) with the source instance ID as the identifier parameter:
aws ec2 terminate-instances --region us-east-1 --instance-ids i-01234abcd1234abcd
08 The output should return the terminate-instances command request information:
{
"TerminatingInstances": [
{
"InstanceId": "i-01234abcd1234abcd",
"CurrentState": {
"Code": 32,
"Name": "shutting-down"
},
"PreviousState": {
"Code": 16,
"Name": "running"
}
}
]
}
09 Repeat steps no. 1 – 8 for each Amazon EC2 instance that you want to re-create, available in the selected AWS cloud region.
10 Change the AWS cloud region by updating the --region command parameter value and repeat the Remediation process for other regions.
References
- AWS Documentation
- Amazon EC2 FAQs
- Scenario 2: VPC with Public and Private Subnets (NAT)
- Creating an Amazon EBS-Backed Linux AMI
- Launching an Instance
- Instance Lifecycle
- Terminate Your Instance
- AWS Command Line Interface (CLI) Documentation
- describe-instances
- describe-route-tables
- create-image
- run-instances
- terminate-instances