# Networking Module for VM Deployment (Phase 1) # Creates VNet, subnets, NSGs for VM-based deployment # Virtual Network resource "azurerm_virtual_network" "main" { name = "${var.cluster_name}-vnet" address_space = [var.vnet_address_space] location = var.location resource_group_name = var.resource_group_name tags = merge(var.tags, { Purpose = "Networking" }) } # Subnet for VMs resource "azurerm_subnet" "vm" { name = "${var.cluster_name}-vm-subnet" resource_group_name = var.resource_group_name virtual_network_name = azurerm_virtual_network.main.name address_prefixes = [var.subnet_address_prefix] service_endpoints = ["Microsoft.Storage", "Microsoft.KeyVault"] } # Network Security Group for VMs resource "azurerm_network_security_group" "vm" { name = "${var.cluster_name}-vm-nsg" location = var.location resource_group_name = var.resource_group_name # Allow SSH (restrict to specific IPs in production) security_rule { name = "AllowSSH" priority = 1000 direction = "Inbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "22" source_address_prefix = length(var.allowed_ssh_ips) > 0 ? null : "*" source_address_prefixes = length(var.allowed_ssh_ips) > 0 ? var.allowed_ssh_ips : null destination_address_prefix = "*" description = length(var.allowed_ssh_ips) > 0 ? "Allow SSH access from specified IPs" : "Allow SSH access from anywhere (WARNING: Not secure for production)" } # Allow P2P (Besu) - TCP (only for Besu nodes, not Nginx proxy) dynamic "security_rule" { for_each = var.enable_besu_rules ? [1] : [] content { name = "AllowP2PTCP" priority = 1001 direction = "Inbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "30303" source_address_prefix = length(var.allowed_p2p_ips) > 0 ? null : "*" source_address_prefixes = length(var.allowed_p2p_ips) > 0 ? var.allowed_p2p_ips : null destination_address_prefix = "*" description = length(var.allowed_p2p_ips) > 0 ? "Allow Besu P2P TCP from specified IPs" : "Allow Besu P2P TCP from anywhere (WARNING: Not secure for production)" } } # Allow P2P (Besu) - UDP (only for Besu nodes, not Nginx proxy) dynamic "security_rule" { for_each = var.enable_besu_rules ? [1] : [] content { name = "AllowP2PUDP" priority = 1002 direction = "Inbound" access = "Allow" protocol = "Udp" source_port_range = "*" destination_port_range = "30303" source_address_prefix = length(var.allowed_p2p_ips) > 0 ? null : "*" source_address_prefixes = length(var.allowed_p2p_ips) > 0 ? var.allowed_p2p_ips : null destination_address_prefix = "*" description = length(var.allowed_p2p_ips) > 0 ? "Allow Besu P2P UDP from specified IPs" : "Allow Besu P2P UDP from anywhere (WARNING: Not secure for production)" } } # Allow RPC HTTP (from Nginx proxy via VPN/ExpressRoute) # NOTE: Backend VMs use private IPs only. Nginx proxy connects via VPN/ExpressRoute. # Restrict to Nginx proxy private IP subnet once VPN is deployed. # Only for Besu nodes, not Nginx proxy dynamic "security_rule" { for_each = var.enable_besu_rules ? [1] : [] content { name = "AllowRPCHTTP" priority = 1003 direction = "Inbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "8545" source_address_prefix = length(var.allowed_rpc_ips) > 0 ? null : "*" source_address_prefixes = length(var.allowed_rpc_ips) > 0 ? var.allowed_rpc_ips : null destination_address_prefix = "*" description = length(var.allowed_rpc_ips) > 0 ? "Allow RPC HTTP from specified IPs (Nginx proxy subnet or Cloudflare Tunnel IPs)" : "Allow RPC HTTP from anywhere (WARNING: Not secure for production)" } } # Allow RPC WebSocket (from Nginx proxy via VPN/ExpressRoute) # NOTE: Backend VMs use private IPs only. Nginx proxy connects via VPN/ExpressRoute. # Restrict to Nginx proxy private IP subnet once VPN is deployed. # Only for Besu nodes, not Nginx proxy dynamic "security_rule" { for_each = var.enable_besu_rules ? [1] : [] content { name = "AllowRPCWS" priority = 1004 direction = "Inbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "8546" source_address_prefix = length(var.allowed_rpc_ips) > 0 ? null : "*" source_address_prefixes = length(var.allowed_rpc_ips) > 0 ? var.allowed_rpc_ips : null destination_address_prefix = "*" description = length(var.allowed_rpc_ips) > 0 ? "Allow RPC WebSocket from specified IPs (Nginx proxy subnet or Cloudflare Tunnel IPs)" : "Allow RPC WebSocket from anywhere (WARNING: Not secure for production)" } } # Allow Metrics (only for Besu nodes, not Nginx proxy) dynamic "security_rule" { for_each = var.enable_besu_rules ? [1] : [] content { name = "AllowMetrics" priority = 1005 direction = "Inbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "9545" source_address_prefix = length(var.allowed_metrics_ips) > 0 ? null : "*" source_address_prefixes = length(var.allowed_metrics_ips) > 0 ? var.allowed_metrics_ips : null destination_address_prefix = "*" description = length(var.allowed_metrics_ips) > 0 ? "Allow Prometheus metrics from specified IPs" : "Allow Prometheus metrics from anywhere (WARNING: Not secure for production)" } } # Allow outbound internet access security_rule { name = "AllowOutboundInternet" priority = 2000 direction = "Outbound" access = "Allow" protocol = "*" source_port_range = "*" destination_port_range = "*" source_address_prefix = "*" destination_address_prefix = "*" description = "Allow outbound internet access" } tags = merge(var.tags, { Purpose = "Network-Security" }) } # Associate NSG with VM subnet (only if subnet_nsg_enabled is true) # For Nginx proxy subnet, we don't need subnet-level NSG (NIC-level NSG is sufficient) resource "azurerm_subnet_network_security_group_association" "vm" { count = var.subnet_nsg_enabled ? 1 : 0 subnet_id = azurerm_subnet.vm.id network_security_group_id = azurerm_network_security_group.vm.id } # Outputs output "vm_subnet_id" { value = azurerm_subnet.vm.id description = "Subnet ID for VMs" } output "vm_nsg_id" { value = azurerm_network_security_group.vm.id description = "Network Security Group ID for VMs" } output "vnet_id" { value = azurerm_virtual_network.main.id description = "Virtual Network ID" }