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.

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.
Leave a Reply