VPN & BGP & Connectivity
Site-to-site VPN with IPsec, BGP path selection and route manipulation, dedicated connectivity with Direct Connect and Cloud Interconnect, and hybrid network security patterns.
Site-to-Site VPN
IPsec Fundamentals
IPsec Protocol Suite
ESP (Encapsulating Security Payload): Provides encryption, authentication, and integrity. The standard choice — use ESP in tunnel mode for site-to-site VPN.
AH (Authentication Header): Authentication and integrity only, no encryption. Rarely used in modern deployments; incompatible with NAT traversal.
Tunnel mode: Entire original IP packet (header + payload) is encrypted and encapsulated. Used for site-to-site VPN — the VPN gateway encrypts the original packet.
Transport mode: Only payload encrypted, original IP header preserved. Used for host-to-host IPsec (e.g. server-to-server inside a network).
IKEv1 vs IKEv2
| Feature | IKEv1 | IKEv2 |
|---|---|---|
| RFC | RFC 2409 (1998) | RFC 7296 (2014) |
| Handshake messages | 9 messages (Main Mode) or 6 (Aggressive Mode) | 4 messages (always) |
| NAT traversal | Extension (RFC 3947) | Built-in |
| EAP auth | Not supported natively | Native (MFA integration) |
| MOBIKE | No | Yes (mobile client IP changes) |
| Dead Peer Detection | Extension | Built-in liveness check |
| Recommendation | Legacy — use only when forced | Always prefer IKEv2 |
IPsec Phase 1 & Phase 2
# Phase 1 (IKE SA / ISAKMP) — establishes secure control channel
# Negotiates: encryption algorithm, hash, DH group, lifetime, auth method (PSK or certs)
# Typical config:
# Encryption: AES-256
# Hash: SHA-256 or SHA-384
# DH Group: 14 (2048-bit) or 19 (256-bit ECDH) — prefer 19+
# Lifetime: 28800 seconds (8 hours)
# Auth: Pre-shared key (PSK) or RSA/ECDSA certificates
# Phase 2 (IPsec SA) — establishes data tunnel using Phase 1 as protection
# Negotiates: ESP encryption/hash, PFS group, lifetime, traffic selectors (interesting traffic)
# Typical config:
# Protocol: ESP
# Encryption: AES-256-GCM (AEAD — no separate hash needed)
# PFS: Group 14 or 19 (Perfect Forward Secrecy — new DH exchange per SA)
# Lifetime: 3600 seconds (1 hour)
# Traffic: 10.0.0.0/16 <→ 192.168.0.0/16
# Quick troubleshooting
# Show IKE SAs (Phase 1)
show crypto isakmp sa # Cisco IOS
ip xfrm state # Linux (StrongSwan/Libreswan)
strongswan statusall # StrongSwan
# Show IPsec SAs (Phase 2)
show crypto ipsec sa # Cisco
ip xfrm policy # Linux
AWS Site-to-Site VPN
# AWS Site-to-Site VPN components:
# Virtual Private Gateway (VGW) — AWS-side VPN endpoint, attached to VPC
# Customer Gateway (CGW) — represents your on-prem device
# VPN Connection — 2 tunnels (different public IPs/AZs) per connection for HA
# Terraform — AWS VPN with BGP
resource "aws_customer_gateway" "onprem" {
bgp_asn = 65000 # your on-prem ASN
ip_address = "203.0.113.1" # on-prem router public IP
type = "ipsec.1"
tags = { Name = "onprem-cgw" }
}
resource "aws_vpn_gateway" "vgw" {
vpc_id = aws_vpc.prod.id
amazon_side_asn = 64512 # AWS private ASN
tags = { Name = "prod-vgw" }
}
resource "aws_vpn_connection" "main" {
vpn_gateway_id = aws_vpn_gateway.vgw.id
customer_gateway_id = aws_customer_gateway.onprem.id
type = "ipsec.1"
static_routes_only = false # false = use BGP
# Tunnel 1 configuration
tunnel1_inside_cidr = "169.254.10.0/30"
tunnel1_preshared_key = var.vpn_psk_1
# Phase 1
tunnel1_ike_versions = ["ikev2"]
tunnel1_phase1_encryption_algorithms = ["AES256"]
tunnel1_phase1_integrity_algorithms = ["SHA2-256"]
tunnel1_phase1_dh_group_numbers = [14, 19]
tunnel1_phase1_lifetime_seconds = 28800
# Phase 2
tunnel1_phase2_encryption_algorithms = ["AES256-GCM-16"]
tunnel1_phase2_integrity_algorithms = ["AEAD-AES-256-GCM-16"]
tunnel1_phase2_dh_group_numbers = [14, 19]
tunnel1_phase2_lifetime_seconds = 3600
tags = { Name = "prod-vpn-to-onprem" }
}
# Enable route propagation from VGW to route tables
resource "aws_vpn_gateway_route_propagation" "private" {
route_table_id = aws_route_table.private_app[0].id
vpn_gateway_id = aws_vpn_gateway.vgw.id
}
GCP Cloud VPN
Classic VPN vs HA VPN
Classic VPN: Single interface, single tunnel, 99.9% SLA. Not recommended for production — single point of failure. Does not support dynamic routing with BGP if using policy-based routes.
HA VPN: Two interfaces, two tunnels to two on-prem devices (or two interfaces of same device), 99.99% SLA. Requires BGP. The recommended production option.
# HA VPN with BGP — full redundancy
gcloud compute vpn-gateways create prod-ha-vpn \
--network=prod-vpc \
--region=us-central1
# On-prem peer gateway (represents your device — 2 interfaces for redundancy)
gcloud compute external-vpn-gateways create onprem-gw \
--interfaces 0=203.0.113.1,1=203.0.113.2
# Cloud Router for BGP
gcloud compute routers create prod-vpn-router \
--network=prod-vpc \
--region=us-central1 \
--asn=65001 # GCP side ASN
# VPN Tunnels (2 tunnels for HA — one to each peer interface)
gcloud compute vpn-tunnels create tunnel-1 \
--region=us-central1 \
--vpn-gateway=prod-ha-vpn \
--vpn-gateway-interface=0 \
--peer-external-gateway=onprem-gw \
--peer-external-gateway-interface=0 \
--shared-secret=$PSK_1 \
--router=prod-vpn-router \
--ike-version=2
gcloud compute vpn-tunnels create tunnel-2 \
--region=us-central1 \
--vpn-gateway=prod-ha-vpn \
--vpn-gateway-interface=1 \
--peer-external-gateway=onprem-gw \
--peer-external-gateway-interface=1 \
--shared-secret=$PSK_2 \
--router=prod-vpn-router \
--ike-version=2
# BGP sessions on Cloud Router (one per tunnel)
gcloud compute routers add-bgp-peer prod-vpn-router \
--region=us-central1 \
--peer-name=onprem-peer-1 \
--interface=if-tunnel-1 \
--peer-ip-address=169.254.10.1 \
--peer-asn=65000 \
--advertised-route-priority=100
gcloud compute routers add-bgp-peer prod-vpn-router \
--region=us-central1 \
--peer-name=onprem-peer-2 \
--interface=if-tunnel-2 \
--peer-ip-address=169.254.10.5 \
--peer-asn=65000 \
--advertised-route-priority=100
# Check BGP status
gcloud compute routers get-status prod-vpn-router --region=us-central1
OpenVPN Configuration
# OpenVPN server config (/etc/openvpn/server.conf)
port 1194
proto udp
dev tun
ca /etc/openvpn/pki/ca.crt
cert /etc/openvpn/pki/issued/server.crt
key /etc/openvpn/pki/private/server.key
dh /etc/openvpn/pki/dh.pem
tls-auth /etc/openvpn/ta.key 0
server 10.8.0.0 255.255.255.0 # VPN client IP pool
push "route 10.0.0.0 255.255.0.0" # push VPC route to clients
push "dhcp-option DNS 10.0.0.2" # push VPC DNS
keepalive 10 120
cipher AES-256-GCM
auth SHA256
tls-version-min 1.2
tls-cipher TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384
# MFA with Google Authenticator (PAM plugin)
plugin /usr/lib/openvpn/openvpn-plugin-auth-pam.so openvpn
verify-client-cert none
username-as-common-name
auth-user-pass-verify /etc/openvpn/auth-script.sh via-env
user nobody
group nogroup
persist-key
persist-tun
status /var/log/openvpn/status.log
verb 3
# Client config (.ovpn)
client
dev tun
proto udp
remote vpn.example.com 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-GCM
auth SHA256
auth-user-pass # prompt for username + OTP
verb 3
<ca>
-----BEGIN CERTIFICATE-----
... CA certificate ...
-----END CERTIFICATE-----
</ca>
WireGuard
# WireGuard: modern, minimal VPN (Linux kernel 5.6+)
# Uses: Curve25519 (key exchange), ChaCha20-Poly1305 (encryption), BLAKE2s (hash)
# ~4,000 lines of code vs OpenVPN's ~100,000 — dramatically reduced attack surface
# Generate key pair
wg genkey | tee server_private.key | wg pubkey > server_public.key
wg genkey | tee client_private.key | wg pubkey > client_public.key
# Server config (/etc/wireguard/wg0.conf)
[Interface]
Address = 10.200.0.1/24
ListenPort = 51820
PrivateKey = <server_private_key>
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# Remote office
PublicKey = <client_public_key>
AllowedIPs = 10.200.0.2/32, 192.168.1.0/24 # client IP + remote subnet
# Client config
[Interface]
Address = 10.200.0.2/24
PrivateKey = <client_private_key>
DNS = 10.0.0.2
[Peer]
PublicKey = <server_public_key>
Endpoint = vpn.example.com:51820
AllowedIPs = 10.0.0.0/16, 10.200.0.1/32 # route VPC + server through tunnel
PersistentKeepalive = 25
# Manage WireGuard
wg-quick up wg0
wg show # status, peer handshakes, transfer stats
systemctl enable wg-quick@wg0
# Performance comparison (1 Gbps test, AWS c5n.2xlarge):
# WireGuard: ~950 Mbps CPU ~15%
# OpenVPN: ~200 Mbps CPU ~100%
# IPsec/StrongSwan: ~700 Mbps CPU ~40%
BGP — Border Gateway Protocol
BGP Fundamentals
iBGP vs eBGP
eBGP (external BGP): Between routers in different Autonomous Systems (ASNs). Direct peering (TTL=1 by default). Used between cloud and on-premises, between ISPs.
iBGP (internal BGP): Between routers in the same AS. Full mesh or route reflectors required. Used internally — e.g. between Cloud Routers in GCP, between Direct Connect gateways.
# ASN ranges
# Public ASNs (IANA assigned): 1–64495
# Private ASNs (RFC 6996): 64512–65534 (2-byte), 4200000000–4294967294 (4-byte)
# AWS uses: 64512 by default for VGW/TGW (configurable)
# GCP Cloud Router: 64512–65534 (your choice)
# Your on-prem: any private ASN (e.g. 65000)
# Key BGP attributes (in path selection order):
# WEIGHT — Cisco proprietary, local to router, higher preferred
# LOCAL_PREF — iBGP, higher preferred (inbound traffic control)
# ORIGINATE — locally originated routes preferred over learned
# AS_PATH — shorter preferred (hop count); manipulated for traffic engineering
# ORIGIN — IGP (i) > EGP (e) > Incomplete (?)
# MED — Multi-Exit Discriminator; lower preferred (outbound traffic control)
# eBGP over iBGP — routes learned via eBGP preferred
# IGP metric — lower cost to next-hop preferred
# Router ID — lowest router ID (tiebreaker)
BGP Path Selection Step-by-Step
# BGP best path selection algorithm (Cisco IOS order):
# 1. Highest WEIGHT (Cisco proprietary — local to router)
# 2. Highest LOCAL_PREF (set within iBGP domain — controls inbound preference)
# 3. Locally originated routes (network statement or redistribute)
# 4. Shortest AS_PATH length (fewer hops = preferred)
# 5. Lowest ORIGIN code (IGP=0 > EGP=1 > Incomplete=2)
# 6. Lowest MED (when multiple paths from same neighboring AS)
# 7. eBGP over iBGP learned routes
# 8. Lowest IGP metric to BGP next-hop
# 9. Oldest eBGP route (stability preference)
# 10. Lowest BGP Router ID (ultimate tiebreaker)
# Memory aid: "We Love Original Short Memories Even If It Hurts"
# W - Weight
# L - Local Pref
# O - Originate (local)
# S - Shortest AS path
# M - Minimum Origin code
# E - External (eBGP over iBGP)
# I - Interior (IGP metric)
# I - ID (Router ID)
BGP Route Manipulation
# AS_PATH Prepending — make a path look longer (less preferred)
# Use case: route traffic away from a specific link (e.g. backup ISP)
route-map PREPEND_BACKUP permit 10
set as-path prepend 65000 65000 65000 # prepend your own ASN 3 times
!
neighbor 203.0.113.2 route-map PREPEND_BACKUP out
# LOCAL_PREF — influence inbound traffic (which link traffic enters your AS)
# Set higher LOCAL_PREF for preferred path
route-map PREFER_PRIMARY permit 10
match ip address prefix-list ALL
set local-preference 200 # default is 100; higher = preferred
!
neighbor 10.1.1.1 route-map PREFER_PRIMARY in
# MED — influence outbound traffic from a neighboring AS
# Lower MED = preferred (neighboring AS will prefer this path to reach you)
route-map SET_MED permit 10
set metric 100 # lower MED for primary link
!
neighbor 203.0.113.1 route-map SET_MED out
# BGP Communities — tag routes for policy application downstream
# Well-known communities:
# NO_EXPORT (65535:65281) — don't advertise outside AS
# NO_ADVERTISE(65535:65282) — don't advertise to any peer
# Custom: 65000:100 = high priority, 65000:200 = backup
route-map TAG_WITH_COMMUNITY permit 10
set community 65000:100 additive
AWS BGP with Direct Connect & Transit Gateway
# AWS Direct Connect BGP:
# - AWS side ASN: 7224 (fixed for DX, or 64512 on VGW/TGW)
# - Customer ASN: any public or private
# - MD5 auth supported for BGP session security
# - BFD (Bidirectional Forwarding Detection) enabled by default
# Direct Connect BGP route control:
# Use LOCAL_PREF on your router to prefer DX over VPN backup
# Use BGP communities on what you advertise to control AWS routing:
# 7224:9100 — LOCAL_PREF 100 (standard)
# 7224:7100 — LOCAL_PREF 100 on all routers in that region
# 7224:8100 — LOCAL_PREF 100 on all AWS routers
# Transit Gateway route table BGP propagation
resource "aws_ec2_transit_gateway_route_table" "prod" {
transit_gateway_id = aws_ec2_transit_gateway.main.id
tags = { Name = "prod-tgw-rt" }
}
resource "aws_ec2_transit_gateway_route_table_propagation" "dx" {
transit_gateway_attachment_id = aws_dx_transit_virtual_interface.main.id
transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.prod.id
}
# Static route on TGW (override BGP for specific prefix)
resource "aws_ec2_transit_gateway_route" "onprem_specific" {
destination_cidr_block = "192.168.100.0/24"
transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.prod.id
transit_gateway_attachment_id = aws_dx_transit_virtual_interface.main.id
}
GCP Cloud Router & BGP
# Cloud Router: GCP's managed BGP speaker — used with HA VPN and Cloud Interconnect
# Advertises VPC subnet routes and custom prefixes via BGP
# Custom route advertisement (advertise additional CIDRs beyond subnets)
gcloud compute routers update prod-vpn-router \
--region=us-central1 \
--advertisement-mode=custom \
--set-advertisement-groups=ALL_SUBNETS \
--set-advertisement-ranges=10.0.0.0/16,10.100.0.0/16
# Update BGP peer with custom MED (to influence return path from on-prem)
gcloud compute routers update-bgp-peer prod-vpn-router \
--region=us-central1 \
--peer-name=onprem-peer-1 \
--advertised-route-priority=100 # MED value sent to peer
# Check BGP session details
gcloud compute routers get-status prod-vpn-router \
--region=us-central1 \
--format='json(result.bgpPeerStatus)'
# Learned routes from BGP peer
gcloud compute routers get-status prod-vpn-router \
--region=us-central1 \
--format='json(result.bgpPeerStatus[].advertisedRoutes)'
BGP Security
# RPKI (Resource Public Key Infrastructure) — cryptographically validates route origins
# ROA (Route Origin Authorization): signed record saying "ASN X is authorized to originate prefix Y/len"
# Check RPKI validity of a prefix
curl -s "https://stat.ripe.net/data/rpki-validation/data.json?resource=AS65000&prefix=192.0.2.0/24"
# BGP prefix filtering — whitelist only expected prefixes from peers
# On Cisco IOS:
ip prefix-list ALLOWED_FROM_ONPREM seq 10 permit 192.168.0.0/16 le 24
ip prefix-list ALLOWED_FROM_ONPREM seq 20 permit 10.200.0.0/16 le 24
!
route-map INBOUND_FILTER permit 10
match ip address prefix-list ALLOWED_FROM_ONPREM
!
neighbor 203.0.113.1 route-map INBOUND_FILTER in
neighbor 203.0.113.1 maximum-prefix 100 90 # alert at 90%, drop session at 100
# MD5 authentication for BGP session
neighbor 203.0.113.1 password 0 your-md5-secret
# AWS Direct Connect BGP MD5 auth (set in CGW config):
resource "aws_customer_gateway" "main" {
bgp_asn = 65000
ip_address = "203.0.113.1"
type = "ipsec.1"
# BGP auth key set in VPN connection or DX virtual interface
}
Dedicated Connectivity
AWS Direct Connect
Direct Connect Architecture
Dedicated connection: 1G, 10G, or 100G port directly at an AWS DX location. Your equipment or colocation provider connects here.
Hosted connection: Sub-1G to 10G via an AWS Partner. Faster to provision, more flexible bandwidth. No control over physical redundancy.
VIF types: Private VIF (connects to VGW/TGW — reaches private IPs), Public VIF (reaches AWS public services like S3, not VPC), Transit VIF (connects to Direct Connect Gateway → Transit Gateway).
# Direct Connect Gateway: allows one DX connection to reach VPCs in multiple regions
# (unlike VGW which is VPC/region specific)
resource "aws_dx_gateway" "main" {
name = "prod-dxgw"
amazon_side_asn = 64512
}
resource "aws_dx_transit_virtual_interface" "prod" {
connection_id = var.dx_connection_id # physical DX connection ID
name = "prod-transit-vif"
vlan = 100 # 802.1Q VLAN tag
address_family = "ipv4"
bgp_asn = 65000
amazon_address = "169.254.100.1/30" # AWS-side BGP link address
customer_address = "169.254.100.2/30" # customer-side BGP link address
bgp_auth_key = var.bgp_md5_key
dx_gateway_id = aws_dx_gateway.main.id
}
resource "aws_dx_gateway_association" "tgw" {
dx_gateway_id = aws_dx_gateway.main.id
associated_gateway_id = aws_ec2_transit_gateway.main.id
allowed_prefixes = [
"10.0.0.0/8",
"172.16.0.0/12",
]
}
# LAG (Link Aggregation Group) — bundle multiple DX ports for bandwidth + redundancy
resource "aws_dx_lag" "prod" {
name = "prod-dx-lag"
connections_bandwidth = "10Gbps"
location = "EqDC2" # DX location code
force_destroy = false
}
GCP Cloud Interconnect
| Option | Speed | SLA | Use Case |
|---|---|---|---|
| Dedicated Interconnect | 10G or 100G per circuit | 99.99% (4 circuits) | High bandwidth, colocation at Google facility |
| Partner Interconnect | 50 Mbps – 10 Gbps | 99.99% (via partner) | Not colocated, use service provider |
# Dedicated Interconnect — 4-circuit redundancy for 99.99% SLA
# 2 metro locations × 2 circuits each
gcloud compute interconnects create prod-interconnect-1 \
--interconnect-type=DEDICATED \
--link-type=LINK_TYPE_ETHERNET_10G_LR \
--requested-link-count=2 \
--location=equinix-sv1 \
--admin-enabled
# VLAN attachments connect Interconnect to Cloud Router
gcloud compute interconnects attachments dedicated create vlan-attach-1 \
--interconnect=prod-interconnect-1 \
--router=prod-interconnect-router \
--region=us-central1 \
--vlan=100 \
--bandwidth=BPS_10G \
--candidate-subnets=169.254.0.0/29
gcloud compute interconnects attachments dedicated create vlan-attach-2 \
--interconnect=prod-interconnect-2 \
--router=prod-interconnect-router \
--region=us-central1 \
--vlan=101 \
--bandwidth=BPS_10G \
--candidate-subnets=169.254.0.8/29
# Check Interconnect operational status
gcloud compute interconnects describe prod-interconnect-1 \
--format='value(operationalStatus,circuitInfos)'
Connectivity Comparison
| Factor | VPN (IPsec) | Direct Connect / Cloud Interconnect |
|---|---|---|
| Setup time | Minutes to hours | Weeks to months (physical provisioning) |
| Bandwidth | Up to ~1.25 Gbps per tunnel | Up to 100 Gbps per circuit |
| Latency | Variable (public internet path + encryption overhead) | Consistent low latency (dedicated path) |
| Reliability | Depends on internet | SLA-backed 99.9–99.99% |
| Cost | Low (hourly + data transfer) | High (port + VLAN + data transfer) |
| Encryption | Built-in (IPsec) | Not encrypted by default — add MACsec or IPsec over DX |
Hybrid Connectivity Patterns
Pattern 1: Primary DX + VPN Backup
Most common pattern. Direct Connect as primary path (high bandwidth, low latency). VPN over internet as automatic failover. BGP preferred over DX via LOCAL_PREF; VPN routes learned with lower LOCAL_PREF. When DX fails, BGP converges to VPN within seconds.
# BGP config for DX primary + VPN backup
# On your on-prem router:
# DX BGP neighbor — high LOCAL_PREF
route-map DX_INBOUND permit 10
set local-preference 200
!
neighbor 169.254.100.1 route-map DX_INBOUND in
# VPN BGP neighbor — lower LOCAL_PREF (backup)
route-map VPN_INBOUND permit 10
set local-preference 100 # lower = less preferred
!
neighbor 169.254.200.1 route-map VPN_INBOUND in
Pattern 2: Dual Direct Connect (Maximum Redundancy)
Two separate DX connections at two different DX locations (different physical facilities). AWS recommends for mission-critical workloads. Can also add VPN as tertiary backup. Use AWS Direct Connect SiteLink for inter-region DX routing.
Network Security
AWS Network Firewall
# AWS Network Firewall: stateful L3-L7 inspection, deployed in VPC subnets
# Uses Suricata-compatible rules for IPS/IDS capabilities
resource "aws_networkfirewall_firewall" "prod" {
name = "prod-network-firewall"
firewall_policy_arn = aws_networkfirewall_firewall_policy.prod.arn
vpc_id = aws_vpc.prod.id
subnet_mapping {
subnet_id = aws_subnet.firewall[0].id
}
subnet_mapping {
subnet_id = aws_subnet.firewall[1].id
}
}
resource "aws_networkfirewall_rule_group" "stateful_rules" {
capacity = 100
name = "prod-stateful-rules"
type = "STATEFUL"
rule_group {
rules_source {
rules_string = <<-EOT
# Block known malicious domains
drop dns $HOME_NET any -> any 53 (msg:"Block malware domain"; dns.query; content:"malware-c2.example.com"; nocase; sid:1000001; rev:1;)
# Allow HTTPS to known good destinations only
pass tls $HOME_NET any -> $EXTERNAL_NET 443 (msg:"Allow HTTPS"; tls.sni; content:"api.partner.com"; nocase; sid:1000002; rev:1;)
# Block SSH to internet
drop tcp $HOME_NET any -> $EXTERNAL_NET 22 (msg:"Block outbound SSH"; flow:to_server,established; sid:1000003; rev:1;)
EOT
}
stateful_rule_options {
rule_order = "STRICT_ORDER"
}
}
}
GCP Cloud Armor
# Cloud Armor: WAF + DDoS protection, integrated with Global HTTPS LB
resource "google_compute_security_policy" "waf" {
name = "prod-waf-policy"
# OWASP Top 10 pre-configured rules
rule {
action = "deny(403)"
priority = 1000
match {
expr {
expression = "evaluatePreconfiguredExpr('xss-v33-stable')"
}
}
description = "Block XSS attacks"
}
rule {
action = "deny(403)"
priority = 1001
match {
expr {
expression = "evaluatePreconfiguredExpr('sqli-v33-stable')"
}
}
description = "Block SQL injection"
}
# Rate limiting — 1000 req/min per IP
rule {
action = "throttle"
priority = 2000
match {
versioned_expr = "SRC_IPS_V1"
config { src_ip_ranges = ["*"] }
}
rate_limit_options {
rate_limit_threshold {
count = 1000
interval_sec = 60
}
conform_action = "allow"
exceed_action = "deny(429)"
enforce_on_key = "IP"
}
}
# Geo-blocking — block specific countries
rule {
action = "deny(403)"
priority = 3000
match {
expr {
expression = "origin.region_code == 'KP' || origin.region_code == 'IR'"
}
}
description = "Block specific country codes"
}
# Default allow rule (required)
rule {
action = "allow"
priority = 2147483647
match {
versioned_expr = "SRC_IPS_V1"
config { src_ip_ranges = ["*"] }
}
description = "Default allow"
}
# Adaptive Protection (ML-based DDoS detection)
adaptive_protection_config {
layer_7_ddos_defense_config {
enable = true
rule_visibility = "STANDARD"
}
}
}
VPC Flow Logs & Traffic Monitoring
# AWS VPC Flow Logs — capture metadata for all traffic (not payload)
# Fields: version, account-id, interface-id, srcaddr, dstaddr, srcport, dstport,
# protocol, packets, bytes, start, end, action (ACCEPT/REJECT), log-status
resource "aws_flow_log" "vpc" {
vpc_id = aws_vpc.prod.id
traffic_type = "ALL"
iam_role_arn = aws_iam_role.flow_log.arn
log_destination = aws_cloudwatch_log_group.flow.arn
# Custom format for better analysis
log_format = "$${version} $${account-id} $${interface-id} $${srcaddr} $${dstaddr} $${srcport} $${dstport} $${protocol} $${packets} $${bytes} $${start} $${end} $${action} $${flow-direction} $${traffic-path}"
}
# Athena query for rejected traffic analysis
# SELECT srcaddr, dstaddr, dstport, COUNT(*) as count
# FROM vpc_flow_logs
# WHERE action = 'REJECT'
# AND year = '2026' AND month = '03'
# GROUP BY srcaddr, dstaddr, dstport
# ORDER BY count DESC
# LIMIT 50;
# GCP VPC Flow Logs
resource "google_compute_subnetwork" "app" {
name = "app-us-central1"
ip_cidr_range = "10.0.0.0/20"
region = "us-central1"
network = google_compute_network.prod.id
log_config {
aggregation_interval = "INTERVAL_5_SEC"
flow_sampling = 0.5 # sample 50% of flows (1.0 = all)
metadata = "INCLUDE_ALL_METADATA"
}
}
# AWS Traffic Mirroring (full packet capture to inspection appliance)
resource "aws_ec2_traffic_mirror_session" "prod" {
description = "Mirror web tier traffic for IDS"
network_interface_id = aws_instance.web.primary_network_interface_id
traffic_mirror_filter_id = aws_ec2_traffic_mirror_filter.web.id
traffic_mirror_target_id = aws_ec2_traffic_mirror_target.ids.id
session_number = 1
virtual_network_id = 7777 # VXLAN VNI to identify session
}
- Always deploy HA VPN (GCP) or dual-tunnel VPN (AWS) — single tunnel = single point of failure
- Use BGP over static routing for automatic failover and route propagation
- Set BFD (Bidirectional Forwarding Detection) for sub-second failure detection on DX/Interconnect
- Encrypt Direct Connect / Cloud Interconnect traffic with MACsec or IPsec overlay
- Implement RPKI ROA for your public prefixes to prevent route hijacking
- Filter BGP prefixes with max-prefix limits to protect against route leaks
- Enable VPC Flow Logs on all subnets — essential for security investigation and traffic analysis
- Deploy Network Firewall (AWS) or Cloud Armor (GCP) for east-west and north-south inspection