Quick tips: FortiSwitch custom command double quotes, ZTNA SAML vhost error, session evaluation after ZTNA tag changes

Not every post needs to be something that takes hours to get together, and that is why I want to start a new type of post, where I write about things I experience in my day-to-day, that seem interesting, and want to put out there, so maybe someone else doesn’t have to spend hours looking for a solution.

With that, dear reader, read about the quick tips for today.

Models and versions:

  • FortiGate 70G on 7.6.6
  • FortiSwitch 424E on 7.6.6
  • FortiClient EMS on 7.4.7
  • FortiClient on 7.4.6
  • Windows 11 client on 25H2

Handling double quotes in FortiSwitch custom commands

Custom commands are an important part of managing FortiSwitches, because not every piece of configuration you can make on a FortiSwitch is available on the FortiGate. Lots of things you don’t touch every day have to be configured using custom commands, and in some cases, you run into the issue of having to use double quotes, which creates an issue, because double quotes, on the FortiGate, signify where the custom command starts and where it ends.

Looking at the example that prompted this post: You want to push a Certificate Authority (CA) certificate to a FortiSwitch for LDAPS authentication.

On a FortiSwitch, this would look like this (certificate shortened for brevity):

config system certificate ca
    edit WIN-CA
	set ca "-----BEGIN CERTIFICATE-----
		MIIFrDCCA5SgAwIBAgIQP+FZ4Onx66RLEDsn8YuCGTANBgkqhkiG9w0BAQ0FADBV
		MRMwEQYKCZImiZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbGFiZG9tYWlu
		MRIwEAYKCZImiZPyLGQBGRYCYWQxDzANBgNVBAMTBldJTi1DQTAeFw0yNTA4MDIx
		...
		TETr69CP4eDwJGD7gZp8Lnz98Xj+fQUcco+/9xuK7JXQlE97H05Mn32YZmv4aFC0
		v2S0t0Lk+YqUVZKAMRfnZ9nu8dTHFE4Q+5TqOxcyEwpmAeXRIWs/HPOXhegu87tu
		bqYj4gD9n/S8BjlOqtx/Yw==
		-----END CERTIFICATE-----"
    next
end

The double quotes in the BEGIN CERTIFICATE line will create your first problem when using a custom command. If you want to push this certificate using a custom command on the FortiGate, you have to replace the double quotes with a % symbol followed by the hex code. In this case, it would be %22.

Using it in a custom command, we can deploy a CA certificate like this (certificate shortened for brevity):

config switch-controller custom-command
    edit "WIN-CA"
        set command "config system certificate ca%0a edit WIN-CA%0a set ca %22-----BEGIN CERTIFICATE-----%0aMIIFrDCCA5SgAwIBAgIn/S8BjlOqtx/Yw==%0a-----END CERTIFICATE-----%22%0a next%0a end%0a"
    next
end
config switch-controller managed-switch
    edit "SERIAL/NAME"
        config custom-command
            edit "WIN-CA"
                set command-name "WIN-CA"
            next
        end
    next
end

Someone hinted at the possibility of this by telling me to use a backslash and the hex code, which didn’t work, and then I remembered that in custom commands, %0a should be used for a line feed, and 0a is the hex code for that. Put two and two together, and you end up with %22.

Thank you to this specific person, who, I am sure, wants to remain anonymous, but he will likely read this.

If you ever need a lookup table or a converter for characters and their hex representation, check out one of the many sites that offer this, like this one: ASCII Character Chart with Decimal, Binary and Hexadecimal Conversions

ZTNA “failed to match an API-gateway” error using SAML SSO authentication

If you’re configuring SAML SSO authentication for your ZTNA connections and you run into the issue where FortiClient displays a “The page you requested has been blocked because no API gateway was matched” and the FortiGate shows an error like “Traffic denied because HTTP url (https://ztnaproxy.domain.com/tcp?address=win-ad.ad.labdomain.com&port=3389&tls=0) failed to match an API-gateway with vhost(name/hostname:saml_auto_vhost_SAML/ztnaproxy.domain.com)” (the FQDN and the “SAML” is custom and will look different for you), I might have an answer for you.

Go into your ZTNA server configuration and the service/server mapping, specify the virtual host, supply a host matched by a substring that corresponds to the domain from the HTTPS URL in the error, and select your certificate.

ZTNA virtual host
config firewall access-proxy-virtual-host
    edit "auto-ztna-vhmoifbct0"
        set ssl-certificate "ztnaproxy.domain.com"
        set host "ztnaproxy.domain.com"
    next
end
config firewall access-proxy
    edit "LAB-ZTNA"
        set vip "LAB-ZTNA"
        config api-gateway
            edit 1
                set url-map "/tcp"
                set service tcp-forwarding
                set virtual-host "auto-ztna-vhmoifbct0"
                config realservers
                    edit 1
                        set address "win-ad.ad.labdomain.com"
                        set mappedport 3389
                    next
                    edit 2
                        set address "ubuntu-ws-1.ad.labdomain.com"
                        set mappedport 80
                    next
                end
            next
            edit 2
                set service samlsp
                set saml-server "ENTRA-SAML"
            next
        end
    next
end

I’m not sure why this error happened, because I have configured this thing before without having to specify a virtual host, but this works.

[Update 2026-04-29]: Yesterday, in bed, I found the cause of this behaviour change. Since 7.6.1 FortiOS learns, and creates, a virtual host implicitly from a SAML authentication when using ZTNA. The proposed workaround of “To avoid this behavior, put the SAML api-gateway in a separate access-proxy and use a different virtual host for your server.” is a bit confusing to me, but unless you have multiple SAML servers for ZTNA, which creates other issues due to authentication rules, it’s not relevant. If you have multiple SAML servers, you will always fall into the authentication rule that is first in the list, unless you configure source and/or destination addresses (CLI-only), so you can authenticate with that one, and you get a user on the FortiGate in WAD, but authorization will fail, because you are hitting a proxy rule with one SAML server, but you were authenticated with another.

Evaluating firewall sessions after dynamic address changes (ZTNA tags, FSSO)

After solving the error from the previous section, I played around with ZTNA tags and noticed that, if a tag changes, existing sessions won’t get evaluated and possibly blocked. This is something you have to be aware of. If you are using dynamic address objects, you probably want existing sessions to not transfer traffic if new ones should get blocked.

Think of an attacker establishing a channel from inside your network to an external command and control server, and if your ZTNA tags notice this, this connection won’t get blocked, because it’s an existing session.

If you want existing firewall policies to get evaluated if dynamic address objects change, be that ZTNA tags, FQDN objects, Fortinet Single Sign-On (FSSO) objects, or otherwise, it’s only a single command away.

config system settings
    set dyn-addr-session-check enable
end

Short and sweet and done

I might do this type of post more often, and I hope that you, dear reader, can take something away from this.

Comments

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

Leave a Reply

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