Skip to main content
New Member
June 19, 2026
Question

FortiZTNA user based mac binding

  • June 19, 2026
  • 8 replies
  • 178 views

Hello , can we do user based ztna web proxy with using local user of Fortigate local database with mac binding , mean ztna web proxy is opened with only same user with same mAc of machine , otherwise rejected 

8 replies

christian_89_
Explorer II
June 19, 2026

Yes, this is achievable with full ZTNA, and it gives you exactly the "same user and same authorized machine, otherwise deny" behaviour. The trick is that a ZTNA proxy policy enforces two independent conditions at once: the user (via groups) and the device (via the EMS client certificate and a posture tag). Combine them with AND match logic and anything that fails either half is rejected.

Here is the build, and where each half lives.

Device side first. The endpoint runs FortiClient enrolled in EMS, EMS acts as the ZTNA CA and issues the client a certificate, and the FortiGate connects to EMS and auto-syncs the posture tags:

 

 

config endpoint-control fctems
edit "EMS1"
set server <ems-ip-or-fqdn>
next
end

ZTNA server, the access proxy VIP plus the real server. client-cert enable is what makes the FortiGate verify the device certificate, this is your device identity:

 

 

config firewall vip
edit "ZTNA-app"
set type access-proxy
set extip <gateway-ip>
set extintf "port1"
set server-type https
set extport 9443
set ssl-certificate "<gateway-cert>"
next
end
config firewall access-proxy
edit "ZTNA-app"
set vip "ZTNA-app"
set client-cert enable
config api-gateway
edit 1
config realservers
edit 1
set ip <internal-server-ip>
set port <port>
next
end
next
end
next
end

User side, against your FortiGate local database. ZTNA needs an explicit authentication scheme and rule, you cannot skip this the way a normal firewall policy does, because the proxy path does not use authd:

 

 

config authentication scheme
edit "ZTNA-Auth-scheme"
set method basic
set user-database "local"
next
end
config authentication rule
edit "ZTNA-Auth-rule"
set srcintf "port1"
set srcaddr "all"
set ip-based enable
set active-auth-method "ZTNA-Auth-scheme"
next
end

The ZTNA rule itself, where the two halves come together. groups is your local user group, ztna-ems-tag is the device tag, and the AND logic means both must match:

 

 

config firewall proxy-policy
edit 1
set name "ZTNA-user-and-device"
set proxy access-proxy
set access-proxy "ZTNA-app"
set srcintf "port1"
set srcaddr "all"
set dstaddr "all"
set groups "<your-local-user-group>"
set ztna-ems-tag "<tag-scoped-to-this-device>"
set ztna-tags-match-logic and
set action accept
set schedule "always"
set utm-status enable
set ssl-ssh-profile "<profile>"
set logtraffic all
next
end

Net result: a wrong user is not in the group and fails, and a wrong machine has no valid device certificate and does not carry the tag, so it is denied. That is your pinning.

Now to your MAC point specifically, because this is where it gets interesting. ZTNA does use MAC, just not as something you type in and bind. When the FortiGate syncs a posture tag from EMS, it resolves that tag into dynamic address objects for the tagged endpoints in both IP form and MAC form. In the ZTNA traffic log you see this directly, the client device tags appear twice, once normally and once with a MAC_ prefix, which are the MAC-form resolved addresses of the registered clients. So the endpoint MAC is represented, but it is driven by EMS registration plus the device certificate, not a manually bound MAC on the FortiGate.

So to pin access to one specific machine, create a Zero Trust tagging rule in EMS that matches only that endpoint, then require that tag in the policy with the AND logic above. The tag resolves to that one device's IP and MAC, the certificate proves it is that enrolled device, and the local user group proves it is that user.

The one hard requirement: this needs FortiClient plus EMS. The FortiGate local database on its own gives you only the user half. The FortiGate alone has no device identity mechanism for the access proxy and no native user-plus-MAC binding, so without EMS the device side cannot be done.

CFR_
New Member
June 19, 2026

We are designing a zero-trust architecture for approximately 50+ endpoints using explicit web proxy access. We do not use Active Directory or external identity providers; instead, we rely entirely on Local Users managed directly on the FortiGate and FortiClient EMS.Our Strict Security Requirement:We need to strictly enforce a 1-to-1 binding between a local user account and their specific physical machine hardware. Even if a user enters their correct local username and password, they must be completely blocked if they attempt to access the web proxy from any other system registered system, or other hardware.Additionally, we want to restrict concurrent logins so that a single local user account can only maintain one active session at a time across the network proxy.Current Infrastructure Components:FortiGate running FortiOS 7.4 (Acting as ZTNA Access Proxy / Explicit Web Proxy)FortiClient EMS (For endpoint registration, user verification, and ZTNA posture tagging)Questions for TAC Guidance:How should we configure the FortiGate ZTNA rule using set user-certificate enable and set user-id-mapping enable in FortiOS 7.4 to strictly cross-check the local username identity against the EMS client certificate when an external LDAP directory is not present?What is the recommended method on FortiClient EMS to ensure that a local user profile/verification code cannot be registered or utilized across multiple physical endpoints?Does configuring set policy-auth-concurrent 1 under system global strictly limit local explicit web proxy sessions to a single concurrent active mapping per user?Please provide the exact CLI configuration syntax and validation commands (diagnose commands) to monitor and verify this local deployment architecture.Thank you.

christian_89_
Explorer II
June 19, 2026

Before the CLI, one conceptual point that changes your whole design, because it is the gap your requirement actually hits.

In standard ZTNA the three checks are independent and ANDed: the device certificate proves the machine, the user authentication proves the user, and the posture tag proves the posture. None of them natively binds a specific user to a specific device. So with a normal policy, if user A enters correct credentials on user B's enrolled and tagged machine, it passes, because the device cert is valid, the credentials are valid, and the tag is present. That is exactly the 1-to-1 binding your requirement forbids, and out of the box ZTNA does not enforce it.

On the two settings you mentioned: I cannot find set user-certificate enable or set user-id-mapping enable as valid ZTNA settings in FortiOS 7.4. They are not real commands, they look like AI-suggested syntax, so do not design around them. The real device-identity knob is set client-cert enable on the access proxy, and the real user side is set groups plus an authentication scheme. There is no native "cross-check the local username against the EMS certificate" toggle when you have no directory.

How to actually enforce strict 1-to-1 user to device without AD:

You scope per user, using one unique EMS tag per physical machine plus one policy per user.

  1. In FortiClient EMS, create a Zero Trust tagging rule that matches only one endpoint, producing a unique tag per machine, for example DEV-alice on Alice's laptop and DEV-bob on Bob's. Use a rule criterion that is unique to that device.
  2. On the FortiGate, create one proxy policy per user that requires both that user's local group and that user's device tag, with AND logic:
config firewall proxy-policy
edit 10
set name "alice-on-alice-pc"
set proxy access-proxy
set access-proxy "ZTNA-app"
set srcintf "port1"
set srcaddr "all"
set dstaddr "all"
set groups "grp-alice"
set ztna-ems-tag "EMS1_DEV-alice"
set ztna-tags-match-logic and
set action accept
set schedule "always"
set utm-status enable
set ssl-ssh-profile "<profile>"
set logtraffic all
next
end

Repeat per user. Now Alice's credentials only pass when the session comes from Alice's tagged machine. Alice's credentials on Bob's machine carry Bob's tag, which does not match Alice's policy, so it is denied, and vice versa. With client-cert enable the device certificate proves it is that specific enrolled endpoint, not a spoof.

Be honest with yourself about the cost: for 50 plus users this is roughly 50 tags and 50 policies. That is the price of strict 1-to-1 binding when you have no directory to do user-to-device mapping for you. It works and it scales administratively, but it is per-user config.

On the EMS side of your question: EMS does not manage your FortiGate local users, so it cannot by itself enforce that a given local user is only used on one endpoint. What EMS gives you is a per-device certificate, one per registration, and control over who can register through invitations. The actual user-to-device pinning lives in the FortiGate policy design above, not in EMS.

On concurrent sessions, your instinct is right and the command is real:

config system global
set policy-auth-concurrent 1
end

With this set to 1, a user can only be authenticated from one source IP at a time, and a second authentication for the same user from another source is denied. You can override it per group or per user:

config user group
edit "grp-alice"
set auth-concurrent-override enable
set auth-concurrent-value 1
next
end

For explicit web proxy specifically, confirm the behaviour with the WAD user view rather than only the firewall auth list, since proxy auth is handled by WAD.

Validation and monitoring commands for this build:

diagnose endpoint ec-shm list      (per-endpoint: IP, MAC, EMS serial, client cert SN, registration and on-net status)
diagnose endpoint record-list (endpoints registered to the FortiGate)
diagnose firewall auth list (authenticated firewall users and their groups)
diagnose wad user list (authenticated explicit web proxy users, for the concurrency check)

Bottom line: drop the user-certificate and user-id-mapping idea, they are not real. Use client-cert enable for the device, local group plus auth scheme for the user, and a per-user tag-and-policy pair for the strict 1-to-1 binding. policy-auth-concurrent 1 covers the single-session requirement. If this grows much beyond 50 users, a real identity source or FortiAuthenticator doing the user-to-device mapping will be far less work than maintaining one policy per person.

CFR_
christian_89_
Explorer II
June 19, 2026

-

CFR_
christian_89_
Explorer II
June 19, 2026

Before the CLI, one conceptual point that changes your whole design, because it is the gap your requirement actually hits.

In standard ZTNA the three checks are independent and ANDed: the device certificate proves the machine, the user authentication proves the user, and the posture tag proves the posture. None of them natively binds a specific user to a specific device. So with a normal policy, if user A enters correct credentials on user B's enrolled and tagged machine, it passes, because the device cert is valid, the credentials are valid, and the tag is present. That is exactly the 1-to-1 binding your requirement forbids, and out of the box ZTNA does not enforce it.

On the two settings you mentioned: I cannot find set user-certificate enable or set user-id-mapping enable as valid ZTNA settings in FortiOS 7.4. They are not real commands, they look like AI-suggested syntax, so do not design around them. The real device-identity knob is set client-cert enable on the access proxy, and the real user side is set groups plus an authentication scheme. There is no native "cross-check the local username against the EMS certificate" toggle when you have no directory.

How to actually enforce strict 1-to-1 user to device without AD:

You scope per user, using one unique EMS tag per physical machine plus one policy per user.

  1. In FortiClient EMS, create a Zero Trust tagging rule that matches only one endpoint, producing a unique tag per machine, for example DEV-alice on Alice's laptop and DEV-bob on Bob's. Use a rule criterion that is unique to that device.
  2. On the FortiGate, create one proxy policy per user that requires both that user's local group and that user's device tag, with AND logic:

 

 

config firewall proxy-policy
edit 10
set name "alice-on-alice-pc"
set proxy access-proxy
set access-proxy "ZTNA-app"
set srcintf "port1"
set srcaddr "all"
set dstaddr "all"
set groups "grp-alice"
set ztna-ems-tag "EMS1_DEV-alice"
set ztna-tags-match-logic and
set action accept
set schedule "always"
set utm-status enable
set ssl-ssh-profile "<profile>"
set logtraffic all
next
end

Repeat per user. Now Alice's credentials only pass when the session comes from Alice's tagged machine. Alice's credentials on Bob's machine carry Bob's tag, which does not match Alice's policy, so it is denied, and vice versa. With client-cert enable the device certificate proves it is that specific enrolled endpoint, not a spoof.

Be honest with yourself about the cost: for 50 plus users this is roughly 50 tags and 50 policies. That is the price of strict 1-to-1 binding when you have no directory to do user-to-device mapping for you. It works and it scales administratively, but it is per-user config.

On the EMS side of your question: EMS does not manage your FortiGate local users, so it cannot by itself enforce that a given local user is only used on one endpoint. What EMS gives you is a per-device certificate, one per registration, and control over who can register through invitations. The actual user-to-device pinning lives in the FortiGate policy design above, not in EMS.

On concurrent sessions, your instinct is right and the command is real:

config system global
set policy-auth-concurrent 1
end

With this set to 1, a user can only be authenticated from one source IP at a time, and a second authentication for the same user from another source is denied. You can override it per group or per user:

config user group
edit "grp-alice"
set auth-concurrent-override enable
set auth-concurrent-value 1
next
end

For explicit web proxy specifically, confirm the behaviour with the WAD user view rather than only the firewall auth list, since proxy auth is handled by WAD.

Validation and monitoring commands for this build:

diagnose endpoint ec-shm list      (per-endpoint: IP, MAC, EMS serial, client cert SN, registration and on-net status)
diagnose endpoint record-list (endpoints registered to the FortiGate)
diagnose firewall auth list (authenticated firewall users and their groups)
diagnose wad user list (authenticated explicit web proxy users, for the concurrency check)

Bottom line: drop the user-certificate and user-id-mapping idea, they are not real. Use client-cert enable for the device, local group plus auth scheme for the user, and a per-user tag-and-policy pair for the strict 1-to-1 binding. policy-auth-concurrent 1 covers the single-session requirement. If this grows much beyond 50 users, a real identity source or FortiAuthenticator doing the user-to-device mapping will be far less work than maintaining one policy per person.

CFR_
New Member
June 19, 2026

Thanks for uodate sir,

if user A enters correct credentials on user B's enrolled and tagged machine, it passes, because the device cert is valid, the credentials are valid, and the tag is present. That is exactly the 1-to-1 binding your requirement forbids, and out of the box ZTNA does not enforce it. If it is okay.

Kindly please give me what exactly i need to do ... For doing this or alternate because our cleint need same ..

christian_89_
Explorer II
June 19, 2026

Here is the exact build, end to end. The whole trick is one unique device tag per machine, plus one policy per user, paired with AND logic. That pairing is what creates the 1-to-1 binding, and there is no way to collapse it into a single policy, because a single policy with all users and all tags would allow any user on any tagged device, which is the gap you want to close.
Step 1, enroll every endpoint in EMS. Install FortiClient, connect Zero Trust Telemetry to your EMS. EMS issues each endpoint a unique device certificate. This is your device identity.
Step 2, in EMS create one Zero Trust tagging rule per machine, so each device carries a unique tag. Name them per user, for example DEV-alice, DEV-bob. The criterion has to be unique to that one device, and since you have no AD, the practical options are the endpoint hostname (if hostnames are unique) or a small per-machine marker you deploy, such as a unique registry value or file that the tagging rule matches. One tag equals one physical machine.
Step 3, on the FortiGate connect the EMS connector and confirm the tags arrive:
config endpoint-control fctems
    edit "EMS1"
        set server <ems-ip-or-fqdn>
    next
end
Then check Policy and Objects, ZTNA, the tags tab, and confirm DEV-alice and the rest are listed.
Step 4, build the ZTNA access proxy with the device certificate check enabled. client-cert enable is what cryptographically proves the specific enrolled machine, not just its IP:
config firewall vip
    edit "ZTNA-app"
        set type access-proxy
        set extip <gateway-ip>
        set extintf "port1"
        set server-type https
        set extport 9443
        set ssl-certificate "<gateway-cert>"
    next
end
config firewall access-proxy
    edit "ZTNA-app"
        set vip "ZTNA-app"
        set client-cert enable
        config api-gateway
            edit 1
                config realservers
                    edit 1
                        set ip <internal-server-ip>
                        set port <port>
                    next
                end
            next
        end
    next
end
Step 5, local users and one group per user. Put each local user in their own group, because the policy pairs a group with a device tag:
config user local
    edit "alice"
        set type password
        set passwd <pw>
    next
end
config user group
    edit "grp-alice"
        set member "alice"
    next
end
Step 6, the authentication scheme and rule against the local database:
config authentication scheme
    edit "ZTNA-Auth-scheme"
        set method basic
        set user-database "local"
    next
end
config authentication rule
    edit "ZTNA-Auth-rule"
        set srcintf "port1"
        set srcaddr "all"
        set ip-based enable
        set active-auth-method "ZTNA-Auth-scheme"
    next
end
Step 7, the heart of it, one policy per user pairing that user's group with that user's device tag, AND logic:
config firewall proxy-policy
    edit 10
        set name "alice-on-alice-pc"
        set proxy access-proxy
        set access-proxy "ZTNA-app"
        set srcintf "port1"
        set srcaddr "all"
        set dstaddr "all"
        set groups "grp-alice"
        set ztna-ems-tag "EMS1_DEV-alice"
        set ztna-tags-match-logic and
        set action accept
        set schedule "always"
        set utm-status enable
        set ssl-ssh-profile "<profile>"
        set logtraffic all
    next
end
Repeat steps 5 and 7 for every user, changing only the user, the group, the tag, and the policy id. Alice on Bob's machine fails because Bob's machine carries DEV-bob, not DEV-alice, so Alice's policy does not match and she is denied.
Step 8, the single session limit:
config system global
    set policy-auth-concurrent 1
end
Step 9, validate:
diagnose endpoint ec-shm list
diagnose endpoint record-list
diagnose firewall auth list
diagnose wad user list
On scale, because this is the part that will hurt. For 50 plus users this is 50 groups, 50 tags, and 50 policies. Two ways to handle that:
Build it as a CLI template and generate the 50 blocks in a text editor, then paste them in, or push them with FortiManager. The config is identical per user except for four fields, so it scripts cleanly.
Or, the cleaner alternate if you expect this to grow or change often, put FortiAuthenticator in the design as your identity source and do the user-to-device or user-to-MAC binding there centrally. FortiAuthenticator supports binding a user to a specific MAC, so you maintain one record per user in one place instead of one policy per user on the FortiGate. That is the scalable answer to the same requirement, at the cost of adding FAC. With pure FortiGate local users and no directory, the per-user policy method is the only native way, and it works, it just does not scale gracefully.
So in one line: unique EMS tag per machine, one local group and one proxy policy per user pairing group AND tag with AND logic, client-cert enable for the device proof, policy-auth-concurrent 1 for the single session. If the per-user policy count is unacceptable, move the binding into FortiAuthenticator.

CFR_
sjoshi
Staff
Staff
June 20, 2026

You can use ZTNA tag,

If those endpoints has specific ztna tag along with correct local user auth then only allow the access

https://docs.fortinet.com/document/fortigate/7.0.0/ztna-architecture/145655/ztna-telemetry-tags-and-policy-enforcement

Thanks, Salon
New Member
June 26, 2026

thanks all