Aviatrix: Invisible Network Shield in AWS
A Student Community Day 2025 session in Nepal
Network Security
Aviatrix: Invisible Network Shield in AWS
A Student Community Day 2025 session in Nepal

In Figure: Me (on the Left) receiving Token of Appreciation from the AWS Student Community Day 2025 Nepal’s Lead (on the Right)
On Dec. 27th 2025, I covered a technical breakout session among 100s of students who appeared at the “AWS Student Community Day 2025” in Kathmandu, Nepal, as a speaker. I am in this article, sharing the same knowledge as a slide run through. Why am I sharing this, because I utilize AWS most of my time being an AWS certified Solution Architect Associate.

Let’s start with this little information in mind:

For this, let me tell you:
- AWS is the largest public cloud service provider currently in the market.
- VPC in AWS (default or self-created) is something that is always needed for any kind of cloud work
- Aviatrix is a Marketplace solution available across Cloud service provider’s marketplace.
Note: Aviatrix solves some Cloud Service Provider specific problems in larger enterprise setting when usually enterprise have to work in multi-account/multi-cloud or multi-region context.
Creation of a simple VPC Environment

- We select a region we would like to work on. E.G., : US-EAST-1A
- We create a VPC manually. It’s like defining a working desk for us. But from Business POV, it’s just the network fences for our infrastructures with in the larger AWS boundary. It is important so that, Organization A can’t within the AWS backbone network, play evil by accessing Organization B’s resources.
#First, create the VPC and capture its ID for subsequent commands.
VPC_ID=$(aws ec2 create-vpc — cidr-block 10.0.0.0/16 — tag-specifications ‘ResourceType=vpc,Tags=[{Key=Name,Value=MyVPC}]’ — query Vpc.VpcId — output text)
echo “VPC ID: $VPC_ID”
Remember the CIDR range, it is what we split into subnets or leave some range for further expansion of resources that will need IPs.
In our case, the example CIDR range is: 10.0.0.0/16
- We Now create two subnets: Subnet A (Public Subnet) with 10.0.1.0/24 and Subnet B (Private Subnet) with 10.0.2.0/24.
#Next, create the public within the VPC in a specific Availability Zone (e.g., us-east-1a) and tag them appropriately
PUBLIC_SUBNET_ID=$(aws ec2 create-subnet — vpc-id $VPC_ID — cidr-block 10.0.1.0/24 — availability-zone us-east-1a — tag-specifications ‘ResourceType=subnet,Tags=[{Key=Name,Value=PublicSubnet}]’ — query Subnet.SubnetId — output text)
echo “Public Subnet ID: $PUBLIC_SUBNET_ID”
#Next, create the private subnets within the VPC in a specific Availability Zone (e.g., us-east-1a) and tag them appropriately
PRIVATE_SUBNET_ID=$(aws ec2 create-subnet — vpc-id $VPC_ID — cidr-block 10.0.2.0/24 — availability-zone us-east-1a — tag-specifications ‘ResourceType=subnet,Tags=[{Key=Name,Value=PrivateSubnet}]’ — query Subnet.SubnetId — output text)
echo “Private Subnet ID: $PRIVATE_SUBNET_ID”
It refers that we can now use 10.0.1.1 to 10.0.1.254 i.e., (254 resources that consume private IP) in the Public Subnet and similarly 254 other resources in private subnet from 10.0.2.1 to 10.0.2.254
- Let’s spin up minimal resources in our respective subnet, and also associate them with a respective security group (For your information, security group are **stateful, **meaning, if an inbound rule is configured, the return traffic initiated due to the incoming rule is automatically allowed to leave the security group boundary), as shown below:

#Lets cover the steps needed on the Public Subnet part first.
# Create the Security Group on Public Subnet and capture its ID
EC2_SG_ID=$(aws ec2 create-security-group --group-name PublicEC2SecurityGroup --description "SG for public EC2" --vpc-id vpc-xxxxxx --query GroupId --output text)
# Allow SSH from your laptop IP
aws ec2 authorize-security-group-ingress --group-id $EC2_SG_ID --protocol tcp --port 22 --cidr 203.0.113.10/32
# Allow HTTP/HTTPS from anywhere (0.0.0.0/0)
aws ec2 authorize-security-group-ingress --group-id $EC2_SG_ID --protocol tcp --port 80 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id $EC2_SG_ID --protocol tcp --port 443 --cidr 0.0.0.0/0
# Launch the EC2 Instance in the public subnet
aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--count 1 \
--instance-type t2.micro \
--key-name MyKeyPair \
--security-group-ids $EC2_SG_ID \
--subnet-id $PUBLIC_SUBNET_ID \
--associate-public-ip-address \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=EC2-Instance}]'
# Create and Configure the Private Security Group
# Create the security group (and capture it's ID) for the RDS instance within the VPC. The rule is to Allow Inbound Port 3306 (MySQL).
RDS_SG_ID=$(aws ec2 create-security-group --group-name PrivateRDSSecurityGroup --description "SG for private RDS" --vpc-id vpc-xxxxxx --query GroupId --output text)
# CRITICAL: Allow inbound MySQL traffic (Port 3306) ONLY from the EC2 Security Group ID created in Step 1
aws ec2 authorize-security-group-ingress --group-id $RDS_SG_ID --protocol tcp --port 3306 --source-security-group $EC2_SG_ID
# Launch the RDS instance, referencing the private subnet group name and the private security group ID.
aws rds create-db-instance \
--db-instance-identifier ecommerce-database-01 \
--db-instance-class db.t3.micro \
--engine mysql \
--master-username dbadmin \
--master-user-password MySecurePassword123 \
--allocated-storage 20 \
--db-subnet-group-name $PRIVATE_SUBNET_ID \
--vpc-security-group-ids $RDS_SG_ID \
--no-multi-az # Explicitly specify single-AZ deployment
Here, during RDS spin up, we have choosen password: *MySecurePassword123 and username: dbadmin*, but in your case you need to use something secure, unique and something only you know. Please note it down or save it somewhere, for future reference.
- For internet connectivity, let’s add an Internet Gateway (IGW).

When an IGW is added, it sits at the edge of VPC (we need to attach it though). There is only one IGW possible to add, per VPC. A hidden/invisible router is always built-in within IGW. We just don’t focus on the router.
# Create IGW and attach it to VPC.
IGW_ID=$(aws ec2 create-internet-gateway — tag-specifications ‘ResourceType=internet-gateway,Tags=[{Key=Name,Value=MyIGW}]’ — query InternetGateway.InternetGatewayId — output text)
aws ec2 attach-internet-gateway — vpc-id $VPC_ID — internet-gateway-id $IGW_ID
PUBLIC_ROUTE_TABLE_ID=$(aws ec2 create-route-table — vpc-id $VPC_ID — tag-specifications ‘ResourceType=route-table,Tags=[{Key=Name,Value=PublicRT}]’ — query RouteTable.RouteTableId — output text)
aws ec2 create-route — route-table-id $PUBLIC_ROUTE_TABLE_ID — destination-cidr-block 0.0.0.0/0 — gateway-id $IGW_ID
aws ec2 associate-route-table — subnet-id $PUBLIC_SUBNET_ID — route-table-id $PUBLIC_ROUTE_TABLE_ID
# Enable auto-assign public IPs for instances launched in this subnet
aws ec2 modify-subnet-attribute — subnet-id $PUBLIC_SUBNET_ID — map-public-ip-on-launch
Can a user now, access the EC2 instance? May be for accessing the e-commerce site (hitting public IP address from the Internet, traffic type: HTTP/HTTPS), running inside?

(Note: Since talk is a network security and VPC focused, we are not showing how we deployed our e-commerce website inside this EC2 at this moment. Imagine, we did it, but for the sake of minimal presentation duration, I skipped to show it.)
And the answer is, not yet!

Why? Because, route table is not associated to our subnets, otherwise we could have accessed EC2 Instance easily.
Creation of Route Table

A default route table exists (which can’t be deleted easily), when VPC is just created. Also, as soon as Private and Public subnet is created, they are implicitly added into the default route table. But for security reasons, we create our own route table.
This can be done in either of the two ways:
-
Edit the default route table to configure routes and attach to the public subnet, and treat it as a public route table. After that, create a private subnet manually and configure routes + attach to private subnet.
-
Or, create a Public route table, and one private route table. Configure respective routes within them and attach them to respective route table. Finally, remove the default route table (because at this point, no subnet is associated within it, thus due to this reason only, now we can delete the default route table).
Create and Configure the Public Route Table
First, create a custom route table within your VPC, add a default route pointing to the Internet Gateway, and then associate it with your public subnet.
# Create the Public Route Table and capture its ID
PUBLIC_RT_ID=$(aws ec2 create-route-table --vpc-id vpc-xxxxxx --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=PublicRT}]' --query RouteTable.RouteTableId --output text)
echo "Public Route Table ID: $PUBLIC_RT_ID"
# Configure a route to the Internet (0.0.0.0/0) via the IGW
aws ec2 create-route --route-table-id $PUBLIC_RT_ID --destination-cidr-block 0.0.0.0/0 --gateway-id igw-xxxxxx
# Associate the Public Route Table with the Public Subnet
aws ec2 associate-route-table --subnet-id subnet-public-id --route-table-id $PUBLIC_RT_ID
Create and Configure the Private Route Table
Next, create a separate private route table. This table will only contain the default local route (which is automatically included when created) but needs to be explicitly associated with your private subnet.
# Create the Private Route Table and capture its ID
PRIVATE_RT_ID=$(aws ec2 create-route-table --vpc-id vpc-xxxxxx --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=PrivateRT}]' --query RouteTable.RouteTableId --output text)
echo "Private Route Table ID: $PRIVATE_RT_ID"
# Note: The 'local' route (communication within the VPC CIDR block) is added automatically.
# The private subnet will not have a route to the internet (0.0.0.0/0), ensuring its isolation.
# Associate the Private Route Table with the Private Subnet
aws ec2 associate-route-table --subnet-id subnet-private-id --route-table-i

Now, the internet traffic can hit the e-commerce website at [http://public-ip-address-of-ec2/](http://public-ip-address-of-ec2/)and the return traffic i.e., HTML pages are served to the user.
Creation of Network Access Control List (NACL)

Network Access Control List, are more like another network security layer, that works for the entire subnet they are created within. They are stateless (meaning, we need to manually define rules for what comes in and what goes out) and are optional in most cases, but used otherwise for use cases like blocking a particular IP and many others.
Create the Public NACL
Create a custom NACL and associate it with your public subnet.
# Create the Public NACL and capture its ID
PUBLIC_NACL_ID=$(aws ec2 create-network-acl --vpc-id vpc-xxxxxx --tag-specifications 'ResourceType=network-acl,Tags=[{Key=Name,Value=PublicNACL}]' --query NetworkAcl.NetworkAclId --output text)
echo "Public NACL ID: $PUBLIC_NACL_ID"
# Associate the NACL with the Public Subnet
aws ec2 replace-network-acl-association --association-id acl-assoc-xxxxxx --network-acl-id $PUBLIC_NACL_ID
# Note: You need the association-id of the default NACL association to replace it.
# A simpler way is to just create a new association:
# aws ec2 associate-network-acl --network-acl-id $PUBLIC_NACL_ID --subnet-id subnet-public-id
Configure rules for the public NACL
Remember, NACLs are stateless (you need both inbound and outbound rules for a session to work) and use numbered rules processed in order. AWS uses 0.0.0.0/0 for “any source/destination”.
# Allow Inbound HTTP (Rule 100)
aws ec2 create-network-acl-entry --network-acl-id $PUBLIC_NACL_ID --ingress --rule-number 100 --protocol tcp --port-range From=80,To=80 --cidr-block 0.0.0.0/0 --rule-action allow
# Allow Inbound HTTPS (Rule 110)
aws ec2 create-network-acl-entry --network-acl-id $PUBLIC_NACL_ID --ingress --rule-number 110 --protocol tcp --port-range From=443,To=443 --cidr-block 0.0.0.0/0 --rule-action allow
# Allow Inbound SSH from your specific admin IP (Rule 120)
aws ec2 create-network-acl-entry --network-acl-id $PUBLIC_NACL_ID --ingress --rule-number 120 --protocol tcp --port-range From=22,To=22 --cidr-block 203.0.113.10/32 --rule-action allow
# Allow all Outbound traffic (Rule 100)
# (For a stateless NACL to work, we must explicitly allow return traffic)
aws ec2 create-network-acl-entry --network-acl-id $PUBLIC_NACL_ID --egress --rule-number 100 --protocol all --cidr-block 0.0.0.0/0 --rule-action allow
Create the Private NACL
Create a custom NACL and associate it with your private subnet where the RDS instance resides. This should be more restrictive.
# Create the Private NACL and capture its ID
PRIVATE_NACL_ID=$(aws ec2 create-network-acl --vpc-id vpc-xxxxxx --tag-specifications 'ResourceType=network-acl,Tags=[{Key=Name,Value=PrivateNACL}]' --query NetworkAcl.NetworkAclId --output text)
echo "Private NACL ID: $PRIVATE_NACL_ID"
# Associate the NACL with the Private Subnet
aws ec2 associate-network-acl --network-acl-id $PRIVATE_NACL_ID --subnet-id subnet-private-id
Configure Private Inbound and Outbound Rules
Configure rules for the private NACL. We only want traffic originating from the public subnet’s CIDR block (10.0.1.0/24) to reach the RDS instance on port 3306.
# Allow Inbound MySQL/Aurora from the Public Subnet CIDR (Rule 100)
aws ec2 create-network-acl-entry --network-acl-id $PRIVATE_NACL_ID --ingress --rule-number 100 --protocol tcp --port-range From=3306,To=3306 --cidr-block 10.0.1.0/24 --rule-action allow
# Allow Outbound return traffic to the Public Subnet (Rule 100)
aws ec2 create-network-acl-entry --network-acl-id $PRIVATE_NACL_ID --egress --rule-number 100 --protocol tcp --port-range From=1024,To=65535 --cidr-block 10.0.1.0/24 --rule-action allow
# Allow Outbound DNS/Ephemeral ports for RDS updates if necessary (Rule 110/120)
aws ec2 create-network-acl-entry --network-acl-id $PRIVATE_NACL_ID --egress --rule-number 110 --protocol udp --port-range From=53,To=53 --cidr-block 0.0.0.0/0 --rule-action allow
Spinning another EC2 for increased traffic (Optional, sharing just the idea)

In case, traffic increase is considered, we spin another EC2 and place it under the existing Security group, just because they serve same purpose i.e., host our e-commerce website.
But, just because another EC2 is spined up in the same subnet and the same security group attached to it, a load balancer is still needed to serve traffic from both of them. Until an Application Load Balancer is added, due to our previous routing rules, only the old one will keep handling all the traffic with stress.
Command to Launch the New Instance
This command is identical to the one used for the first instance, ensuring it shares the same configuration and security rules. We’ll give it a different name tag for identification.
aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--count 1 \
--instance-type t2.micro \
--key-name MyKeyPair \
--security-group-ids sg-public-ec2-sg-id \
--subnet-id subnet-public-id \
--associate-public-ip-address \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=ECommerce-EC2-Instance-2}]'
Adding an Application Load Balancer (Optional, sharing just the idea)

Create a Target Group
The target group defines where traffic is sent and configures health checks for the instances.
TARGET_GROUP_ARN=$(aws elbv2 create-target-group \
--name ECommerce-TG \
--protocol HTTP \
--port 80 \
--target-type instance \
--vpc-id vpc-xxxxxx \
--health-check-protocol HTTP \
--health-check-path /index.html \
--query 'TargetGroups[0].TargetGroupArn' --output text)
echo "Target Group ARN: $TARGET_GROUP_ARN"
Register EC2 Instances with the Target Group
Register your two existing EC2 instances with the newly created target group.
aws elbv2 register-targets \
--target-group-arn $TARGET_GROUP_ARN \
--targets Id=i-abcdefgh1 Id=i-abcdefgh2
Create a Security Group for the ALB
The ALB needs its own security group that allows inbound HTTP traffic from the internet.
ALB_SG_ID=$(aws ec2 create-security-group --group-name ALBSecurityGroup --description "SG for ALB" --vpc-id vpc-xxxxxx --query GroupId --output text)
# Allow HTTP traffic from anywhere
aws ec2 authorize-security-group-ingress --group-id $ALB_SG_ID --protocol tcp --port 80 --cidr 0.0.0.0/0
# (Optional) Allow HTTPS traffic if you plan to use SSL certificates
# aws ec2 authorize-security-group-ingress --group-id $ALB_SG_ID --protocol tcp --port 443 --cidr 0.0.0.0/0
Create the Application Load Balancer
Create the internet-facing ALB in your public subnet and associate the ALB security group. Since you have only one AZ, you must specify only that subnet ID.
ALB_ARN=$(aws elbv2 create-load-balancer \
--name ECommerce-ALB \
--subnets subnet-public-id \
--security-groups $ALB_SG_ID \
--scheme internet-facing \
--type application \
--query 'LoadBalancers[0].LoadBalancerArn' --output text)
echo "ALB ARN: $ALB_ARN"
Create a Listener for the ALB
Create a listener on port 80 that forwards all incoming traffic to the target group you created earlier.
aws elbv2 create-listener \
--load-balancer-arn $ALB_ARN \
--protocol HTTP \
--port 80 \
--default-actions Type=forward,TargetGroupArn=$TARGET_GROUP_ARN
Stronger Security Layer in Mind
If you skipped the second EC2 spinning step and the load balancer configuration part, as they were optional idea shared, we can now focus on the mindset of introducing a stronger security layer.
Generally, as better part of security, we keep EC2 instance (running our E-commerce website) and the RDS, both at the private subnet. And for any reason, we need to update packages or in general access EC2 instance running e-commerce website in the private subnet, we ssh into it through another EC2 instance (bastion instance) created in the public host.
In the diagram below, EC2 instance C is running an e-commerce website and EC2 A is a bastion host in the public subnet. To let EC2 instance C communicate with the internet, we add a Network Address Translation (NAT) Gateway in the public subnet.
Note: NAT gateway does not allow connection initiated from the internet, but only allows the back and forth exchange of traffic, given that EC2 C requested it in the first place.

The bigger Idea
Now let’s see the bigger idea.

What we created earlier is only small scope representation of a single AWS VPC. But in an organization, we need to work with a transit gateway and connect multiple VPC together.

Or even complex architectures emerge, if we need to connect with on-prem data center using Direct Connect associated with the Transit Gateway.

In even complex, more difficult very large-scale enterprise scenario, using site-to-site VPN, we may have to connect to other cloud (here, we have an example of Azure, shown in the diagram). An IPSec tunnel is created between the VPN components of the respective cloud service provider.

In such a case, additional challenges emerge around network security landscape across all the interconnected architecture/infrastructure.
AWS and other Cloud Service Provider’s NAT solution incurs high cost if information exchange is high. Plus, they can’t scale beyond their pre-defined threshold (hard limit) capacity.

Due to the problem that any individual Cloud Service Provider (CSP), like AWS, won’t offer solutions to effectively transfer data to other cloud, because they want people to stick with their very own platform. They introduce hard limits and make changing CSP harder.
They don’t offer tools beyond their native tools, limited within their platform. Even in this scenario, enterprise business still needs monitoring, network and security solutions that work beyond any CSP limitations. There's where third-party solutions like Aviatrix exists as a marketplace solution in every CSP.
But before going into Aviatrix, let’s see two of the secure architecture patterns and practices in AWS.

Organization controls and overviews other accounts and resources centrally

Network segmentation practices and using advanced AWS native security tools

Aviatrix uses a gateway that offers several other features. The gateway together acts as a single distributed data plane.

Using gateway, we now can address deeper and concerning networking and security problems/concerns of larger enterprises.

I share more contents over LinkedIn, hopefully let’s connect over there. Consider following the Author for more contents over Medium.
메타데이터
- post_id
- 0da7a51174cf
- slug
- aviatrix-invisible-network-shield-in-aws-0da7a51174cf
- url
- https://medium.com/@immrbhattarai/aviatrix-invisible-network-shield-in-aws-0da7a51174cf
- canonical_url
- https://medium.com/@immrbhattarai/aviatrix-invisible-network-shield-in-aws-0da7a51174cf
- author_url
- https://medium.com/@immrbhattarai
- status
- ok
- fetched_at
- 2026-07-23 10:42:44