# ============================================================================= # CLOUDFLARE : Traffic Policies : Firewall Policies : Network # ============================================================================= #========================================================== # Local Variables #========================================================== locals { precedence = { # NETWORK (L4) Policies - Access Infrastructure Integration access_infra_target = 4000 # Access Infrastructure integration (between DNS groups) # NETWORK (L4) Policies - Zero Trust RDP Access Control rdp_admin_allow = 21000 # Allow RDP for IT admins (identity-based) rdp_default_deny = 21700 # Default deny RDP (after allow, before lateral movement) # NETWORK (L4) Policies - Lateral Movement Prevention (East-West Traffic) block_lateral_ssh = 22550 # Block SSH lateral movement block_lateral_rdp = 22600 # Block RDP lateral movement block_lateral_smb = 22200 # Block SMB lateral movement block_lateral_winrm = 22300 # Block WinRM lateral movement block_lateral_database = 22400 # Block database lateral movement # NETWORK (L4) Policies - IP Access Control ip_access_block = 23000 # Block direct IP access to apps (force hostname-based access) } # Common rule settings for block policies default_block_settings_network = { block_page_enabled = false ip_categories = false ip_indicator_feeds = false insecure_disable_dnssec_validation = false } # Gateway policies configuration # Organized by policy type: NETWORK (L4) policies first # Following Cloudflare best practices with 1000-spacing between major groups # Integrates with dashboard-managed policies at precedence: 1000-3000, 5000-20000, 36000-40000 gateway_policies = { #========================================================== # NETWORK (L4) POLICIES # Port/Protocol/IP-based filtering evaluated before HTTP policies # Terraform precedence ranges: 4000, 21000-23000 # Dashboard precedence ranges: 1000-3000, 36000-37000 #========================================================== # Access Infrastructure Integration (Precedence: 4000) access_infra_target = { name = "NETWORK - Allow: Cible en matière d'infrastructures d'accès" description = "Évaluer les applications d'accès avant ou après des stratégies de passerelle spécifiques" enabled = true action = "allow" precedence = local.precedence.access_infra_target filters = ["l4"] traffic = "access.target" notification_enabled = false } # Zero Trust RDP Access Control (Precedence: 21000) rdp_admin_access = { name = "NETWORK - Allow: RDP - Accès administrateur informatique" description = "Autoriser l'accès RDP aux administrateurs informatiques avec vérification de l'identité et de la posture des appareils" enabled = true action = "allow" precedence = local.precedence.rdp_admin_allow filters = ["l4"] traffic = "net.dst.ip == ${var.gcp_windows_vm_internal_ip} and net.dst.port == ${var.cloudflare_domain_controller_rdp_port} and net.protocol == \"tcp\"" identity = "any(identity.saml_attributes[*] == \"groups=${var.okta_itadmin_saml_group_name}\") or any(identity.saml_attributes[*] == \"groups=${var.okta_infra_admin_saml_group_name}\")" device_posture = "any(device_posture.checks.passed[*] == \"${var.cloudflare_macos_posture_id}\") or any(device_posture.checks.passed[*] == \"${var.cloudflare_windows_posture_id}\") or any(device_posture.checks.passed[*] == \"${var.cloudflare_linux_posture_id}\")" notification_enabled = false } # Lateral Movement Prevention - East-West Traffic (Precedence: 22000-22400) block_lateral_ssh = { name = "NETWORK - Block: Mouvement latéral SSH" description = "Bloquez les connexions SSH entre les machines virtuelles internes pour empêcher les mouvements latéraux, tout en autorisant les connexions SSH directes depuis les clients WARP." enabled = true action = "block" precedence = local.precedence.block_lateral_ssh filters = ["l4"] traffic = "net.dst.port == 22 and net.protocol == \"tcp\" and (net.dst.ip in {${var.aws_private_cidr} ${var.gcp_infra_cidr} ${var.gcp_windows_rdp_cidr} ${var.gcp_warp_cidr} ${var.azure_subnet_cidr}}) and (net.src.ip in {${var.aws_private_cidr} ${var.gcp_infra_cidr} ${var.gcp_windows_rdp_cidr} ${var.gcp_warp_cidr} ${var.azure_subnet_cidr}}) and not (net.src.ip in {${var.cloudflare_warp_cgnat_cidr}})" block_reason = "SSH lateral movement blocked - use authorized access methods or ensure device compliance" notification_enabled = true } block_lateral_rdp = { name = "NETWORK - Block: Mouvement latéral RDP" description = "Bloquez les connexions RDP entre les machines virtuelles internes pour empêcher les mouvements latéraux, tout en autorisant les connexions RDP directes depuis les clients WARP." enabled = true action = "block" precedence = local.precedence.block_lateral_rdp filters = ["l4"] traffic = "net.dst.port == 3389 and net.protocol == \"tcp\" and (net.dst.ip in {${var.aws_private_cidr} ${var.gcp_infra_cidr} ${var.gcp_warp_cidr} ${var.azure_subnet_cidr}}) and (net.src.ip in {${var.aws_private_cidr} ${var.gcp_infra_cidr} ${var.gcp_windows_rdp_cidr} ${var.gcp_warp_cidr} ${var.azure_subnet_cidr}}) and not (net.src.ip in {${var.cloudflare_warp_cgnat_cidr}})" block_reason = "RDP lateral movement blocked - use authorized methods" notification_enabled = true } block_lateral_smb = { name = "NETWORK - Block: Mouvement latéral SMB" description = "Bloquez les connexions SMB/CIFS entre les machines virtuelles internes pour empêcher les mouvements latéraux, tout en autorisant les connexions SMB directes depuis les clients WARP." enabled = true action = "block" precedence = local.precedence.block_lateral_smb filters = ["l4"] traffic = "net.dst.port in {445 139} and net.protocol == \"tcp\" and (net.dst.ip in {${var.aws_private_cidr} ${var.gcp_infra_cidr} ${var.gcp_windows_rdp_cidr} ${var.gcp_warp_cidr} ${var.azure_subnet_cidr}}) and (net.src.ip in {${var.aws_private_cidr} ${var.gcp_infra_cidr} ${var.gcp_windows_rdp_cidr} ${var.gcp_warp_cidr} ${var.azure_subnet_cidr}}) and not (net.src.ip in {${var.cloudflare_warp_cgnat_cidr}})" block_reason = "SMB lateral movement blocked - use authorized methods" notification_enabled = true } block_lateral_winrm = { name = "NETWORK - Block: Mouvement latéral WinRM" description = "Bloquez les connexions WinRM entre les machines virtuelles internes pour empêcher les mouvements latéraux, tout en autorisant les connexions WinRM directes depuis les clients WARP." enabled = true action = "block" precedence = local.precedence.block_lateral_winrm filters = ["l4"] traffic = "net.dst.port in {5985 5986} and net.protocol == \"tcp\" and (net.dst.ip in {${var.aws_private_cidr} ${var.gcp_infra_cidr} ${var.gcp_windows_rdp_cidr} ${var.gcp_warp_cidr} ${var.azure_subnet_cidr}}) and (net.src.ip in {${var.aws_private_cidr} ${var.gcp_infra_cidr} ${var.gcp_windows_rdp_cidr} ${var.gcp_warp_cidr} ${var.azure_subnet_cidr}}) and not (net.src.ip in {${var.cloudflare_warp_cgnat_cidr}})" block_reason = "WinRM lateral movement blocked - use authorized methods" notification_enabled = true } block_lateral_database = { name = "NETWORK - Block: Mouvement latéral dans les bases de données" description = "Bloquer les connexions aux bases de données entre les machines virtuelles internes afin d'empêcher les mouvements latéraux, tout en autorisant l'accès direct à la base de données depuis les clients WARP." enabled = true action = "block" precedence = local.precedence.block_lateral_database filters = ["l4"] traffic = "net.dst.port in {3306 5432 1433 1521 27017} and net.protocol == \"tcp\" and (net.dst.ip in {${var.aws_private_cidr} ${var.gcp_infra_cidr} ${var.gcp_windows_rdp_cidr} ${var.gcp_warp_cidr} ${var.azure_subnet_cidr}}) and (net.src.ip in {${var.aws_private_cidr} ${var.gcp_infra_cidr} ${var.gcp_windows_rdp_cidr} ${var.gcp_warp_cidr} ${var.azure_subnet_cidr}}) and not (net.src.ip in {${var.cloudflare_warp_cgnat_cidr}})" block_reason = "Database lateral movement blocked - use authorized methods" notification_enabled = true } # IP-based Access Control (Precedence: 23000) block_ip_access = { name = "NETWORK - Block : Accéder aux applications GCP via une adresse IP privée" description = "Cette règle bloque l'accès à l'application Compétition et à l'application Administration via l'adresse IP et le port." enabled = true action = "block" precedence = local.precedence.ip_access_block filters = ["l4"] traffic = "(net.dst.ip == ${var.gcp_vm_internal_ip} and net.dst.port == ${var.cloudflare_intranet_app_port}) or (net.dst.ip == ${var.gcp_vm_internal_ip} and net.dst.port == ${var.cloudflare_competition_app_port})" block_reason = "This website is blocked because you are trying to access an internal app via its IP address" notification_enabled = true } # Default Deny - Evaluated Last (Precedence: 21700) rdp_default_deny = { name = "NETWORK - Block : Deny par défaut" description = "Refuser l'accès RDP aux utilisateurs sans privilèges d'administrateur informatique (évalué après la politique d'autorisation)" enabled = true action = "block" precedence = local.precedence.rdp_default_deny filters = ["l4"] traffic = "net.dst.ip == ${var.gcp_windows_vm_internal_ip} and net.dst.port == ${var.cloudflare_domain_controller_rdp_port} and net.protocol == \"tcp\"" block_reason = "RDP access denied - insufficient privileges" notification_enabled = true } } } #========================================================== # Gateway Policies #========================================================== resource "cloudflare_zero_trust_gateway_policy" "policies" { for_each = local.gateway_policies account_id = local.cloudflare_account_id name = each.value.name description = each.value.description enabled = each.value.enabled action = each.value.action precedence = each.value.precedence filters = each.value.filters traffic = each.value.traffic # Optional fields identity = try(each.value.identity, null) device_posture = try(each.value.device_posture, null) rule_settings = merge( local.default_block_settings_network, { block_reason = try(each.value.block_reason, "") notification_settings = { enabled = try(each.value.notification_enabled, false) msg = try(each.value.block_reason, "") } redirect = try(each.value.redirect_url, null) != null ? { target_uri = each.value.redirect_url preserve_path_and_query = false include_context = false } : null gen_ai_prompt_log = try(each.value.gen_ai_prompt_log, null) != null ? { enabled = each.value.gen_ai_prompt_log } : null } ) }