- Allow VPN IPSec port 500, 4500, and protocol ESP access to specific IP addresses only
- Allow only to specific BGP peers to connect to the port 179 TCP
- SSL VPN - limit access to the port 10443 to a specific country, Israel in this example
- Deny all services from all IP addresses
- Limit management port access to specific IPs
- Verification and Debug
- Additional resources
Local-in policy is the policy guarding/protecting the Fortigate itself, i.e. it filters/restricts access when the destination is one of the Fortigate interfaces and its IPs. Below you will find example configurations, but before jumping in, you have to know few important facts about Local-in policy:
It is visible in the GUI by default starting with FortiOS 7.x, but in older versions you have to go to System → Feature Visibility → Local-in Policy to make it so.
The Local-in policy can only be configured in CLI, the GUI display is read-only.
Additionally, the GUI displays only default rules, created automatically by the Fortigate when you enable appropriate services. GUI will not show any rules you configure on CLI, and thus may confuse you into thinking CLI-configured rules do not work. My advice: forget about GUI, work on CLI from the beginning.
You have separate, ipv4 and ipv6, local-in policies.
The default action in rules is deny, so when you see no action in the show output, it means the action is to deny.
You cannot disable/delete/manipulate the auto-created by Fortigate rules any other way but by disabling/deleting services that opened them up. The custom rules we create on CLI override (go above) the default rules, but do not remove them. This means you have to take them into account. E.g., once you configure BGP on the Fortigate, this will open port 179 TCP to ALL, so to restrict BGP port to specific IPs, you will need to create 2 rules: 1st with action accept and use those specific IPs, then 2nd rule below, that denies ALL to port 179 TCP of BGP. This way, the default auto-created rule port 179 TCP - allow ALL will not be reached when matching the traffic.
When configuring on CLI, you must specify: incoming interface to protect, source and destination address (you can use all), schedule, and service (you can use specific or ALL).
In newer FortiOS versions, if I remember correctly, 6.4.9 or newer, we can set as a source address the Geography (Geolocation) object, allowing/blocking this way access by the country.
Local-in policy does NOT control NAT/port-forwarded rules, aka Virtual IPs (VIPs). This means, for example, if you configured a port-forwarding VIP allowing some specific port or a one-to-one NAT in Security Rules, no matter what you do in Local-in policy for the same IPs, the Fortigate will only look at Security Rules, ignoring Local-in. In short - VIPs override Local-in policies.
By default, Local-in policy hits are not logged, you have to set in Log Settings → Log All for denied packets to be logged. The logs are in Local Traffic section.
You can use Workspace Mode to prevent mistakenly locking out yourself when changing Local-in policy, see Resources at the end of the post.
Allow VPN IPSec port 500, 4500, and protocol ESP access to specific IP addresses only
Task: We set up VPN site to site with the remote peer of 188.8.131.52 and this opened port 500 (IKE), port 4500 (NAT-T), and protocol ESP to all IPs on the Internet. Let’s limit it to the 184.108.40.206 only.
Create a firewall address object (if not already) for the remote peer:
config firewall address edit "VPNpeer1" set comment "Remote peer for VPN" set subnet 220.127.116.11 255.255.255.255 next end
Create rule to allow IKE and ESP from this peer on port1 (WAN interface):
config firewall local-in-policy edit 0 set intf "port1" set srcaddr "VPNpeer1" set dstaddr "all" set action accept set service "IKE" "ESP" //"IKE" service includes ports 500 and 4500 set schedule "always" next end
Create rule below to deny IKE and ESP protocols to everyone else:
config firewall local-in-policy edit 0 set intf "port1" set srcaddr "all" set dstaddr "all" set service "IKE" "ESP" set schedule "always" next end
Done. Now, this Fortigate will only answer to this peer (18.104.22.168) on port 500 UDP (for IKE), port 4500 for NAT Traversal, and to protocol ESP (Phase 2 VPN).
Allow only to specific BGP peers to connect to the port 179 TCP
Strictly speaking, by BGP protocol standard, it is enough for just one peer to listen for incoming BGP connections on port 179 TCP. So, even if we block incoming port 179 altogether, the BGP session would still be established if the remote BGP peer has port 179 open and listening. But let’s not go overboard.
Task: After creating BGP configuration between Fortigate and the remote peer of 22.214.171.124, you noticed that port 179 TCP on the Fortigate answers to connections from any IP. Not good, let’s limit port 179 just to the BGP peer.
Create firewall address object for the remote BGP peer (if not already):
config firewall address edit BGPpeer126.96.36.199 set subnet 188.8.131.52/32 set comment "Remote BGP peer address" end
Create Local-in rule to allow this peer connection to our port 179:
config firewall local-in-policy edit 0 set intf "port1" set srcaddr "BGPpeer184.108.40.206" set dstaddr "all" set action accept set service "BGP" set schedule "always" next end
Create a rule below, that block all IPs to port 179 on the Fortigate:
config firewall local-in-policy edit 0 set intf "port1" set srcaddr "all" set dstaddr "all" set service "BGP" set schedule "always" next end
SSL VPN - limit access to the port 10443 to a specific country, Israel in this example
Fortigates have suffered a bunch of remotely exploitable vulnerabilities in their SSL VPN service. And while not securing against that, restricting access to VPN SSL to the country where the Fortigate and VPN clients are located will set up another hurdle on the attackers' path.
|Starting with Fortios 7.2 it is no longer necessary to use Local-in policy for that because VPN SSL Settings accept Geo object as source address to limit the access. For versions before 7.2, it is still doable only in Local-in policy.|
Create Geo address representing Israel IPs:
config firewall address edit "ILgeoIPs" set type geography set comment "All Israel IPs" set country "IL" next end
Use it in a Local-in policy for port 10443 (or any other set for VPN SSL)
config firewall local-in-policy edit 0 set intf "port1" set srcaddr "ILgeoIPs" set dstaddr "all" set service "Custom_10443" <-- Custom service, created earlier, not shown set schedule "always" set action accept next end
Finally, deny access to all the rest:
config firewall local-in-policy edit 0 set intf "port1" set srcaddr all set dstaddr "all" set service "Custom_10443" <-- Custom service, created earlier, not shown set schedule "always" next end
Deny all services from all IP addresses
|Do this only after you have identified all services you need to be exposed to the outside on the given interface, especially management if any, and created rules in Local in Policy allowing them.|
config firewall local-in-policy edit 0 set intf "port1" set srcaddr "all" set dstaddr "all" set action deny set service "ALL" set schedule "always" next
Limit management port access to specific IPs
Here, I actually would recommend against using Local-in policy for that.
Fortigate already has a built-feature
The risk is great - Local-in rules are not visible in GUI, IP addresses change frequently, and it is easy to forget to change such a rule with the result being locked out of the Fortigate altogether. The chance of having to use console to get access back is substantial.
You can create a Loopback interface and enable management protocols just there. This way, you will have to create an explicit Security rule that will be prominent, which will also log all management access by default.
If you still decide to do so - configure rule as in other cases. The Local-in policy overrides the Trusted Host settings for admin users.
Verification and Debug
Show configured Local-in policies: show firewall local-in-policy
show firewall local-in-policy config firewall local-in-policy edit 1 set uuid 140e2800-ea2d-51ec-838f-8ecf16d58d4e set intf "port1" set srcaddr "VPNpeer1" set dstaddr "all" set action accept set service "IKE" "ESP" set schedule "always" next edit 2 set uuid cfcaa5f0-ea2d-51ec-bc4a-56cacfb950b4 set intf "port1" set srcaddr "all" set dstaddr "all" set service "IKE" "ESP" set schedule "always" next edit 3 set uuid d89f54b6-ea30-51ec-9646-909a1610e650 set intf "port1" set srcaddr "BGPpeer220.127.116.11" set dstaddr "all" set action accept set service "BGP" set schedule "always" next edit 4 set uuid 0f9275d4-ea31-51ec-5c9b-271138ae6f3d set intf "port1" set srcaddr "all" set dstaddr "all" set service "BGP" set schedule "always" next end
Show policy hit count: diag firewall iprope show 00100001 policy-id
Let’s see how many times incoming BGP connections were blocked on rule 4 above:
diag firewall iprope show 00100001 4 idx:4 pkts:30 (30 0 0 0 0 0 0 0) bytes:1800 (1800 0 0 0 0 0 0 0) asic_pkts:0 (0 0 0 0 0 0 0 0) asic_bytes:0 (0 0 0 0 0 0 0 0) flag:0x0 hit count:30 (30 0 0 0 0 0 0 0) first hit:2022-06-12 04:21:01 last hit:2022-06-12 04:25:35
We can see that 30 packets have been blocked incoming on port 179 so far. Note: hit count statistics on Local-in rules are available starting with 7.0 only.
|The key here is to use 00100001 as the table index for Local-in policy.|
Usual and proven: diagnose debug flow
Let’s see how the Fortigate blocks BGP incoming connection in real-time.
dia debug flow filter port 179 dia debug flow show function diagnose debug enable dia debug flow trace start # id=65308 trace_id=1 func=print_pkt_detail line=5895 msg="vd-root:0 received a packet(proto=6, 10.10.10.218:23538->10.10.10.111:179) tun_id=0.0.0.0 from port1. flag [S], seq 2606637155, ack 0, win 65535" id=65308 trace_id=1 func=init_ip_session_common line=6076 msg="allocate a new session-00003239, tun_id=0.0.0.0" id=65308 trace_id=1 func=vf_ip_route_input_common line=2605 msg="find a route: flag=84000000 gw-10.10.10.111 via root" id=65308 trace_id=1 func=fw_local_in_handler line=522 msg="iprope_in_check() check failed on policy 4, drop"
Follow me on https://www.linkedin.com/in/yurislobodyanyuk/ not to miss what I publish on Linkedin, Github, blog, and more.