Created on
05-09-2023
03:36 AM
Edited on
06-25-2025
01:48 AM
By
Jean-Philippe_P
This article describes a configuration example with a Hub and spoke topology where a FortiGate acting as an SSL VPN client will establish a tunnel with the SSL VPN server to allow users that are LAN connected behind the Spoke(client) to be tunneled through SSL VPN and be able to reach destinations on the Hub.
FortiOS v7.0.x and newer versions.
FortiGate SSL VPN client and Server configuration
For this case, the following network example will be used:
To generate certificates in a Microsoft environment, follow the KB article below, which shows an example to create an EAP server certificate for FortiAuthenticator.
The same concept can be used to generate Server certificates for FortiGate or other endpoints:
Technical Tip: How to issue EAP certificate with Microsoft certification authority
In this scenario, the certificates and private keys have been generated using the XCA certificate generation tool (OpenSSL frontend).
Server certificate (CN = client2.lab.net).
CA certificate (issuer) (CN = global.lab.net).
Go to System -> Certificates -> Create/Import.
Select 'Certificate'.
Import the Server certificate.
After it is imported, the certificate will appear under 'Local Certificate'.
Go to System -> Certificates -> Create/Import.
Select 'CA Certificate'
Import CA certificate:
The certificate will appear under 'Remote CA Certificate' named as CA_Cert_1 in case this was the first imported case.
Configure the Local user and PKI user.
In this case, a local use called 'client2' will be used, which will represent the SSL VPN Spoke FortiGate when the tunnel is established.
To enable certificate authentication, it will be necessary to create a PKI user. This option is not available in the GUI, but it will appear after creating the first user through CLI:
config user peer
edit "pk1"
set ca "CA_Cert_1"
set subject "CN = client2.lab.net"
next
end
The user 'pk1' has been created and the issuing CA has been defined, which was previously imported, and the subject parameter matches the Subject field in the Certificate 'CN = client2.lab.net'
The entry will now be available on the GUI:
Create the SSLVPN portal and configure the SSL VPN settings.
A new portal called 'testportal2' will be created, and split tunneling based on Policy Destination will be enabled.
Leave other settings as default:
Configure the SSL VPN settings and add portal mapping:
Additionally, an authentication rule will be configured for the portal, adding the certificate authentication requirement and defining the 'client2':
config vpn ssl settings
set servercert "client2.lab.net"
set tunnel-ip-pools "SSLVPN_TUNNEL_ADDR1"
set tunnel-ipv6-pools "SSLVPN_TUNNEL_IPv6_ADDR1"
set source-interface "port1"
set source-address "all"
set source-address6 "all"
set default-portal "full-access"
config authentication-rule
edit 1
set users "client2"
set portal "testportal2"
set client-cert enable
set user-peer "pk1"
next
end
end
Create the firewall policy with the destination of the remote subnet 20.20.20.0/24.
CLI config:
config firewall policy
edit 1
set name "sslvpn2"
set uuid xx
set srcintf "ssl.root"
set dstintf "port2" <---- Lan interface of HUB.
set action accept
set srcaddr "all"
set dstaddr "Internal2" <----- Address object Internal LAN network of Hub.
set schedule "always"
set service "ALL"
set nat enable
set users "client2" <------ FortiGate Client.
next
end
Configuration of SPOKE FortiGate (Client in the Branch office).
Summary:
config user peer
edit "pk1"
set ca "CA_Cert_1"
set subject "CN = client2.lab.net"
next
end
Configure the SSL VPN client.
In newer FortiOS v7.0.x, there is an additional option in VPN -> SSL VPN client.
Here, an SSL VPN tunnel interface has been created under the WAN(port1) of the Spoke FortiGate.
In the SSL VPN client configuration, the following settings have been created, where under the 'Server' parameter, it will be necessary to specify the Public IP where the HUB FortiGate listens for connections. The pre-shared key must be the same as the password for the user 'client2'.
CLI configuration:
config vpn ssl client
edit "sslclient_to_srv"
set interface "sslclient_port1"
set user "client2"
set psk ENC XXXXXX
set peer "pk1"
set server "X.X.X.X"
set port 10443
set certificate "client2.lab.net"
next
end
Configure the Firewall policy.
CLI configuration:
edit 8
set name "Policy_to_Server_ssl_tunnel"
set uuid XX
set srcintf "port2" <----- Lan interface Spoke.
set dstintf "sslclient_port1"
set action accept
set srcaddr "all"
set dstaddr "all"
set schedule "always"
set service "ALL"
set nat enable
next
Validation of the configuration.
Once the configuration of the SSL VPN client on the SPOKE is finished and the Firewall policy is in place, the attempt to automatically establish the tunnel SPOKE <->HUB will be made.
diagnose debug reset
diagnose debug app sslvpn -1
diagnose debug app fnbamd -1
diagnose debug console timestamp enable
diagnose debug enable
Successful connection attempt:
2023-05-08 17:10:13 [3162:root:13]User Agent: FortiSSLVPN
2023-05-08 17:10:13 [3162:root:13]rmt_logincheck_cb_handler:1283 user 'client2' has a matched local entry.
2023-05-08 17:10:13 [3162:root:13]sslvpn_auth_check_usrgroup:2978 forming user/group list from policy.
2023-05-08 17:10:13 [3162:root:13]sslvpn_auth_check_usrgroup:3024 got user (1) group (0:0).
2023-05-08 17:10:13 [3162:root:13]sslvpn_validate_user_group_list:1890 validating with SSL VPN authentication rules (1), realm ().
2023-05-08 17:10:13 [3162:root:13]sslvpn_validate_user_group_list:1975 checking rule 1 cipher.
2023-05-08 17:10:13 [3162:root:13]sslvpn_validate_user_group_list:1983 checking rule 1 realm.
2023-05-08 17:10:13 [3162:root:13]sslvpn_validate_user_group_list:1994 checking rule 1 source intf.
2023-05-08 17:10:13 [3162:root:13]sslvpn_validate_user_group_list:2033 checking rule 1 vd source intf.
2023-05-08 17:10:13 [3162:root:13]sslvpn_validate_user_group_list:2526 rule 1 done, got user (1:1) group (0:0) peer group (0).
2023-05-08 17:10:13 [3162:root:13]sslvpn_validate_user_group_list:2534 got user (1:1) group (0:0) peer group (0).
2023-05-08 17:10:13 [3162:root:13]sslvpn_validate_user_group_list:2876 got user (1:1), group (0:0) peer group (0).
2023-05-08 17:10:13 [3162:root:13]fam_cert_send_req:1109 do certificate peer check first(2).
2023-05-08 17:10:13 [3162:root:13]fam_cert_send_req:1181 doing certificate checking for 1 peer(s).
2023-05-08 17:10:13 [2341] handle_req-Rcvd auth_cert req id=691969652, len=1101, opt=0
2023-05-08 17:10:13 [974] __cert_auth_ctx_init-req_id=691969652, opt=0
2023-05-08 17:10:13 [103] __cert_chg_st- 'Init'
2023-05-08 17:10:13 [140] fnbamd_cert_load_certs_from_req-1 cert(s) in req.
2023-05-08 17:10:13 [661] __cert_init-req_id=691969652
2023-05-08 17:10:13 [710] __cert_build_chain-req_id=691969652
2023-05-08 17:10:13 [257] fnbamd_chain_build-Chain discovery, opt 0x13, cur total 1
2023-05-08 17:10:13 [273] fnbamd_chain_build-Following depth 0
2023-05-08 17:10:13 [308] fnbamd_chain_build-Extend chain by system trust store. (good: 'CA_Cert_1')
2023-05-08 17:10:13 [273] fnbamd_chain_build-Following depth 1
2023-05-08 17:10:13 [287] fnbamd_chain_build-Self-sign detected.
2023-05-08 17:10:13 [99] __cert_chg_st- 'Init' -> 'Validation'
2023-05-08 17:10:13 [831] __cert_verify-req_id=691969652
2023-05-08 17:10:13 [832] __cert_verify-Chain is complete.
2023-05-08 17:10:13 [457] fnbamd_cert_verify-Chain number:2
2023-05-08 17:10:13 [471] fnbamd_cert_verify-Following cert chain depth 0
2023-05-08 17:10:13 [533] fnbamd_cert_verify-Issuer found: CA_Cert_1 (SSL_DPI opt 1)
2023-05-08 17:10:13 [471] fnbamd_cert_verify-Following cert chain depth 1
2023-05-08 17:10:13 [675] fnbamd_cert_check_group_list-checking group with name 'pk1'
2023-05-08 17:10:13 [490] __check_add_peer-check 'pk1'
2023-05-08 17:10:13 [366] peer_subject_cn_check-Cert subject 'CN = client2.lab.net, CN = client2.lab.net'
2023-05-08 17:10:13 [294] __RDN_match-Checking 'CN' val 'client2.lab.net' -- match.
2023-05-08 17:10:13 [324] __cert_subject_RDN_compare-Total matched RDNs in cert: 1
2023-05-08 17:10:13 [391] peer_subject_cn_check-Subject is good.
2023-05-08 17:10:13 [497] __check_add_peer-'pk1' check ret:good
2023-05-08 17:10:13 [612] __peer_user_clear_unmatched-Clear all user(s) other than 'pk1'
2023-05-08 17:10:13 [631] __peer_user_clear_unmatched-
2023-05-08 17:10:13 [191] __get_default_ocsp_ctx-def_ocsp_ctx=(nil), no_ocsp_query=0, ocsp_enabled=0
2023-05-08 17:10:13 [738] fnbamd_cert_check_group_list-Peer users
2023-05-08 17:10:13 [741] fnbamd_cert_check_group_list- 'pk1' ('N/A','N/A')
2023-05-08 17:10:13 [867] __cert_verify_do_next-req_id=691969652
2023-05-08 17:10:13 [99] __cert_chg_st- 'Validation' -> 'Done'
2023-05-08 17:10:13 [912] __cert_done-req_id=691969652
2023-05-08 17:10:13 [1652] fnbamd_auth_session_done-Session done, id=691969652
2023-05-08 17:10:13 [957] __fnbamd_cert_auth_run-Exit, req_id=691969652
2023-05-08 17:10:13 [1689] create_auth_cert_session-fnbamd_cert_auth_init returns 0, id=691969652
2023-05-08 17:10:13 [1608] auth_cert_success-id=691969652
2023-05-08 17:10:13 [1059] fnbamd_cert_auth_copy_cert_status-req_id=691969652
2023-05-08 17:10:13 [1067] fnbamd_cert_auth_copy_cert_status-Matched peer user 'pk1'
2023-05-08 17:10:13 [833] fnbamd_cert_check_matched_groups-checking group with name 'pk1'
2023-05-08 17:10:13 [895] fnbamd_cert_check_matched_groups-matched
2023-05-08 17:10:13 [1098] fnbamd_cert_auth_copy_cert_status-Leaf cert status is unchecked.
2023-05-08 17:10:13 [1186] fnbamd_cert_auth_copy_cert_status-Cert st 2c0, req_id=691969652
2023-05-08 17:10:13 [216] fnbamd_comm_send_result-Sending result 0 (nid 672) for req 691969652, len=2149
2023-05-08 17:10:13 [3162:root:13]__auth_cert_cb:895 certificate check OK.
2023-05-08 17:10:13 [3162:root:13]__auth_cert_cb:912 certificate check OK, matched peer [pk1].
2023-05-08 17:10:13 2023-05-08 17:10:13 [1553] destroy_auth_cert_session-id=691969652
2023-05-08 17:10:13 [3162:root:13]sslvpn_update_user_group_list:1720 Remove user(s) and group(s) do not set matched peer [pk1].
[1032] fnbamd_cert_auth_uninit-req_id=691969652
2023-05-08 17:10:13 2023-05-08 17:10:13 [131] fnbamd_peer_ctx_free-Freeing peer ctx 'pk1'
[3162:root:13]sslvpn_update_user_group_list:1724 got user (1:1), group (0:0) peer group (0) after update.
2023-05-08 17:10:13 [3162:root:13]sslvpn_authenticate_user:183 authenticate user: [client2]
2023-05-08 17:10:13 [3162:root:13]sslvpn_authenticate_user:197 create fam state
2023-05-08 17:10:13 [3162:root:13]user 'client2' uses 2FA: ctx->peer_two_factor = 0, ctx->peer_name.peername = 1, ctx->is_two_factor = 0
2023-05-08 17:10:13 [3162:root:13]fam_auth_send_req:882 found node client2:1:pk1, valid:1
2023-05-08 17:10:13 [3162:root:13][fam_auth_send_req_internal:426] Groups sent to FNBAM:
2023-05-08 17:10:13 [3162:root:13]group_desc[0].grpname = client2
2023-05-08 17:10:13 [3162:root:13][fam_auth_send_req_internal:438] FNBAM opt = 0X301420
2023-05-08 17:10:13 local auth is done with user 'client2', ret=0
2023-05-08 17:10:13 [3162:root:13]fam_auth_send_req_internal:514 fnbam_auth return: 0
2023-05-08 17:10:13 [3162:root:13][fam_auth_send_req_internal:539] Authenticated groups (1) by FNBAM with auth_type (1):
2023-05-08 17:10:13 [3162:root:13]Received: auth_rsp_data.grp_list[0] = 16777218
2023-05-08 17:10:13 [3162:root:13][fam_auth_send_req_internal:652] The user client2 is authenticated.
2023-05-08 17:10:13 [3162:root:13]Auth successful for user [client2] with matched user-peer [pk1]
2023-05-08 17:10:13 [3162:root:13]fam_do_cb:665 fnbamd return auth success.
2023-05-08 17:10:13 [3162:root:13]SSL VPN login matched rule (1).
2023-05-08 17:10:13 [3162:root:13]User Agent: FortiSSLVPN
2023-05-08 17:10:13 [3162:root:13]rmt_web_session_create:1209 create web session, idx[0]
2023-05-08 17:10:13 [3162:root:13]login_succeeded:536 redirect to hostcheck
2023-05-08 17:10:13 [3162:root:13]User Agent: FortiSSLVPN
2023-05-08 17:10:13 [3162:root:13]deconstruct_session_id:709 decode session id ok, user=[client2], group=[],authserver=[],portal=[testportal2],host[X.X.X.Y],realm=[],csrf_token=[B0FEE0F19A14662E198D8689F019133D],idx=0,auth=1,sid=27f04e3b,login=1683558613,access=1683558613,saml_logout_url=no,pip=no,grp_
info=[ATJgjh],rmt_grp_info=[]
2023-05-08 17:10:13 [3162:root:13]deconstruct_session_id:709 decode session id ok, user=[client2], group=[],authserver=[],portal=[testportal2],host[X.X.X.Y],realm=[],csrf_token=[B0FEE0F19A14662E198D8689F019133D],idx=0,auth=1,sid=27f04e3b,login=1683558613,access=1683558613,saml_logout_url=no,pip=no,grp_
info=[ATJgjh],rmt_grp_info=[]
2023-05-08 17:10:13 [3162:root:13]deconstruct_session_id:709 decode session id ok, user=[client2], group=[],authserver=[],portal=[testportal2],host[X.X.X.Y],realm=[],csrf_token=[B0FEE0F19A14662E198D8689F019133D],idx=0,auth=1,sid=27f04e3b,login=1683558613,access=1683558613,saml_logout_url=no,pip=no,grp_
info=[ATJgjh],rmt_grp_info=[]
2023-05-08 17:10:13 [3162:root:13]req: /remote/fortisslvpn_xml?dual_stack=1
2023-05-08 17:10:13 [3162:root:13]deconstruct_session_id:709 decode session id ok, user=[client2], group=[],authserver=[],portal=[testportal2],host[X.X.X.Y],realm=[],csrf_token=[B0FEE0F19A14662E198D8689F019133D],idx=0,auth=1,sid=27f04e3b,login=1683558613,access=1683558613,saml_logout_url=no,pip=no,grp_
info=[ATJgjh],rmt_grp_info=[]
2023-05-08 17:10:13 [3162:root:13]deconstruct_session_id:709 decode session id ok, user=[client2], group=[],authserver=[],portal=[testportal2],host[X.X.X.Y],realm=[],csrf_token=[B0FEE0F19A14662E198D8689F019133D],idx=0,auth=1,sid=27f04e3b,login=1683558613,access=1683558613,saml_logout_url=no,pip=no,grp_
info=[ATJgjh],rmt_grp_info=[]
2023-05-08 17:10:13 [3162:root:13]rmt_fortisslvpn_xml_cb_handler:1139 Client requests dual stack tunnel
2023-05-08 17:10:13 [3162:root:13]sslvpn_reserve_dynip:1476 tunnel vd[root] ip[X.X.X.X] app session idx[0]
2023-05-08 17:10:13 [3162:root:13]form_ipv4_pol_split_tunnel_addr:79 Matched policy (id = 1) to add ipv4 split tunnel routing address
2023-05-08 17:10:13 [3162:root:13]req: /remote/sslvpn-tunnel2
2023-05-08 17:10:13 [3162:root:13]sslvpn_tunnel2_handler,59, Calling rmt_conn_access_ex.
2023-05-08 17:10:13 [3162:root:13]deconstruct_session_id:709 decode session id ok, user=[client2], group=[],authserver=[],portal=[testportal2],host[X.X.X.Y],realm=[],csrf_token=[B0FEE0F19A14662E198D8689F019133D],idx=0,auth=1,sid=27f04e3b,login=1683558613,access=1683558613,saml_logout_url=no,pip=no,grp_
info=[ATJgjh],rmt_grp_info=[]
2023-05-08 17:10:13 [3162:root:13]normal tunnel2 request received.
2023-05-08 17:10:13 [3162:root:13]sslvpn_tunnel2_handler,173, Calling tunnel2.
2023-05-08 17:10:13 [3162:root:13]tunnel2_enter:1289 0x7fa7f7f56500:0x7fa7f7322000 sslvpn user[client2],type 1,logintime 0 vd 0 vrf 0
2023-05-08 17:10:13 [3162:root:13]tun dev (ssl.root) opened (33)
2023-05-08 17:10:13 [3162:root:13]fsv_associate_fd_to_ipaddr:2022 associate X.X.X.X to tun (ssl.root:33)
2023-05-08 17:10:13 [3162:root:13]proxy arp: scanning 6 interfaces for IP X.X.X.X
2023-05-08 17:10:13 [3162:root:13]Cannot determine ethernet address for proxy ARP
2023-05-08 17:10:13 [3162:root:13]Will add auth policy for policy 1 for user client2:
2023-05-08 17:10:13 [3162:root:13]Add auth logon for user client2:, matched group number 1
Validation on HUB FortiGate.
Go to Dashboard -> Network -> SSL VPN.
It will be possible to see the SSL VPN spoke tunnel represented by the Local user 'client2'.
Route injection validation on SPOKE FortiGate.
In the Branch office FortiGate, it is possible to verify that it has dynamically added a route to the remote subnet 20.20.20.0/24 learned from the HUB.
firewall # get router info routing-table database
Codes: K - kernel, C - connected, S - static, R - RIP, B - BGP
O - OSPF, IA - OSPF inter area
N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
E1 - OSPF external type 1, E2 - OSPF external type 2
i - IS-IS, L1 - IS-IS level-1, L2 - IS-IS level-2, ia - IS-IS inter area
> - selected route, * - FIB route, p - stale info
Routing table for VRF=0
S *> 0.0.0.0/0 [10/0] via Y.Y.Y.Y, port1, [1/0]
C *> 10.10.10.0/24 is directly connected, port3
C *> X.X.X.X/20 is directly connected, port1
C *> X.X.X.X/32 is directly connected, sslclient_port1
S *> 20.20.20.0/24 [10/0] is directly connected, sslclient_port1, [1/0]
C *> X.X.X.X/32 is directly connected, VPN
C *> 192.168.10.0/24 is directly connected, port2
In the same manner, it is possible to have multiple other branches which are possible to reference as 'client3' or 'branch3' and so on in a Hub and Spoke topology.
More details and options for this type of setup are provided in the Fortinet documentation below:
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.