Networking Module
Overview
The Networking Module creates a secure, isolated network infrastructure for the Artos platform. It establishes a Virtual Private Cloud (VPC) with private subnets for application workloads, isolated database subnets, comprehensive security groups, and VPC endpoints for accessing AWS services without internet connectivity. The architecture supports both new VPC creation and integration with existing VPCs.
Key Features
- Private Architecture: All workloads run in private subnets with no direct internet access
- Network Isolation: Separate subnets for applications, databases, and network tiers
- Security Groups: Fine-grained traffic control between components
- VPC Endpoints: Private connectivity to AWS services (S3, ECR, Secrets Manager, etc.)
- Flow Logging: Network traffic monitoring and audit trails
- Flexible Deployment: Support for new VPC creation or existing VPC integration
Network Architecture
VPC Configuration
The VPC provides the isolated network boundary for all Artos resources.
Default CIDR: 10.0.0.0/16 (65,536 IP addresses)
Key Settings:
- DNS Hostnames: Enabled (required for EKS and VPC endpoints)
- DNS Support: Enabled (enables DNS resolution within VPC)
Existing VPC Support: The module can use an existing VPC instead of creating a new one. When vpc_id is provided, the module:
- Creates only the required subnets (if not provided)
- Integrates with existing networking infrastructure
Subnet Design
The module creates three types of subnets distributed across availability zones for high availability.
1. Private Subnets
Purpose: Host EKS worker nodes, application pods, and internal services.
Default CIDRs:
10.0.10.0/24 (254 IPs per subnet)
10.0.20.0/24
Deployed Across: 2 availability zones (configurable)
Key Characteristics:
- No public IP assignment
- No direct internet access
- Traffic routed within VPC or through VPC endpoints
- Used for EKS control plane and worker nodes
Workloads:
- EKS worker nodes
- Application pods (backend API, Celery workers)
- Internal load balancers
- Bastion hosts
2. Database Subnets
Purpose: Completely isolated subnets for RDS databases.
Default CIDRs:
10.0.100.0/24
10.0.200.0/24
Deployed Across: 2 availability zones (required for RDS Multi-AZ)
Key Characteristics:
- Fully isolated from internet
- Only accessible from EKS nodes via security groups
- Separate route table with no routes (isolated)
- Used exclusively for database instances
Workloads:
- RDS PostgreSQL instances
- Database replicas
- Read replicas (if configured)
Route Tables
Route tables control how traffic flows between subnets and external networks.
Private Route Table
Associated With: Private subnets
Routes:
- Local VPC traffic only (automatic)
- No internet gateway route (no internet access)
- Enterprise connectivity via VPC peering or VPN (configured separately)
Purpose: Keeps application workloads isolated from internet while allowing VPC-internal communication.
Database Route Table
Associated With: Database subnets
Routes:
- Local VPC traffic only (automatic)
- No other routes (maximum isolation)
Purpose: Ensures databases are completely isolated and only accessible via security group rules.
Security Groups
Security groups act as virtual firewalls controlling inbound and outbound traffic for resources.
1. EKS Cluster Security Group
Attached To: EKS control plane
Ingress Rules:
| Port | Protocol | Source | Description |
|---|
| 443 | TCP | EKS Nodes SG | HTTPS API server from nodes |
| 443 | TCP | Additional SGs | HTTPS from bastion/other resources |
| 443 | TCP | VPC CIDR | HTTPS from all VPC resources |
| 2379-2380 | TCP | Self | etcd client/server communication |
| 12379 | TCP | Self | etcd metrics |
Egress Rules:
- All traffic:
0.0.0.0/0 (required for cluster operations and AWS API calls)
Purpose: Controls access to the Kubernetes API server and enables control plane components to communicate.
2. EKS Nodes Security Group
Attached To: EKS worker nodes (EC2 instances)
Ingress Rules:
| Port Range | Protocol | Source | Description |
|---|
| All | TCP | Self | Node-to-node communication |
| All | TCP | VPC CIDR | Communication from cluster |
| 1025-65535 | TCP | Self | Dynamic port range for services |
| All | UDP | Self | Pod-to-pod UDP traffic |
| 10250 | TCP | VPC CIDR | kubelet API |
| 30000-32767 | TCP | Self | NodePort services |
| 443 | TCP | VPC CIDR | HTTPS API access |
| 2379-2380 | TCP | VPC CIDR | etcd communication |
| 12379 | TCP | VPC CIDR | etcd metrics |
Egress Rules:
- All traffic to VPC CIDR (allows communication within VPC)
Purpose: Enables Kubernetes networking, pod-to-pod communication, and service exposure.
Port 10250: The kubelet API port is critical for Kubernetes operations. The control plane uses this port to execute commands on nodes, collect metrics, and manage pod lifecycle.
3. RDS Security Group
Attached To: RDS database instances
Ingress Rules:
| Port | Protocol | Source | Description |
|---|
| 5432 | TCP | EKS Nodes SG | PostgreSQL from application pods |
Egress Rules:
Purpose: Restricts database access to only EKS worker nodes (application pods).
Database Isolation: The RDS security group only allows traffic from EKS nodes. Direct access from developer workstations requires SSH tunneling through the bastion host or VPN configuration.
4. ALB Security Group (Internal)
Attached To: Internal Application Load Balancer
Ingress Rules:
| Port | Protocol | Source | Description |
|---|
| 80 | TCP | Frontend ALB SG | HTTP from frontend |
| 443 | TCP | Frontend ALB SG | HTTPS from frontend |
| 443 | TCP | Bastion SG | HTTPS from bastion (optional) |
Egress Rules:
- All traffic:
0.0.0.0/0 (ALB needs to reach target pods)
Purpose: Controls access to the backend API load balancer.
5. Frontend ALB Security Group
Attached To: Frontend Application Load Balancer
Ingress Rules:
| Port | Protocol | Source | Description |
|---|
| 443 | TCP | VPC CIDR | HTTPS from VPC resources |
| 443 | TCP | Bastion SG | HTTPS from bastion (optional) |
Egress Rules:
- All traffic:
0.0.0.0/0 (ALB needs to reach target pods)
Purpose: Controls access to the frontend application.
6. VPC Endpoints Security Group
Attached To: All VPC interface endpoints
Ingress Rules:
| Port | Protocol | Source | Description |
|---|
| 443 | TCP | VPC CIDR | HTTPS from all VPC resources |
Egress Rules:
Purpose: Allows VPC resources to access AWS services via private endpoints.
VPC Endpoints
VPC endpoints enable private connectivity to AWS services without requiring internet access or NAT gateways.
Gateway Endpoints
S3 Endpoint
Type: Gateway endpoint (routes added to route tables)
Service: com.amazonaws.{region}.s3
Purpose: Private access to S3 buckets
Use Cases:
- Application file storage and retrieval
- ECR image layer storage (ECR uses S3 backend)
- Log archival and backups
- Static asset delivery
Interface Endpoints
Interface endpoints create Elastic Network Interfaces (ENIs) in your subnets with private IP addresses.
ECR Endpoints
Services:
com.amazonaws.{region}.ecr.dkr - Docker Registry API
com.amazonaws.{region}.ecr.api - ECR control plane API
Purpose: Pull container images from ECR without internet access
Use Cases:
- EKS nodes pulling container images
- CI/CD pipelines accessing ECR
- Image scanning and vulnerability checks
Why Both Are Needed:
ecr.dkr: Docker daemon communicates with ECR registry
ecr.api: ECR control plane operations (list images, get authorization tokens)
EC2 Endpoint
Service: com.amazonaws.{region}.ec2
Purpose: EC2 API operations from private subnets
Use Cases:
- EKS node bootstrap scripts calling EC2 APIs
- Auto Scaling Group operations
- ENI attachment and management
- Instance metadata access
STS Endpoint
Service: com.amazonaws.{region}.sts
Purpose: AWS Security Token Service for IAM role assumption
Use Cases:
- IRSA (IAM Roles for Service Accounts) in EKS
- Pod service accounts assuming IAM roles
- Temporary credential generation
- Cross-account access
Critical For: EKS workloads using IAM roles - without this endpoint, pods cannot assume IAM roles.
EKS Endpoint
Service: com.amazonaws.{region}.eks
Purpose: EKS control plane API access
Use Cases:
- kubectl commands from bastion host
- EKS cluster management operations
- eksctl operations
- Cluster configuration updates
Elastic Load Balancing Endpoint
Service: com.amazonaws.{region}.elasticloadbalancing
Purpose: Load balancer management APIs
Use Cases:
- AWS Load Balancer Controller creating ALBs
- Target group registration
- Health check configuration
- Listener rule management
Secrets Manager Endpoint
Service: com.amazonaws.{region}.secretsmanager
Purpose: Retrieve secrets from AWS Secrets Manager
Use Cases:
- Application pods fetching database credentials
- API key retrieval
- Configuration secret access
- Certificate management
SSM Endpoints
Services:
com.amazonaws.{region}.ssm - Systems Manager
com.amazonaws.{region}.ssmmessages - Session Manager messaging
com.amazonaws.{region}.ec2messages - SSM Agent communication
Purpose: AWS Systems Manager Session Manager connectivity
Use Cases:
- SSH-less bastion host access
- Node troubleshooting via Session Manager
- Secure remote command execution
- Session logging and auditing
Critical For: Bastion host connectivity without SSH or public IPs.
Private DNS
All interface endpoints have Private DNS enabled, which means:
- AWS service calls automatically resolve to the VPC endpoint IP
- No application code changes required
- Services use standard AWS SDK endpoints
- Traffic stays within VPC
Example:
# This code automatically uses VPC endpoint
import boto3
s3_client = boto3.client('s3') # Resolves to VPC endpoint, not public internet
VPC Flow Logs
Flow logs capture network traffic metadata for security analysis and troubleshooting.
Log Destination: CloudWatch Logs group /vpc/{vpc_id}/flow-logs
Retention: 14 days
Traffic Captured: ALL (accepted and rejected traffic)
Aggregation Interval: 60 seconds
Logged Fields:
- Source and destination IP addresses
- Source and destination ports
- Protocol
- Number of packets
- Number of bytes
- Action (ACCEPT or REJECT)
- Timestamp
Use Cases:
- Security incident investigation
- Network troubleshooting
- Compliance auditing
- Traffic pattern analysis
- Unusual traffic detection
Example Flow Log Entry:
2 123456789012 eni-abc123 10.0.10.50 10.0.100.20 45234 5432 6 10 1024 1609459200 1609459260 ACCEPT OK
Interpretation:
- Source:
10.0.10.50:45234 (EKS node)
- Destination:
10.0.100.20:5432 (RDS database)
- Protocol: 6 (TCP)
- Action: ACCEPT (security groups allowed)
Module Configuration
Basic Configuration (New VPC)
module "networking" {
source = "./modules/networking"
name_prefix = "artos-production"
# VPC configuration
vpc_cidr = "10.0.0.0/16"
# Subnet configuration
private_subnet_cidrs = ["10.0.10.0/24", "10.0.20.0/24"]
database_subnet_cidrs = ["10.0.100.0/24", "10.0.200.0/24"]
# Enable VPC endpoints
enable_vpc_endpoints = true
tags = {
Environment = "production"
}
}
Configuration with Existing VPC
module "networking" {
source = "./modules/networking"
name_prefix = "artos-production"
# Use existing VPC
vpc_id = "vpc-existing123"
vpc_cidr = "10.0.0.0/16" # Must match existing VPC CIDR
# Use existing subnets or create new ones
private_subnet_ids = ["subnet-abc123", "subnet-def456"]
database_subnet_ids = ["subnet-ghi789", "subnet-jkl012"]
# Enable VPC endpoints in existing VPC
enable_vpc_endpoints = true
tags = {
Environment = "production"
}
}
Production Configuration
module "networking_production" {
source = "./modules/networking"
name_prefix = "artos-production"
# VPC with larger CIDR for growth
vpc_cidr = "10.0.0.0/16"
# Multiple AZs for high availability
private_subnet_cidrs = [
"10.0.10.0/24", # AZ1 - 254 IPs
"10.0.20.0/24", # AZ2 - 254 IPs
"10.0.30.0/24" # AZ3 - 254 IPs
]
database_subnet_cidrs = [
"10.0.100.0/24", # AZ1
"10.0.200.0/24", # AZ2
"10.0.210.0/24" # AZ3
]
# Enable all VPC endpoints for private connectivity
enable_vpc_endpoints = true
# Additional security groups that can access EKS (e.g., bastion)
additional_security_group_ids = [
module.bastion.bastion_security_group_id
]
# Bastion access to ALBs
bastion_security_group_id = module.bastion.bastion_security_group_id
tags = {
Environment = "production"
ManagedBy = "terraform"
CostCenter = "engineering"
}
}
Development Configuration
module "networking_dev" {
source = "./modules/networking"
name_prefix = "artos-dev"
# Smaller VPC for development
vpc_cidr = "10.1.0.0/16"
# Single AZ for development
private_subnet_cidrs = ["10.1.10.0/24"]
database_subnet_cidrs = ["10.1.100.0/24"]
# VPC endpoints optional for dev (can reduce costs)
enable_vpc_endpoints = true
tags = {
Environment = "development"
AutoShutdown = "true"
}
}
Security Best Practices
1. Network Segmentation
The module implements defense-in-depth through multiple isolation layers:
Subnet Isolation:
- Application workloads in private subnets
- Databases in isolated database subnets
- No direct communication between tiers without security group rules
Route Table Isolation:
- Database subnets have no routes (local traffic only)
- Private subnets have no internet gateway route
- Each tier has dedicated route table
2. Security Group Rules
Follow the principle of least privilege:
Good Practices:
- Specific port ranges instead of “all ports”
- Reference security groups instead of CIDR blocks when possible
- Document each rule’s purpose in descriptions
- Regular audit of unused rules
Example:
# Good - specific and documented
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.eks_nodes.id]
description = "PostgreSQL access from EKS pods only"
}
# Avoid - too permissive
ingress {
from_port = 0
to_port = 65535
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
3. VPC Endpoint Security
VPC endpoints should be protected:
- Dedicated security group for endpoints
- Restrict access to VPC CIDR only
- Enable Private DNS for transparent access
- Monitor endpoint usage via VPC Flow Logs
4. Flow Log Analysis
Regularly analyze VPC Flow Logs for:
Security Threats:
- Rejected traffic patterns (potential attacks)
- Unusual source IPs
- Port scanning attempts
- Data exfiltration indicators
Example CloudWatch Insights Query:
fields @timestamp, srcAddr, dstAddr, dstPort, action
| filter action = "REJECT"
| stats count() by srcAddr, dstPort
| sort count desc
| limit 20
5. CIDR Planning
Plan IP address space carefully:
Recommendations:
- Use RFC 1918 private ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
- Leave room for growth (don’t use /24 for entire VPC)
- Avoid overlapping CIDRs with corporate networks
- Reserve ranges for future subnets
Example Allocation:
VPC: 10.0.0.0/16 (65,536 IPs)
├── Private Subnets: 10.0.0.0/20 (4,096 IPs)
├── Database Subnets: 10.0.100.0/22 (1,024 IPs)
├── Reserved for Future: 10.0.104.0/22 (1,024 IPs)
└── Management: 10.0.255.0/24 (256 IPs)
Troubleshooting
Cannot Access AWS Services from Pods
Symptoms: Pods fail to access S3, ECR, Secrets Manager, etc.
Troubleshooting:
- Verify VPC Endpoints are Created:
aws ec2 describe-vpc-endpoints \
--filters "Name=vpc-id,Values=<vpc-id>" \
--query 'VpcEndpoints[*].[ServiceName,State]' \
--output table
- Check Endpoint Security Group:
aws ec2 describe-security-groups \
--group-ids <endpoint-sg-id> \
--query 'SecurityGroups[0].IpPermissions'
- Verify Private DNS is Enabled:
aws ec2 describe-vpc-endpoints \
--vpc-endpoint-ids <endpoint-id> \
--query 'VpcEndpoints[0].PrivateDnsEnabled'
# Should return: true
- Test from Pod:
kubectl run test-pod --image=amazon/aws-cli --rm -it -- /bin/bash
# Inside pod:
nslookup s3.amazonaws.com # Should resolve to VPC endpoint private IP
aws s3 ls # Should work without internet access
RDS Connection Failures
Symptoms: Applications cannot connect to RDS database.
Troubleshooting:
- Verify Security Group Rules:
aws ec2 describe-security-groups \
--group-ids <rds-sg-id> \
--query 'SecurityGroups[0].IpPermissions[?ToPort==`5432`]'
- Check if Source is EKS Nodes Security Group:
# Should show EKS nodes security group as source
- Test Connectivity from Pod:
kubectl run -it --rm debug --image=postgres:14 --restart=Never -- \
psql -h <rds-endpoint> -U <username> -d postgres
- Check VPC Flow Logs:
# Look for REJECT actions on port 5432
aws logs filter-log-events \
--log-group-name /vpc/<vpc-id>/flow-logs \
--filter-pattern "[version, account, eni, source, destination, srcport, destport=\"5432\", protocol, packets, bytes, windowstart, windowend, action=\"REJECT\", flowlogstatus]"
Bastion Cannot Access EKS API
Symptoms: kubectl commands fail from bastion host.
Solutions:
- Verify Bastion in Additional Security Groups:
additional_security_group_ids = [
module.bastion.bastion_security_group_id
]
- Check EKS Security Group Ingress:
aws ec2 describe-security-groups \
--group-ids <eks-cluster-sg> \
--query 'SecurityGroups[0].IpPermissions[?ToPort==`443`]'
- Verify EKS VPC Endpoint:
aws ec2 describe-vpc-endpoints \
--filters "Name=service-name,Values=com.amazonaws.<region>.eks" \
--query 'VpcEndpoints[0].[State,PrivateDnsEnabled]'
- EKS Module - Uses VPC and subnets for cluster deployment
- RDS Module - Uses database subnets and security groups
- Bastion Module - Deployed in private subnets with VPC endpoint access
- IAM Module - IRSA requires STS VPC endpoint
Module Maintenance: This module is compatible with Terraform 1.0+ and AWS Provider 5.x. VPC endpoints and security groups follow AWS best practices for private cloud deployments. Review and adjust CIDR blocks based on your network architecture requirements before deployment.