Having issues with enforcing MFA on MacOS Tunneler

Hey all, I am very confused on MFAs in the MacOS tunneler.

Using Posture Checks with Service Policies

In the following setup here, I used Posture Checks on my DIAL services:

# Setup posture check of MFA
ziti edge create posture-check mfa global-mfa-check \
  -a "mfa-required" \
  -w -u \
  -s 28800
  
# Apply to all DIAL services
ziti edge update service-policy dev-dial --posture-check-roles "#mfa-required"
ziti edge update service-policy corp-dial --posture-check-roles "#mfa-required"
ziti edge update service-policy prod-dial --posture-check-roles "#mfa-required"

When I do this, I enter my MFA but I see "FAIL (MFA)". Why is this a reason this is occurring?

Using Auth Policies

When I setup an MFA Auth Policy for the identities, if I try to enable MFA on the MacOS Tunneler for the 1st time it errors with the following:

If I ask my developer to 1st to enroll on MFA THEN add the Auth policy to the identity. It works. Why are there these issues occurring?

Thanks for letting us know, @MacFee. I'll take a look at this today. Meanwhile could you please DM me the Packet Tunnel log from the user's system? You can get it from the "Z" menu in the macOS menu bar -> Logging -> Packet Tunnel...

Also is this problem specific to the macOS tunneler, or have you only tried it on macOS so far?

Thanks!

I've only tried MacOS since our developers primarily use Macbooks.

Posture Check with SPs

I tried finding logs for this but really couldn't find anything out of the ordinary. I'll dip more into this one.

Auth Policy error from Packet Tunnel:

This is for a fresh developer that attains a new .jwt token, that has the Auth Policy, and clicks on the MFA button to be brought up with the IPC Error.


(66779)[2026-06-02T18:27:17.055Z]    WARN ziti-sdk:ziti_ctrl.c:725 verify_api_session() ctrl[https://ziti-corp.ctlr.foo.com:1280] no API session
(66779)[2026-06-02T18:27:17.055Z]   ERROR ziti-sdk:auth_queries.c:155 ziti_mfa_enroll_get_internal_cb() ztx[1] error during enroll MFA call: 0 - UNAUTHORIZED - no api session token set for ziti_controller
[2026-06-02T18:27:47:523Z]    INFO PacketTunnelProvider:PacketTunnelProvider.swift:384 logNetworkPath() Network Path Update:
Status:satisfied, Expensive:false, Cellular:false, DNS:true
   Interfaces: 
     14: name:en0, type:wifi 
     14: name:en0, type:wifi 
     30: name:utun10, type:other
[2026-06-02T18:27:47:550Z]    WARN PacketTunnelProvider:PacketTunnelProvider.swift:330 getUpstreamDns() No fallback DNS configured. Setting to first resolver: 192.168.1.1
[2026-06-02T18:27:47:550Z]    INFO PacketTunnelProvider:PacketTunnelProvider.swift:369 startNetworkMonitor() Setting fallback DNS to 192.168.1.1
(66779)[2026-06-02T18:27:47.550Z]    INFO tunnel-cbs:ziti_dns.c:273 ziti_dns_set_upstream() DNS upstream[1] is set to 192.168.1.1:53

Thanks for following up.

I started looking at the posture check setup. I'm not sure if I did exactly the same thing that you did so I'll repeat my steps here:

  1. create a service, with associated dial and bind policies
  2. update the dial policy with mfa posture check:
    ziti edge update service-policy nc-dial --posture-check-roles "#mfa-required"
  3. create an identity:
    ziti edge create identity qs1 -o ~/nc1.jwt
  4. enroll the identity with Ziti Desktop Edge for macOS. the identity connects, but MFA is not yet enabled for the identity.
  5. toggle the MFA button for the identity, and enter the initial MFA code. the identity is now successfully connected with MFA, but no services have been assigned yet.
  6. assign the service to the identity: ziti edge update identity qs1 -a nc-dialers
  7. Ziti Desktop Edge for macOS pops up a notification saying "failed posture check(s) for service 'nc'"
  8. attempting to refresh MFA with a correct code fails with "an MFA enrollment already exists"
  9. remove the service attribute from the identity: ziti edge update identity qs1 -a ""
  10. refreshing MFA is successful once again, but of course the service that the identity wants access to is unavailable.

So the posture check fails even though the MFA was initially successful, and furthermore it is no longer possible to refresh MFA when the service with the posture check is assigned.

I'm sure this is a bug. I'm running out of time for today but I'll do some digging and send an update tomorrow. I also need to look into the auth policy issue that you described.

Thanks. That is exactly what occurs from the Posture Checks side. I'm able to enter the MFA token but still receive the "FAIL (MFA)" once I input the posture-check-role onto the DIAL policy.

I'm pretty sure this is a bug in MacOS Tunneler specifically.

Hello @MacFee

I dug a little deeper on the posture check setup.

  1. I did not have the problem when I used a 1.6.x controller. I'm not sure if that's an option for you.
  2. I wrote up an issue to cover this: MFA auth is not possible with 2.0 controller when service policy includes MFA posture check · Issue #1061 · openziti/ziti-sdk-c · GitHub

I'll get back to looking at the auth policy issue later today or tomorrow.

Thanks!

We are using the latest version of controller and edge router being v2.0.0. I just began the implementation of Ziti and the latest used is v2.0.0. I'll see on downgrading when I have the chance to see if the issue persists.

I was able to get a closer look at using an auth policy that requires TOTP, and I see the same problem that you describe ("Unable to parse enrollment response message").

This looks like a bug in the underlying SDK that Ziti Desktop Edge for macOS uses for interacting with the OpenZiti controller. Specifically, the SDK requires the identity have a session with the controller before allowing MFA to be enabled, but the auth policy requires TOTP so a session is not possible. For now I wrote up MFA enrollment is not possible when auth policy requires TOTP · Issue #1062 · openziti/ziti-sdk-c · GitHub to track this.

Thanks for the deep dive on these 2 issues @scareything. I have 2 questions:

  1. Would you know the timeline to have these issues resolved? MFA is something we want to establish for our company following SOC2 compliance and this will be a damper in enforcing MFA.
  2. Would it be best to downgrade from v2.0.0 to v1.6.16?