Skip to main content
heljag
Staff
Staff
February 26, 2026

Technical Tip: Implementing Spoke-to-Cloud VM reachability using hub firewall translation

  • February 26, 2026
  • 0 replies
  • 175 views
Description This article describes how to set up an VIP and IP pool to translate the traffic from spoke machines to cloud VM through IPsec tunnels.
Scope FortiGate.
Solution

The following diagram represents the scenario:

 

KB_VIP.drawio (1).png

 

In this example, the goal is to be able to reach the Cloud Server from machine 10.2.1.10, passing through the HUB.

The spoke firewall is connected to the hub firewall through an IPsec tunnel and the hub firewall is also connected to the cloud firewall through an IPsec tunnel.

Since the selectors in the tunnel cloud_tunnel only allow the subnets 192.168.1.0/24 and 10.101.50.0/24 and cannot be changed, SNAT needs to be used.
Selectors from the spoke firewall to the hub firewall cannot be changed either, so a VIP will be used to forward and mask traffic to the cloud server.

 

 

  • Hub firewall tunnel list.

 

HUB FW # diagnose vpn tunnel list
list all ipsec tunnel in vd 0
------------------------------------------------------
name=from_spoke ver=2 serial=2 12.12.12.12:0->14.14.14.14:0 nexthop= tun_id=2.2.2.2 tun_id6=::2.2.2.2 status=up dst_mtu=0 weight=1
bound_if=27 real_if=0 lgwy=static/1 tun=intf mode=auto/1 encap=none/552 options[0228]=npu frag-rfc run_state=0 role=primary accept_traffic=1 overlay_id=0

proxyid_num=1 child_num=0 refcnt=6 ilast=158648 olast=43376624 ad=/0
stat: rxp=30 txp=0 rxb=4062 txb=0
dpd: mode=on-demand on=1 status=ok idle=20000ms retry=3 count=0 seqno=0
natt: mode=none draft=0 interval=0 remote_port=0
fec: egress=0 ingress=0
proxyid=from_spoke proto=0 sa=1 ref=2 serial=1
   src: 0:192.168.168.0-192.168.168.255:0
   dst: 0:10.2.1.0-10.2.1.255:0
   SA: ref=3 options=10226 type=00 soft=0 mtu=1280 expire=9890/0B replaywin=2048
       seqno=1 esn=0 replaywin_lastseq=00000000 qat=0 rekey=0 hash_search_len=1
   life: type=01 bytes=0/0 timeout=10531/10800
   dec: spi=dd34f064 esp=aes key=32 1105275dd933ada59f85ad939f79ee0addf70b480281878bbfe89e1d89695840
        ah=sha256 key=32 1bf1438757df85cb4e779c7ea46810a7faf52a5a149c2c62f863a05d44a3c789
   enc: spi=c3028f3f esp=aes key=32 1a92a0e423e639f4208866db521511bb006a5b36e6543f2f36ed4857aa814bc3
        ah=sha256 key=32 4db1b5b05d2b87beca0203dc005ee05fcdcc6d720088f14baffa7a23d4679ec7
   dec:pkts/bytes=0/0, enc:pkts/bytes=0/0

------------------------------------------------------
name=cloud_tunnel ver=2 serial=1 12.12.12.12:0->13.13.13.13:0 nexthop= tun_id=1.1.1.1 tun_id6=::1.1.1.1 status=up dst_mtu=1500 weight=1
bound_if=27 real_if=27 lgwy=static/1 tun=intf mode=auto/1 encap=none/552 options[0228]=npu frag-rfc run_state=0 role=primary accept_traffic=1 overlay_id=0

proxyid_num=1 child_num=0 refcnt=4 ilast=1 olast=1 ad=/0
stat: rxp=0 txp=54 rxb=0 txb=356
dpd: mode=on-demand on=1 status=ok idle=20000ms retry=3 count=0 seqno=25
natt: mode=none draft=0 interval=0 remote_port=0
fec: egress=0 ingress=0
proxyid=cloud_tunnel proto=0 sa=1 ref=2 serial=2 auto-negotiate
   src: 0:192.168.1.0-192.168.1.255:0
   dst: 0:10.101.50.0-10.101.50.255:0
   SA: ref=5 options=18227 type=00 soft=0 mtu=1438 expire=6892/0B replaywin=2048
       seqno=2 esn=0 replaywin_lastseq=00000000 qat=0 rekey=0 hash_search_len=1
   life: type=01 bytes=0/0 timeout=6930/7200
   dec: spi=dd34f065 esp=aes key=32 cc1e116daf23331b7a464aadfd646d5d627f91ddbd982d68aef67ed41ccd29c2
        ah=sha256 key=32 302d77f9484b2fa73afa6a2d281ff140b774a7ed9000caf85e18d6149bc1a4e0
   enc: spi=275660bf esp=aes key=32 c8c9fbd8c3b5630f3cc570a3b9bdfc88bcb8925e4d090aa52fdf58330969949e
        ah=sha256 key=32 64be415ceb86a5b5284c8246f08afa9a28fbb8f2cc61cee94f99d70ab24031f6
   dec:pkts/bytes=0/0, enc:pkts/bytes=2/0

 

  • Hub firewall configuration.

 

A firewall VIP will be created to handle the traffic initiated by the machine 10.2.1.10.

The VIP is using a dummy subnet (192.168.168.0/24) which is part of the selectors configured in the tunnel from_spoke.

This will ensure that traffic initiated by the machine will never use the real server IP or reach the HUB LAN subnet (192.168.1.0/24).

 

config firewall vip
    edit "Webserver_VIP"
        set extip 192.168.168.52
        set mappedip "10.101.50.1"
        set extintf "any"
        set portforward enable
        set protocol tcp
        set extport 443
        set mappedport 443
    next
end

 

Since the selectors from the cloud_tunnel are only using subnet 192.168.1.0/24, an IP pool from the same range will be used to SNAT the traffic. The IP used is the same one configured in the internal interface of the hub firewall.


config firewall ippool
    edit "IP_192.168.1.99"
        set startip 192.168.1.99
        set endip 192.168.1.99
        set arp-reply disable
    next
end

 

config system interface
    edit "internal"
        set vdom "root"
        set ip 192.168.99 255.255.255.0
        set allowaccess ping https ssh http telnet fgfm
        set type hard-switch
        set stp enable
        set role lan
    next
end

 

For the policy, the policy should allow traffic from the 'from_spoke' interface to the 'cloud_tunnel' interface, using the VIP (Webserver_VIP) and the IP Pool (IP_192.168.1.99).


config firewall policy
    edit 0
        set name "to Cloud"
        set srcintf "from_spoke
        set dstintf "cloud_tunnel"
        set action accept
        set srcaddr "all"
        set dstaddr "Webserver_VIP"
        set schedule "always"
        set service "ALL"
        set nat enable
        set ippool enable
        set poolname "IP_192.168.1.99"
    next
end

 

The session of this traffic is showed below:

 

session info: proto=6 proto_state=01 duration=14 expire=2 timeout=3600 refresh_dir=both flags=00000000 socktype=0 sockport=0 av_idx=0 use=5
origin-shaper=
reply-shaper=
per_ip_shaper=
class_id=0 ha_id=0 policy_dir=0 tunnel=/ tun_id=0.0.0.0/2.2.2.2 vlan_cos=0/255
state=may_dirty npu
statistic(bytes/packets/allow_err): org=240/4/1 reply=0/0/0 tuples=4
tx speed(Bps/kbps): 0/0 rx speed(Bps/kbps): 0/0
orgin->sink: org pre->post, reply pre->post dev=29->28/28->29 gwy=1.1.1.1/0.0.0.0
hook=pre dir=org act=dnat 10.2.1.10:18926->192.168.168.52:443(10.101.50.1:443)
hook=post dir=org act=snat 10.2.1.10:18926->10.101.50.1:443(192.168.1.99:18926)
hook=pre dir=reply act=dnat 10.101.50.1:443->192.168.1.99:18926(10.2.1.10:18926)
hook=post dir=reply act=snat 10.101.50.1:443->10.2.1.10:18926(192.168.168.52:443)
pos/(before,after) 0/(0,0), 0/(0,0)
misc=0 policy_id=4 pol_uuid_idx=769 auth_info=0 chk_client_info=0 vd=0
serial=01f120ae tos=ff/ff app_list=0 app=0 url_cat=0