Connecting to an HA FortiClient EMS cluster without an external load balancer

Do you want redundancy? Don’t answer that question; the answer is “Yes!” This means you want your FortiClient EMS deployment to be redundant, and this gives you the problem of how to handle the FortiClients and FortiGate connection to your HA EMS nodes if you don’t have an external load balancer.

Well, dear reader, I’ve got just the solution for you, so let’s see how it’s done.

The setup

  • FortiGate 70G on 7.6.7
  • 2x FortiClient EMS on 7.4.7
  • Windows 11 client on 25H2 with FortiClient 7.4.7

As far as layer 3 is concerned, I have a CLIENT and DMZ VLAN, where, respectively, the client and the EMS instances are located, as well as the UNUSED VLAN, which gets used later.

Topology

The configuration is very light this time, but it is still on the Fortinet resources GitHub repository.

FortiClient EMS HA basics

First things first. There are several HA deployment options, and I specifically cover the one using only VM appliances. As the documentation states, this requires FortiClient EMS 7.4.5, so keep this in mind.

Setting the cluster up is not hard; it took me about 5 minutes, and there are no additional licenses necessary; you just need the resources for the VM.

A few points here:

  • In order to change the priority of the nodes, you use the ha standby command on the current primary (use ha get nodes to see the cluster state). There are two roles in the cluster: the database and EMS
  • If you want to demote the current primary for both roles, you use ha standby --type=”db” and ha standby --type=”ems”. The documentation linked above only mentions it but doesn’t give an example of how to do it in the document. You need the CLI reference for this.
    • In the following example, EMS2 is the primary for both roles at first and gets demoted
    • I have removed the “Preferred DCs” column so it fits nicely in here, and because it’s empty anyway
ems@EMS2 $> ha get nodes
EMS Node(s):
 Name                                             | Role    | Status  | Last Seen
--------------------------------------------------+---------+---------+----------------------------
 EMS2 (*)                                         | primary | online  | 2026-06-18 18:32:57.841091
 EMS1                                             | standby | online  | 2026-06-18 18:33:00.042757

DB Node(s):
 Host                            | Port    | Role    | Status   | Latency (ms)
--------------------------------------------------+---------+---------+------------
 192.168.1.177                   | 5432    | standby | online   | 28
 127.0.0.1                       | 5432    | primary | online   | 27
ems@EMS2 $> ha standby --type="db"
Node demoted successfully!
ems@EMS2 $> ha standby --type="ems"
Node demoted successfully!
ems@EMS2 $> ha get nodes
EMS Node(s):
 Name                                             | Role    | Status  | Last Seen
--------------------------------------------------+---------+---------+----------------------------
 EMS1                                             | primary | online  | 2026-06-18 18:35:10.04539
 EMS2 (*)                                         | standby | online  | 2026-06-18 18:35:01.322546

DB Node(s):
 Host                            | Port    | Role    | Status   | Latency (ms)
--------------------------------------------------+---------+---------+------------
 192.168.1.177                   | 5432    | primary | online   | 27
 127.0.0.1                       | 5432    | standby | online   | 26
  • The telemetry service, TCP/8013, is only active on the current primary, and the HTTPS service is active on both nodes
    • This is an important point for later
  • EMS does not have its own virtual IP or load balancer service, like haproxy, so any outside connections to it have to get load balanced via something. If you have an external load balancer, this is simple, but that’s a luxury not everyone has
  • The failover time is calculated using the following formula: High Availability Keep Alive Interval * 2 + 60
    • The High Availability Keep Alive Interval is configured in the EMS Settings menu
    • With default settings, this means it takes 80 seconds for a failover to occur

    With the basics covered, let’s connect things to EMS.

    The FortiClient to EMS connection

    This one is easy. You can use the Load Balance feature on the FortiGate to accomplish this (it has to be enabled in Feature Visibility first), and then you can configure everything in the Virtual Servers menu under Policy & Objects.

    For FortiClients, we need, at the minimum, TCP/8013 for the telemetry, so create your virtual server using that as the port and also the health check (either in-line or using the Health Check menu). If you need the installer port, default TCP/11443, you can do that too.

    FortiClient EMS HA Virtual Server
    config firewall vip
        edit "EMS-HA-FORTICLIENT"
            set type server-load-balance
            set server-type tcp
            set extip 192.168.1.190
            set extintf "CLIENTS"
            set monitor "tcp8013"
            set extport 8013
            config realservers
                edit 1
                    set ip 192.168.1.177
                    set port 8013
                next
                edit 2
                    set ip 192.168.1.178
                    set port 8013
                next
            end
        next
    end

    With the virtual server created, you can create your policy accordingly. Note that if you use the type TCP in your virtual server, the policy can be created using the flow-based inspection mode, which means hardware offloading. You only need proxy-based for non-TCP/UDP/IP types. Attach security profiles to the policy as necessary.

    FortiClient EMS HA Policy

    That’s everything you need for FortiClient to connect to EMS. Use the FQDN or an invitation code in the FortiClient GUI and hit Connect. Simple, right?

    The FortiGate to EMS connection

    This is the one that required some time to get working because the FortiGate needs to load-balance its own traffic, which I didn’t think was actually possible, but apparently it is.

    We, again, start with the virtual server, and we need TCP/443 here, but crucially, the health check must not be TCP/443. As mentioned above, the HTTPS service is active on both nodes, so this would create immediate issues depending on your load-balancing method and, in general, create issues.

    What service is only active on the primary node? The telemetry service, so we reuse the health check for TCP/8013, we already used for the FortiClient connection. The real servers still use TCP/443, however.

    I am binding the virtual server to the UNUSED VLAN because I only need to put the virtual server in a policy to activate it. This is done more so to show that this is possible. In reality, you would probably bind it to a management interface, so if you go to the virtual server IP, you always land on the active EMS node’s GUI.

    FortiGate EMS HA Virtual Server
    config firewall vip
        edit "EMS-HA-FORTIGATE"
            set type server-load-balance
            set server-type tcp
            set extip 192.168.1.190
            set extintf "LOOPBACK"
            set monitor "tcp8013"
            set extport 443
            config realservers
                edit 1
                    set ip 192.168.1.177
                    set port 443
                next
                edit 2
                    set ip 192.168.1.178
                    set port 443
                next
            end
        next
    end

    With this in mind, the policy we need is nothing special. The virtual server just needs to be active, and again, we can use a flow-based policy. Attach security profiles to the policy as necessary.

    FortiGate EMS HA Policy

    In the Fabric Connector for EMS, we use the FQDN, which resolves to the virtual server IP, and at least in my configuration, nothing happens because the traffic gets sourced incorrectly.

    FortiGate EMS connector

    To solve this, you go to the CLI and set a source IP for this connection. I am using the IP that the DMZ interface has, so that will be the one I see on EMS.

    Since we’re already in the CLI, we can use the command execute fctems verify <ID> (insert your ID) to start the verification, which should give us a certificate to accept.

    FortiGate EMS fabric CLI configuration

    70G-EMSHA # config endpoint-control fctems
    70G-EMSHA (fctems) # edit 1
    70G-EMSHA (1) # set source-ip 192.168.1.202
    70G-EMSHA (1) # end
    
    The configuration will not be effective unless server certificate is verified.
    You can get and verify server certificate by the following command:
    "execute fctems verify 1" (ems table id)
    
    70G-EMSHA # execute fctems verify 1
    
            Subject:     CN = ems-ha.ad.labdomain.com
            Issuer:      DC = com, DC = labdomain, DC = ad, CN = WIN-CA
            Valid from:  2026-06-18 08:10:35  GMT
            Valid to:    2028-06-17 08:10:35  GMT
            Fingerprint: 6D:6B:80:BC:10:9B:31:F7:15:7A:CC:03:71:01:98:9C:97:A0:4D:C5:21:12:C8:10:15:31:F3:43:E8:9B:2E:A7
            Root CA:     No
            Version:     3
            Serial Num:
                    32:00:00:00:44:88:6a:ed:ca:e6:cb:5b:39:00:02:00:00:00:44
            Extensions:
                    Name:     X509v3 Subject Key Identifier
                    Critical: no
                    Content:
                    BC:33:E8:D9:39:2F:29:36:21:E2:B7:88:9A:70:8D:79:63:F7:C6:55
    
                    Name:     X509v3 Key Usage
                    Critical: yes
                    Content:
                    Digital Signature, Key Encipherment
    
                    Name:     X509v3 Subject Alternative Name
                    Critical: no
                    Content:
                    DNS:ems-ha.ad.labdomain.com, DNS:ems1.ad.labdomain.com, DNS:ems2.ad.labdomain.com
    
                    Name:     X509v3 Authority Key Identifier
                    Critical: no
                    Content:
                    F3:CD:D6:6C:D4:C1:35:68:D7:EE:AA:07:7A:A8:5A:73:B8:48:6C:D5
    
                    Name:     X509v3 CRL Distribution Points
                    Critical: no
                    Content:
                    Full Name:
      URI:ldap:///CN=WIN-CA(2),CN=WIN-AD,CN=CDP,CN=Public%20Key%20Services,CN=Services,CN=Configuration,DC=ad,DC=labdomain,DC=com?certificateRevocationList?base?objectClass=cRLDistributionPoint
      URI:http://WIN-AD.ad.labdomain.com/CertEnroll/WIN-CA(2).crl
    
    
                    Name:     Authority Information Access
                    Critical: no
                    Content:
                    CA Issuers - URI:ldap:///CN=WIN-CA,CN=AIA,CN=Public%20Key%20Services,CN=Services,CN=Configuration,DC=ad,DC=labdomain,DC=com?cACertificate?base?objectClass=certificationAuthority
    CA Issuers - URI:http://WIN-AD.ad.labdomain.com/CertEnroll/WIN-AD.ad.labdomain.com_WIN-CA(2).crt
    
                    Name:     Microsoft certificate template
                    Critical: no
                    Content:
                    0-.%+.....7.....n...z...%...w........G..f..d...
    
                    Name:     X509v3 Extended Key Usage
                    Critical: no
                    Content:
                    TLS Web Server Authentication
    
                    Name:     Microsoft Application Policies Extension
                    Critical: no
                    Content:
                    0.0
    ..+.......
    
    EMS configuration needs user to confirm server certificate.
    Do you wish to add the above certificate to trusted remote certificates? (y/n)y
    
    Certificate successfully configured and verified.

    Once we have entered the good old y, we can go to EMS and authorize the FortiGate that appeared.

    EMS fabric device

    Wrapping up

    And that’s that. You can do your failover tests (mine worked), and hopefully, dear reader, you can now connect your FortiClients and your FortiGate to your EMS HA deployment. Additional FortiGates should be much easier to handle; it’s just the first one that requires a bit of special attention.

    And if you don’t have enough of FortiClient EMS yet, you can read about how you can use its certificate management capabilities to help with VPN authentication, full SSL/TLS inspection and 802.1X.

    Comments

    Be civil. Stay on topic. Don’t lie.

    Leave a Reply

    Your email address will not be published. Required fields are marked *