This article gives a brief configuration example from one spoke to other spoke using IPsec, through the Hub firewall.
Note: Dial-up IPsec to IPsec communication is not possible.
SPOKE-1 -> IPSEC -> HUB -> IPSEC -> SPOKE-2
Assuming already Spokes to Hub VPNs are established.
FortiGate.
For IPsec configuration:
Go to VPN -> IPSec Tunnels and select the tunnel to edit.
For Address Objects configuration:
Go to Policy&Objects -> Addresses, select 'Create New' for new objects, or select the existing one by 'double clicking' to edit the existing one.
For Static Routes:
Go to Network -> Static Routes, select 'Create New' for new objects, or select the existing one by 'double clicking' to edit the existing one.
Spoke-1 Configuration:
Phase-1 which is already configured.
Phase-2 which is already configured for the Hub subnets:
In order to route the traffic for Spoke-2 via the tunnel to HUB, in the IPsec it will only be necessary to add the spoke-2 subnets in phase-2 as separate SA:
An address object for spoke-2 network has been created as below:
Select 'Add' in phase-2:
Select Spoke-1 address object in local address and Spoke-2 address object in remote address (it is also necessary to select type as IP address and give the networks directly as per the convenience).
Select the proposals accordingly and select 'OK'.
Once the IPSec phase-2 configuration is done, it will be necessary to configure the policy.
Policy (Before):
Edit policy to allow the traffic to Spoke-2 network over HUB tunnel:
The Spoke-2 network has been added to the policy, in the example it is 'SPOKE-2'.
Above policy is for outbound for Spoke-2 network, now it is necessary to edit the policy for inbound also as below:
For Inbound, incoming interface is HUB tunnel, outgoing interface is WAN2 (which is lan interface in the example).
In the source it is necessary to add the SPOKE-2 in addition to Hub network.
Add a static route for the SPOKE-2 network via HUB tunnel:
SPOKE-2 configuration:
Phase-1 configuration on the SPOKE-2 which is already configured:
Phase-2 configuration which already has the HUB network:
It is necessary to do the same as in the SPOKE-1, add the SPOKE-1 network in the phase-2 configuration.
An address object has been added with the name of the SPOKE-1 for spoke-1 network:
The same will be added in the phase-2 as below.
Select 'Add' in phase-2:
Select the proposals accordingly and select 'OK'.
Policy configuration:
Below is the policy already created to allow traffic from Spoke-2 LAN network to HUB.
Now add the spoke-1 network to the same policy as below:
Edit the policy for inbound traffic from Spoke-1 network:
For inbound from Spoke-1 network, Incoming Interface is HUB, Outgoing interface is WAN2 (which is local lan interface in the example).
In the source we add SPOKE-1 in addition to HUB network and destination remains local network. Select Rest UTM profiles accordingly.
Add route for SPOKE-1 network via HUB tunnel:
HUB Configuration.
Phase-1 for Spoke-1:
Phase-2 for Spoke-1:
Objects created for Spoke-1 and Spoke-2 as below which I will use in the Phase2:
It is necessary to create another phase-2 for Spoke-2 network in SPOKE-1 VPN, so HUB can accept the traffic for Spoke-2 network from SPOKE-1:
In the above example, it is possible to see we added SPOKE-2 as local on HUB and SPOKE-1 as remote in Phase-2 of VPN to Spoke-1.
This is because since HUB is in between, for Spoke-1 HUB becomes the one who should accept the traffic for Spoke-2.
It is necessary the same to do for Spoke-2 VPN
Phase-1 for Spoke-2 VPN:
Phase-2 for Spoke-2 VPN:
It is necessary to create another phase-2 for Spoke-1 network in SPOKE-2 VPN, so HUB can accept the traffic for Spoke-1 network from SPOKE-2:
In the above example, it is possible to see that SPOKE-1 has been added as local on HUB and SPOKE-2 as remote in Phase-2 of VPN to Spoke-2.
As mentioned above since HUB is in between, for Spoke-1 HUB becomes the one who should accept the traffic for Spoke-1.
Now the IPSec configuration is done, it is necessary to configure the policy.
Unlike the spokes, in the HUB we need to create a seperate policy from tunnel interfaces to tunnel interfaces. As below.
To allow traffic from Spoke-1 to Spoke-2:
Incoming interface will be Spoke-1 tunnel, outgoing interface is Spoke-2 tunnel.
Source will be Spoke-1 network and destination will be Spoke-2 network, rest UTM profiles and services have to be configured as per the requirement.
To allow traffic from Spoke-2 to Spoke-1:
Incoming interface will be Spoke-2 tunnel, outgoing interface is Spoke-1 tunnel.
Source will be Spoke-2 network and destination will be Spoke-1 network, rest UTM profiles and services have to be configured as per the requirement.
VERIFY.
SPOKE-1:
Spoke-1 # get router info routing-table details 10.68.6.173
Routing table for VRF=0
Routing entry for 10.68.6.0/24
Known via "static", distance 10, metric 0, best
* directly connected, HUB
Spoke-1 #
SPOKE-2:
Spoke-2 # get router info routing-table details 10.14.3.130
Routing table for VRF=0
Routing entry for 10.14.3.0/24
Known via "static", distance 10, metric 0, best
* directly connected, HUB
Spoke-2 #
Ping from spoke-1 to Spoke-2:
Spoke-1 # execute ping-options source 10.14.3.130
Spoke-1 # execute ping 10.68.6.173
PING 10.68.6.173 (10.68.6.173): 56 data bytes
64 bytes from 10.68.6.173: icmp_seq=0 ttl=254 time=1.2 ms
64 bytes from 10.68.6.173: icmp_seq=1 ttl=254 time=1.0 ms
64 bytes from 10.68.6.173: icmp_seq=2 ttl=254 time=0.9 ms
64 bytes from 10.68.6.173: icmp_seq=3 ttl=254 time=0.8 ms
64 bytes from 10.68.6.173: icmp_seq=4 ttl=254 time=0.8 ms
--- 10.68.6.173 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 0.8/0.9/1.2 ms
Spoke-1 #
Sniffer output from the HUB:
HUB # dia sniffer packet any 'host 10.14.3.130 and host 10.68.6.173' 4 0 a
interfaces=[any]
filters=[host 10.14.3.130 and host 10.68.6.173]
2022-09-08 04:42:06.859593 SPOKE-1 in 10.14.3.130 -> 10.68.6.173: icmp: echo request
2022-09-08 04:42:06.859704 SPOKE-2 out 10.14.3.130 -> 10.68.6.173: icmp: echo request
2022-09-08 04:42:06.860194 SPOKE-2 in 10.68.6.173 -> 10.14.3.130: icmp: echo reply
2022-09-08 04:42:06.860245 SPOKE-1 out 10.68.6.173 -> 10.14.3.130: icmp: echo reply
2022-09-08 04:42:07.878012 SPOKE-1 in 10.14.3.130 -> 10.68.6.173: icmp: echo request
2022-09-08 04:42:07.878059 SPOKE-2 out 10.14.3.130 -> 10.68.6.173: icmp: echo request
2022-09-08 04:42:07.878503 SPOKE-2 in 10.68.6.173 -> 10.14.3.130: icmp: echo reply
2022-09-08 04:42:07.878552 SPOKE-1 out 10.68.6.173 -> 10.14.3.130: icmp: echo reply
^C
8 packets received by filter
0 packets dropped by kernel
HUB #
Sniffer output from the Spoke-2:
Spoke-2 # dia sniffer packet any 'host 10.14.3.130 and host 10.68.6.173' 4 0 a
interfaces=[any]
filters=[host 10.14.3.130 and host 10.68.6.173]
2022-09-08 04:45:17.791935 HUB in 10.14.3.130 -> 10.68.6.173: icmp: echo request
2022-09-08 04:45:17.792004 HUB out 10.68.6.173 -> 10.14.3.130: icmp: echo reply
2022-09-08 04:45:18.809589 HUB in 10.14.3.130 -> 10.68.6.173: icmp: echo request
2022-09-08 04:45:18.809622 HUB out 10.68.6.173 -> 10.14.3.130: icmp: echo reply
2022-09-08 04:45:19.829650 HUB in 10.14.3.130 -> 10.68.6.173: icmp: echo request
2022-09-08 04:45:19.829691 HUB out 10.68.6.173 -> 10.14.3.130: icmp: echo reply
2022-09-08 04:45:20.849498 HUB in 10.14.3.130 -> 10.68.6.173: icmp: echo request
2022-09-08 04:45:20.849538 HUB out 10.68.6.173 -> 10.14.3.130: icmp: echo reply
2022-09-08 04:45:21.869492 HUB in 10.14.3.130 -> 10.68.6.173: icmp: echo request
2022-09-08 04:45:21.869533 HUB out 10.68.6.173 -> 10.14.3.130: icmp: echo reply
^C
10 packets received by filter
0 packets dropped by kernel
Spoke-2 #
In the same way, vice-versa should also work.
Spoke-2 # execute ping-options source 10.68.6.173
Spoke-2 # execute ping 10.14.3.130
PING 10.14.3.130 (10.14.3.130): 56 data bytes
64 bytes from 10.14.3.130: icmp_seq=0 ttl=254 time=1.1 ms
64 bytes from 10.14.3.130: icmp_seq=1 ttl=254 time=1.0 ms
64 bytes from 10.14.3.130: icmp_seq=2 ttl=254 time=1.0 ms
64 bytes from 10.14.3.130: icmp_seq=3 ttl=254 time=0.8 ms
64 bytes from 10.14.3.130: icmp_seq=4 ttl=254 time=0.8 ms
--- 10.14.3.130 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 0.8/0.9/1.1 ms
Spoke-2 #
Troubleshooting:
# diagnose sniffer packet any 'host x.x.x.x and host y.y.y.y and icmp' 4 0 a <----- x.x.x.x is the source and y.y.y.y is the destination.
Above command to check if packets are sent and received, it is necessary to run on firewalls and once done, stop using: CTRL+C.
Below is the debug flow commands:
# diagnose debug reset
# diagnose debug flow filter clear
# diagnose debug flow filter addr x.x.x.x
# diagnose debug flow filter proto 1
# diagnose debug flow show iprope enable
# diagnose debug flow trace start 100
# diagnose debug enable
Once the above commands run, initiate the ping and once the error is seen, stop the debug using:
# diagnose debug disable
# diagnose debug reset
The Fortinet Security Fabric brings together the concepts of convergence and consolidation to provide comprehensive cybersecurity protection for all users, devices, and applications and across all network edges.
Copyright 2025 Fortinet, Inc. All Rights Reserved.