FortiGate Best Practices Part 1: Essential Firewall Objects for a Hardened Perimeter

The configurations outlined in this post are my personal recommendations for deploying and hardening FortiGate firewalls across various environments. They are particularly well suited for new firewall deployments where you have the opportunity to build a solid security foundation from day one, but they can also be retrofitted into existing environments with proper planning and change management. Every network is different, so I encourage you to evaluate each recommendation against your own requirements and risk tolerance before implementing.
This is Part 1 of a series covering the foundational firewall objects and policies I consider essential for any FortiGate deployment. None of these require expensive add-on licenses. They are disciplined, layered practices that dramatically reduce your attack surface when applied together.
1. Blocking Malicious ISDB Objects (LAN → WAN)
FortiGuard Labs, Fortinet's threat intelligence and research organization, maintains an Internet Service Database (ISDB) that categorizes well-known IP ranges and services, including Botnet C&C servers, cryptomining pools, malware distribution networks, and other known malicious destinations. This data is delivered to your FortiGate through FortiGuard subscription services and updated automatically, providing near-real-time threat intelligence directly within your firewall policy.
The concept is straightforward: create a deny policy at the top of your LAN-to-WAN policy chain that blocks traffic to these malicious ISDB categories before your general allow rules are evaluated.
CLI Configuration
config firewall policy
edit 0
set name "Block-Malicious-ISDB-LAN-to-WAN"
set srcintf "lan"
set dstintf "wan1"
set srcaddr "all"
set internet-service-dst enable
set internet-service-dst-name "Botnet-C&C" "Malware" "Phishing" "Spam" "Tor" "Cryptomining"
set action deny
set schedule "always"
set logtraffic all
set comments "Block outbound to known malicious ISDB categories"
next
end
GUI Steps
- Navigate to Policy & Objects → Firewall Policy
- Create a new policy with source interface set to your LAN and destination to WAN
- Under Destination, toggle Internet Service and select the malicious categories: Botnet-C&C, Malware, Phishing, Spam, Tor, Cryptomining
- Set the action to DENY and enable Log Violation Traffic
- Drag this policy above your general LAN-to-WAN allow rules
Tip: Browse the available categories under Policy & Objects → Internet Service Database for additional categories worth blocking in your environment. Note thatinternet-service-dstanddstaddrare mutually exclusive in the same policy.
2. Blocking Cloud Services and Malicious ISDB Objects on Your DMZ / Exposed Ports (WAN → DMZ)
If you have a DMZ or externally published services (web servers, mail relays, VPN portals), you should be filtering inbound traffic against ISDB objects as well. There is no legitimate reason for a known Botnet C&C address or a Tor exit node to be communicating with your public-facing infrastructure.
Additionally, consider blocking inbound connections from major cloud providers on your DMZ. Attackers frequently use inexpensive VPS instances on AWS, Azure, GCP, DigitalOcean, and OVH to launch attacks. If your DMZ services are only intended to serve end users or specific business partners, blocking these ranges can significantly reduce attack traffic.
CLI Configuration
config firewall policy
edit 0
set name "Block-Malicious-ISDB-WAN-to-DMZ"
set srcintf "wan1"
set dstintf "dmz"
set srcaddr "all"
set internet-service-src enable
set internet-service-src-name "Botnet-C&C" "Malware" "Phishing" "Spam" "Tor" "Cryptomining"
set action deny
set schedule "always"
set logtraffic all
set comments "Block inbound from known malicious sources to DMZ"
next
end
To also block cloud providers from reaching your DMZ:
config firewall policy
edit 0
set name "Block-Cloud-Providers-WAN-to-DMZ"
set srcintf "wan1"
set dstintf "dmz"
set srcaddr "all"
set internet-service-src enable
set internet-service-src-name "Amazon.Web.Services" "Microsoft-Azure" "Google-Cloud" "DigitalOcean" "OVHcloud"
set action deny
set schedule "always"
set logtraffic all
set comments "Block inbound from major cloud providers to DMZ"
next
end
Adding an External Malicious IP Feed for Inbound Blocking
ISDB objects provide excellent coverage, but they do not catch everything. I recommend supplementing them with community-maintained malicious IP lists via FortiGate's External Connector (Threat Feed) feature. One of the best maintained lists I have found is from Romain Marcoux's GitHub repository:
Feed URL: https://raw.githubusercontent.com/romainmarcoux/malicious-ip/refs/heads/main/full-aa.txt
Creating the External Threat Feed
CLI:
config system external-resource
edit "Malicious-IP-Feed-Inbound"
set type address
set resource "https://raw.githubusercontent.com/romainmarcoux/malicious-ip/refs/heads/main/full-aa.txt"
set refresh-rate 60
set comments "Romain Marcoux malicious IP aggregation feed"
next
end
GUI:
- Navigate to Security Fabric → External Connectors
- Click Create New and select IP Address under Threat Feeds
- Name it
Malicious-IP-Feed-Inbound, enter the URL above, and set the refresh rate to 60 minutes
The FortiGate will pull down the list and create a dynamic address object you can reference in any firewall policy.
Using the Feed in a Deny Policy
config firewall policy
edit 0
set name "Block-Malicious-Feed-WAN-to-DMZ"
set srcintf "wan1"
set dstintf "dmz"
set srcaddr "Malicious-IP-Feed-Inbound"
set dstaddr "all"
set schedule "always"
set action deny
set logtraffic all
set comments "Block inbound from external malicious IP threat feed"
next
end
Place this policy above your legitimate DMZ allow rules.
3. WAN → LAN Geo-Reverse Blocking (Allow US Only)
If your business only operates within the United States (or any specific set of countries), there is little reason to accept inbound connections from the entire planet. Geo-blocking will not stop a determined attacker using a US-based VPN, but it eliminates an enormous volume of opportunistic scanning, brute-force attempts, and automated exploit traffic.
I recommend a "reverse block" approach: create address objects for the countries you want to allow and deny everything else. Whitelist, not blacklist.
Create the Geography Address Object
config firewall address
edit "GEO-US-Only"
set type geography
set country "US"
next
end
Create the Reverse Block Policy
First create a policy that allows traffic from your whitelisted countries, then a catch-all deny below it.
config firewall policy
edit 0
set name "Allow-US-Inbound"
set srcintf "wan1"
set dstintf "lan"
set srcaddr "GEO-US-Only"
set dstaddr "your-server-vip"
set action accept
set schedule "always"
set logtraffic all
set comments "Allow inbound from US only"
next
edit 0
set name "Deny-All-Other-GEO-Inbound"
set srcintf "wan1"
set dstintf "lan"
set srcaddr "all"
set dstaddr "your-server-vip"
set action deny
set schedule "always"
set logtraffic all
set comments "Deny all non-US inbound traffic"
next
end
Important: Ensure the allow-US policy sits above the deny-all policy. Policy order matters.
4. Monitor and Block Outbound Traffic to Malicious Destinations (LAN → WAN IoC Detection)
Section 1 covered blocking known-bad ISDB destinations outbound, but ISDB will not cover every malicious IP. To fill that gap, we can leverage another external threat feed curated specifically for outbound connections to malicious infrastructure.
Romain Marcoux maintains a separate list for this purpose:
Feed URL: https://raw.githubusercontent.com/romainmarcoux/malicious-outgoing-ip/refs/heads/main/full-outgoing-ip-aa.txt
If a host on your LAN is attempting to communicate outbound to an IP on this list, that is a potential indicator of compromise (IoC). It could be a machine phoning home to a C2 server, participating in a botnet, or exfiltrating data.
Creating the Outbound Malicious IP Feed
config system external-resource
edit "Malicious-Outgoing-IP-Feed"
set type address
set resource "https://raw.githubusercontent.com/romainmarcoux/malicious-outgoing-ip/refs/heads/main/full-outgoing-ip-aa.txt"
set refresh-rate 60
set comments "Malicious outgoing IP feed - IoC detection for outbound traffic"
next
end
Creating the Block and Alert Policy
config firewall policy
edit 0
set name "Block-Outbound-Malicious-IoC"
set srcintf "lan"
set dstintf "wan1"
set srcaddr "all"
set dstaddr "Malicious-Outgoing-IP-Feed"
set schedule "always"
set action deny
set logtraffic all
set comments "Block and log outbound to known malicious IPs - IoC indicator"
next
end
Place this above your general outbound allow policies. When this policy generates logs, every hit warrants investigation. I recommend forwarding these logs to your SIEM (FortiAnalyzer, Splunk, Elastic, etc.) with a high severity tag and creating a correlation rule that triggers on this policy ID. This serves as your early warning system for compromised endpoints.
5. Block Traffic to Malicious Countries
Beyond blocking individual malicious IPs, you can block outbound traffic to entire countries known for hosting disproportionate amounts of malicious infrastructure. This is not appropriate for every environment, but for networks with zero business reason to communicate with certain regions, it provides a pragmatic additional layer of defense.
Create Geography Objects and Group Them
config firewall address
edit "GEO-Russia"
set type geography
set country "RU"
next
edit "GEO-China"
set type geography
set country "CN"
next
edit "GEO-NorthKorea"
set type geography
set country "KP"
next
edit "GEO-Iran"
set type geography
set country "IR"
next
end
config firewall addrgrp
edit "GEO-Blocked-Countries-Outbound"
set member "GEO-Russia" "GEO-China" "GEO-NorthKorea" "GEO-Iran"
next
end
Create the Deny Policy
config firewall policy
edit 0
set name "Block-Outbound-Malicious-Countries"
set srcintf "lan"
set dstintf "wan1"
set srcaddr "all"
set dstaddr "GEO-Blocked-Countries-Outbound"
set schedule "always"
set action deny
set logtraffic all
set comments "Block outbound traffic to high-risk countries"
next
end
Caveat: Some legitimate SaaS services and CDNs have nodes in these countries. I recommend testing in monitor mode first (set the action to accept with full logging) and reviewing the traffic for a week or two before switching to deny.
6. Limiting Inbound Traffic by AS Number for Externally Exposed Services
If you have an externally published application that is only meant to be accessed by a specific organization (a partner portal, a B2B API, or a vendor integration endpoint), you can restrict inbound traffic to only the Autonomous System Numbers (ASNs) belonging to that organization.
What is an ASN?
An Autonomous System Number is a unique identifier assigned to a network or group of networks operating under a single administrative domain. Every ISP, major enterprise, cloud provider, and hosting company has one or more ASNs. When you know the ASN of the organization that needs to reach your service, you can look up all the IP prefixes they advertise via BGP and whitelist only those.
You can look up ASNs using tools such as https://bgp.he.net, https://www.peeringdb.com, or https://stat.ripe.net. For example, searching for Comcast on bgp.he.net will reveal AS7922 as one of their primary ASNs along with all their announced IP prefixes.
Configuring FortiGate with an ASN Prefix Feed
Once you have the list of prefixes for your target ASN in a plain text file (one CIDR per line) hosted on an accessible web server or repository, configure FortiGate to consume it as an external resource:
config system external-resource
edit "ASN-Partner-Whitelist"
set type address
set resource "https://your-server.example.com/feeds/asn-7922-prefixes.txt"
set refresh-rate 1440
set comments "Whitelisted IP prefixes for partner ASN 7922"
next
end
Creating the Restrictive Inbound Policy
config firewall policy
edit 0
set name "Allow-Partner-ASN-Only"
set srcintf "wan1"
set dstintf "dmz"
set srcaddr "ASN-Partner-Whitelist"
set dstaddr "partner-portal-vip"
set action accept
set schedule "always"
set service "HTTPS"
set logtraffic all
set comments "Allow inbound only from partner ASN prefixes"
next
edit 0
set name "Deny-All-Other-to-Partner-Portal"
set srcintf "wan1"
set dstintf "dmz"
set srcaddr "all"
set dstaddr "partner-portal-vip"
set action deny
set schedule "always"
set logtraffic all
set comments "Deny all other inbound to partner portal"
next
end
This significantly reduces your attack surface by making your service reachable only from the specific IP ranges that your partner's network announces via BGP.
7. FortiGate Local-In Policies
Regular firewall policies control traffic through the FortiGate (transiting from one interface to another). However, they do not control traffic destined to the FortiGate itself, such as management access (HTTPS, SSH), VPN services (IPsec, SSL-VPN), BGP sessions, FortiGuard subscription updates, and FGFM management traffic.
By default, FortiOS has an implicit local-in policy that allows traffic to any service enabled on an interface. If you have HTTPS management and SSL-VPN enabled on your WAN interface, the entire internet can attempt to reach those services. Local-in policies allow you to restrict who can communicate with the FortiGate itself on each interface.
Pre-FortiOS 7.4.4: Local-In Policies (CLI Only)
Prior to FortiOS 7.4.4, local-in policies were CLI-only. This is likely why many administrators never configured them.
Example: Restrict SSL-VPN Access on WAN to US Only
config firewall local-in-policy
edit 0
set intf "wan1"
set srcaddr "GEO-US-Only"
set dstaddr "all"
set action accept
set service "SSL-VPN"
set schedule "always"
set comments "Allow SSL-VPN from US only"
next
edit 0
set intf "wan1"
set srcaddr "all"
set dstaddr "all"
set action deny
set service "SSL-VPN"
set schedule "always"
set comments "Deny SSL-VPN from all other sources"
next
end
Example: Restrict Management Access to Internal Subnet
config firewall local-in-policy
edit 0
set intf "lan"
set srcaddr "Management-Subnet"
set dstaddr "all"
set action accept
set service "HTTPS" "SSH" "PING"
set schedule "always"
set comments "Allow management from admin subnet only"
next
edit 0
set intf "lan"
set srcaddr "all"
set dstaddr "all"
set action deny
set service "HTTPS" "SSH"
set schedule "always"
set comments "Deny management from all other LAN sources"
next
end
Important Notes for Pre-7.4.4
- Local-in policies are evaluated per-interface. Create separate policies for each interface you want to protect.
- There is an implicit allow at the bottom of the local-in policy table. If you add a specific allow rule, you must also add a corresponding deny rule below it, otherwise non-matching traffic will still be permitted.
- View the local-in policy table with:
show firewall local-in-policy - View hit counts with:
diagnose firewall local-in-policy list
FortiOS 7.4.4+ and 7.6: GUI Support and Enhancements
Starting with FortiOS 7.4.4, Fortinet introduced GUI visibility for local-in policies. Enable it with:
config system settings
set gui-local-in-policy enable
end
Once enabled, Local In Policy appears under Policy & Objects in the GUI with the same drag-and-drop interface used for regular firewall policies.
What Changed in 7.6
FortiOS 7.6 introduced a key improvement: default local-in deny behavior. You can now set the default local-in policy to deny instead of allow, effectively adopting a zero-trust model where you must explicitly allow every service that needs to reach the FortiGate.
config system settings
set gui-local-in-policy enable
set local-in-policy-default deny
end
Warning: Enabling local-in-policy-default deny without first creating the necessary allow rules will lock you out of management and break FortiGuard subscription updates, VPN services, and anything else that communicates directly with the FortiGate. At a minimum, ensure you have allow rules for: management access (HTTPS/SSH from your admin subnet), FortiGuard subscription updates, HA heartbeat (if applicable), VPN services, and NTP/DNS if the FortiGate is configured to use those.Recommended Local-In Policy Stack
On the WAN interface:
- Allow SSL-VPN from whitelisted GEO or specific address objects
- Allow IPsec/IKE from known peer IPs
- Allow FortiGuard subscription updates (destination FortiGuard ISDB objects)
- Allow FGFM from FortiManager IP (if centrally managed)
- Deny everything else
On the LAN interface:
- Allow management (HTTPS, SSH) from the admin VLAN/subnet only
- Allow DNS/NTP from internal servers if the FortiGate serves these roles
- Deny everything else
Wrapping Up
None of these configurations require expensive add-on licenses. External threat feeds use base FortiOS functionality, ISDB data is curated by FortiGuard Labs and delivered through a standard FortiGuard subscription, and geo-blocking and local-in policies are built into the platform. Combined, they create multiple overlapping layers of protection:
- ISDB blocking catches known categorized threats, both inbound and outbound
- External threat feeds fill the gaps with community-driven threat intelligence
- Geo-blocking eliminates entire regions of irrelevant traffic
- ASN whitelisting shrinks your attack surface to only the networks that should reach your services
- Outbound IoC monitoring provides early warning when a host may be compromised
- Local-in policies protect the firewall itself
In the upcoming parts, I will cover network segmentation, administration, firmware, App Control, Deep Inspection Policies and more.
If you found this post helpful or would like assistance implementing these practices in your environment, feel free to reach out.