JDunphy-2FA-ActiveSync
Purpose
How Zimbra’s mailboxd (Jetty-based Java backend) interacts with its SOAP API—whether it uses internal Java methods directly or routes calls through its own public SOAP endpoints. Exploring the differences between Zimbra’s official ActiveSync module and Z-Push, especially around 2FA handling. Finally, whether a Jetty connector/bridge could enhance Z-Push to handle 2FA and improve performance for long-polling behavior in ActiveSync.
Integrating Zimbra Mailboxd with SOAP and ActiveSync – Zimbra vs Z-Push
Overview of Zimbra Mailboxd and SOAP API Interaction
Zimbra Mailboxd Architecture: Zimbra’s mailboxd is a Java-based server process (running on a Jetty servlet container) that hosts all core mailbox services. Within mailboxd, Zimbra deploys a “service” web application which handles user data requests (mail, contacts, calendar, etc.) via SOAP and REST APIs, plus other protocols like CalDAV, CardDAV, and Zimbra Mobile Sync (ActiveSync) (Troubleshooting Course Content Rough Drafts-Zimbra Architecture Component Overview - Zimbra :: Tech Center) (Troubleshooting Course Content Rough Drafts-Zimbra Architecture Component Overview - Zimbra :: Tech Center). In other words, mailboxd itself is the component that implements the SOAP web service endpoints and related services.
Internal API vs. SOAP Loopback: Since mailboxd is the server that provides the SOAP API, it does not normally call its own SOAP interface over HTTP when performing internal operations. Instead, mailboxd uses native Java methods and internal APIs to carry out mailbox actions directly. For example, a SOAP CreateMessageRequest
is handled by Java controller logic which invokes lower-level Mailbox Java classes – there’s no need for an HTTP SOAP call to itself. This design avoids unnecessary overhead; mailboxd acts on requests directly rather than “looping back” through a public SOAP URL.
In practice, the SOAP interface is mainly for external clients or components. Internal modules of mailboxd (like the web client handler or the mobile sync handler) have direct access to the same backend logic that SOAP calls would trigger. Thus, they bypass the network serialization step entirely. There are some special cases (e.g. in multi-server environments) where one mailbox server might call SOAP on another server for cross-mailbox operations, but within a single mailboxd process, the interactions stay in-process via Java method calls.
Evidence of Internal Use: For instance, Zimbra’s native ActiveSync module is built into mailboxd and recognized as a distinct protocol internally. It does not use SOAP to talk to the mailbox – it calls the Java API directly. A Zimbra engineer notes that when documentation refers to ActiveSync support working with app passwords, it’s referring to Zimbra’s native ActiveSync (“proto activesync”) and not an external program like Z-Push that interacts via SOAP (Z-Push using Application Passcode is not working as expected · Issue #7 · maldua-suite/zimbra-maldua-2fa · GitHub). This implies the built-in ActiveSync is tightly integrated, whereas external solutions must use the SOAP web services. In short, mailboxd’s Jetty server and SOAP engine act as the controller; internal features invoke mailbox operations directly, rather than issuing SOAP requests back into the system.
(In Zimbra’s installation, a special internal admin account (the “zimbra” user) is created for any necessary inter-service SOAP calls, but typical mailbox operations within the server don’t require looping back through SOAP.)
Zimbra Native ActiveSync vs. Z-Push (PHP) – Implementation and 2FA
ActiveSync Implementations: Zimbra Network Edition includes a native ActiveSync (EAS) module implemented in Java and running inside mailboxd (often called Zimbra Mobile). In contrast, Z-Push is an open-source ActiveSync server written in PHP, often used with Zimbra Open Source Edition to provide EAS support. Z-Push is not part of Zimbra itself; it communicates with Zimbra as an external client, usually via Zimbra’s SOAP API or IMAP/REST interfaces (the Zimbra backend for Z-Push primarily uses SOAP). Below is a comparison of the two approaches:
Aspect | Zimbra Native ActiveSync (NE) | Z-Push ActiveSync (PHP) |
---|---|---|
Implementation | Java module within mailboxd (Jetty). Runs as a servlet/handler inside Zimbra’s server process. | PHP web application (e.g. on Apache/NGINX+PHP-FPM). External to Zimbra; uses Zimbra APIs to fetch/send data. |
Integration Level | Deeply integrated – uses Zimbra’s internal Java APIs to access mailbox data (no SOAP calls needed). Logging and control are part of Zimbra (e.g. logs to sync.log on the server) (Ajcody-Logging - Zimbra :: Tech Center).
|
External – communicates over SOAP/HTTP (XML-over-HTTP) as a client. Essentially acts like an Outlook client would, but translating ActiveSync to Zimbra SOAP. |
Authentication | Handled by Zimbra’s standard auth mechanisms inside mailboxd. Accepts basic auth from devices and verifies credentials via internal methods. Has full access to Zimbra’s authentication context (including 2FA logic, tokens, etc.). | Must authenticate to Zimbra via SOAP (AuthRequest). Typically uses the user’s username/password on each sync request or reuses a Zimbra auth token. Authentication is subject to SOAP’s rules, and Z-Push has to handle failures or tokens in PHP. |
Two-Factor Auth Support | Supported via App Passwords: Zimbra’s 2FA (two-step auth) feature treats ActiveSync as a “non-interactive” protocol. Users with 2FA generate an app-specific password and use that on their device. The native EAS module recognizes the ActiveSync protocol and allows app passwords in place of the second factor (Z-Push using Application Passcode is not working as expected · Issue #7 · maldua-suite/zimbra-maldua-2fa · GitHub) (Z-Push using Application Passcode is not working as expected · Issue #7 · maldua-suite/zimbra-maldua-2fa · GitHub). (The device does not handle TOTP codes; Zimbra simply requires the app password for EAS/IMAP/POP.) | Limited/Problematic: Because Z-Push logs in via SOAP, it falls under “SOAP/HTTP” rules – and by default Zimbra does not accept app passwords on SOAP logins (Z-Push using Application Passcode is not working as expected · Issue #7 · maldua-suite/zimbra-maldua-2fa · GitHub). If 2FA is enabled on an account, a SOAP AuthRequest expects the real password + TOTP code (or a pre-obtained auth token). Standard ActiveSync clients can’t perform that multi-step login. Result: Z-Push fails to authenticate if 2FA is required. The recommended workaround is to disable 2FA for accounts using Z-Push or use a custom solution (GitHub - maldua-suite/zimbra-maldua-2fa: Two Factor Authentication for Zimbra FOSS). (E.g. community 2FA extensions for Zimbra OSE explicitly state Z-Push is not supported with 2FA enabled (GitHub - maldua-suite/zimbra-maldua-2fa: Two Factor Authentication for Zimbra FOSS).) |
Protocol Support | Generally supports a range of ActiveSync policy features and versions that Zimbra has implemented. (E.g. Zimbra NE ActiveSync in recent versions supports at least EAS 14.x. Newer versions might support additional policies like remote wipe, etc.) | Supports whatever EAS versions Z-Push provides (commonly EAS 14.0 or 14.1). Z-Push’s Zimbra backend reports protocol version 14 by default (Z-Push Zimbra Backend - Browse /Release54 at SourceForge.net). Feature support (e.g. notes sync, SMS sync) depends on Z-Push’s roadmap and the backend capabilities (Z-Push Zimbra Backend - Browse /Release54 at SourceForge.net) (Z-Push Zimbra Backend - Browse /Release54 at SourceForge.net). Some advanced features might not be fully available. |
Administration & Policies | Managed via Zimbra Admin Console/CLI. Admins can toggle mobile policies, device wipe, sync settings per COS/account. (These apply to the native EAS only.) | No direct integration with Zimbra admin tools. Policies would be those provided by Z-Push (which has its own config for max items, attachment size, etc., but not Zimbra COS-driven). Device management (wipes, etc.) must be done through Z-Push’s admin tool, not Zimbra’s. |
Performance | Optimized in-process. Can directly subscribe to mailbox notifications for new mail (for push) and access data in memory or local store quickly. Fewer data transformations (uses Java objects -> ActiveSync WBXML). Overall, tends to be efficient and scalable with the Zimbra server’s threading model. | Adds overhead due to being external: each sync involves SOAP XML serialization, network overhead (even if localhost), and translation in PHP. Caching strategies in Z-Push mitigate some overhead. Performance also depends on PHP execution and backend calls. Generally uses more CPU and memory per request on the Zimbra side (SOAP parsing) and on the Z-Push side (PHP processing). |
2FA Handling in Detail: The most significant difference above is two-factor authentication. In Zimbra NE’s built-in module, when a user has 2FA enabled, they are instructed to use an application-specific password for ActiveSync clients (since phones can’t prompt for TOTP codes) (Z-Push using Application Passcode is not working as expected · Issue #7 · maldua-suite/zimbra-maldua-2fa · GitHub) (Z-Push using Application Passcode is not working as expected · Issue #7 · maldua-suite/zimbra-maldua-2fa · GitHub). Zimbra’s auth code knows that for ActiveSync (and other non-web protocols) it should check the app password list instead of expecting a TOTP. The code snippet below (from Zimbra’s AuthMechanism) shows that SOAP and HTTP basic auth are explicitly excluded from app password checks, whereas other protocols will accept an app password if 2FA is required (Z-Push using Application Passcode is not working as expected · Issue #7 · maldua-suite/zimbra-maldua-2fa · GitHub) (Z-Push using Application Passcode is not working as expected · Issue #7 · maldua-suite/zimbra-maldua-2fa · GitHub):
<syntaxhighlight lang="java">if (twoFactorAuthRequired && authCtxt != null) {
// if 2FA enabled, check non-HTTP protocols for app-specific passwords Protocol proto = (Protocol) authCtxt.get("proto"); switch(proto) { case soap: case http_basic: break; // (for SOAP/HTTP, proceed to require actual 2FA code flow) default: if (appPasswords.isEnabled()) { appPasswords.authenticate(password); // verifies app password authDone = true; } else { throw AuthFailedServiceException.AUTH_FAILED(...); } }
}</syntaxhighlight>
Effect: A login via native ActiveSync (proto “sync”) hits the default
case – the given password is checked against the user’s app passwords and can succeed without a TOTP (Z-Push using Application Passcode is not working as expected · Issue #7 · maldua-suite/zimbra-maldua-2fa · GitHub). A login via SOAP, however, falls into the case soap
branch (no app password check) and would normally trigger a 2FA required response. This is why Z-Push cannot simply use an app password: from Zimbra’s perspective it’s a SOAP client, and it will reject the app password as an “invalid password” in a 2FA scenario (Z-Push using Application Passcode is not working as expected · Issue #7 · maldua-suite/zimbra-maldua-2fa · GitHub). Unless Z-Push were modified to perform the two-step SOAP Auth (which isn’t feasible with standard EAS clients), the only viable approach is disabling 2FA on that account or using Zimbra NE’s built-in EAS. In fact, the community 2FA zimlet for OSE flatly states: “Z-Push is not supported … You will only be able to use Z-Push if you disable 2FA for that account.” (GitHub - maldua-suite/zimbra-maldua-2fa: Two Factor Authentication for Zimbra FOSS).
Feature and Usage Differences: Aside from 2FA, administrators will notice other differences. The native EAS logs to Zimbra’s logs (sync.log
, synctrace.log
, etc.), and issues like device sync errors can be monitored there. Z-Push maintains its own logs (usually in /var/log/z-push/
), and troubleshooting often involves both Z-Push and Zimbra logs (e.g. SOAP errors in mailbox.log). Z-Push’s PHP nature means it may require tuning PHP, web server, and it stores sync state on disk (or in memcache/DB if configured) – those are external to Zimbra’s own store. By contrast, Zimbra’s built-in ActiveSync leverages the mailbox’s data and state directly (it doesn’t need to persist sync state externally because it can query the mailbox for changes via internal APIs).
In summary, Zimbra’s native ActiveSync is more seamless (but requires Network Edition or a module like ZeXtras Mobile in some OSE setups), whereas Z-Push is a clever workaround to bring EAS to Zimbra OSE, at the cost of additional complexity. They handle 2FA very differently: the native approach works with Zimbra’s 2FA/app passwords out-of-the-box, while Z-Push effectively bypasses 2FA (or needs a custom extension) (GitHub - maldua-suite/zimbra-maldua-2fa: Two Factor Authentication for Zimbra FOSS).
Jetty Integration for Z-Push – Connectors and Bridges for Authentication
One idea to improve Z-Push and Zimbra integration is to run Z-Push “inside” the Zimbra server or proxy, using Jetty as a front-end. This can be done by configuring Jetty (which runs mailboxd) to forward ActiveSync requests to Z-Push, or even serve Z-Push via a CGI/bridge. In fact, community members have created proof-of-concept Jetty connectors that do exactly this:
- Deploying Z-Push on Jetty: Zimbra’s Jetty can be configured to serve a new webapp for Z-Push. For example, one approach is to place the Z-Push PHP code under Jetty and use Jetty’s CGI servlet to execute the PHP interpreter. The ActiveSync endpoint (
/Microsoft-Server-ActiveSync
) is then re-routed to this Z-Push webapp instead of Zimbra’s built-in handler (How to run z-push on zimbra jetty: Proof of Concept - Zimbra Forums) (Configuring Exchange ActiveSync with Autodiscover on Zimbra | by Omar Khalil | Medium). In essence, Jetty acts as the web server for Z-Push, allowing it to share the same domain and port as Zimbra. - Disabling Native EAS: The Jetty config is adjusted to override the mapping for ActiveSync. Zimbra’s default Jetty setup maps the ActiveSync URL to its internal sync servlet (
/service/extension/zimbrasync
). By editingjetty.xml.in
, one can redirect that path to the Z-Push script. As one guide describes, “tell Jetty to use Z-Push when.../Microsoft-Server-ActiveSync
is being requested instead of its native EAS.” (Configuring Exchange ActiveSync with Autodiscover on Zimbra | by Omar Khalil | Medium) This typically involves commenting out or altering the Jetty context for/Microsoft-Server-ActiveSync
to point to/z-push/
(where anindex.php
handles the request). - Advantages of a Jetty Bridge: Running Z-Push on the mailbox server can improve performance and integration. There’s no extra network hop between the device and Z-Push, and if on the same host, Z-Push’s SOAP calls to Zimbra can go to
localhost
(fast and secure). It also means a single HTTPS endpoint (Zimbra’s) serves both webmail and ActiveSync. This can simplify SSL configuration and allow reuse of Zimbra’s proxy if present. Additionally, by being on Jetty, we could potentially leverage Zimbra’s AuthToken system: for example, in theory Jetty could authenticate the HTTP Basic auth itself and pass a valid Zimbra auth token or user identity to Z-Push (though this would require custom development). Such a mechanism could let Z-Push skip the SOAP Auth step, or allow app passwords to work by handling auth in Java before invoking PHP. - 2FA via a Jetty Mediator: One could imagine implementing a Jetty filter or servlet that intercepts ActiveSync logins. This filter (in Java) would read the Authorization header from the device, then call Zimbra’s internal auth logic (which would honor app passwords for ActiveSync protocol) to authenticate. If successful, it could inject the user’s AuthToken or set the
REMOTE_USER
for the CGI, thereby logging the user in as if they were already authenticated to Zimbra. Z-Push’s backend could then use the provided AuthToken to make mailbox requests (Zimbra’s SOAP API accepts an AuthToken in the header). This arrangement would effectively bridge the gap so that 2FA-enabled accounts can use Z-Push: the Jetty layer deals with the 2FA/app password, and Z-Push just gets a session token. While this isn’t available out-of-the-box, it’s technically feasible via Zimbra’s extension framework and Jetty config. (It’s worth noting a contributor’s idea: to create a custom SOAP endpoint or extension that could allow Z-Push to supply a 2FA code or adjust the protocol context (Z-Push using Application Passcode is not working as expected · Issue #7 · maldua-suite/zimbra-maldua-2fa · GitHub) (Z-Push using Application Passcode is not working as expected · Issue #7 · maldua-suite/zimbra-maldua-2fa · GitHub) – however, these require coding on Zimbra’s side.) - Complexity and Support: The Jetty integration of Z-Push is still essentially a workaround. It requires manual changes to Zimbra’s config (which may need re-applying after upgrades) and is not officially supported. One must also ensure PHP and required libraries are present on the Zimbra server for the CGI approach. Despite these hurdles, community documentation shows it’s doable and has been used to unify the solution (How to run z-push on zimbra jetty: Proof of Concept - Zimbra Forums) (How to run z-push on zimbra jetty: Proof of Concept - Zimbra Forums). If implementing such a bridge, careful testing of authentication, performance, and upgrade impact is needed.
In summary, a Jetty connector can make Z-Push feel more like a part of Zimbra: same URL, potentially shared authentication context, and possibly more efficient internal calls. It doesn’t rewrite Z-Push in Java, but it can mitigate some PHP limitations. For example, Jetty is better at handling long-lived requests than certain Apache/PHP setups; using Jetty’s async IO for the ActiveSync “Ping” could reduce load on PHP (one could offload the wait to Jetty and only wake PHP when there’s data – though this would require substantial custom integration logic).
Optimizing Z-Push Performance (Push/Polling and Resource Use)
Z-Push’s default mode of operation uses long-polling to emulate “Direct Push” for ActiveSync. A mobile device issues a Ping
request, which the server (Z-Push) holds open until a change occurs or a timeout is reached. This design can strain resources, especially in PHP environments. Here are ways to optimize Z-Push with Zimbra:
- Use Zimbra’s WaitSet (Push) API: Zimbra provides a SOAP mechanism called WaitSet for efficient push notifications. The Z-Push Zimbra backend indeed leverages this – it opens a persistent SOAP WaitSet request to listen for changes on the user’s mailbox (Z-Push Zimbra Backend / Support Requests / #183 Z-Push creating too many SOAP requests from a single device). This is far more efficient than polling every folder repeatedly. Ensure that your Z-Push backend is configured to use WaitSets (most recent Zimbra backend versions do by default). This reduces CPU and SOAP traffic because Zimbra will notify when new mail or updates arrive. Note: There were some bugs in older Zimbra versions (e.g. a deadlock bug in pre-7.2.1) related to WaitSets (Z-Push Zimbra Backend - Browse /Release54 at SourceForge.net), but those have long been fixed. If you see errors like “WaitSet not found” in Z-Push logs (Z-Push Zimbra Backend / Support Requests / #183 Z-Push creating too many SOAP requests from a single device) (Z-Push Zimbra Backend / Support Requests / #183 Z-Push creating too many SOAP requests from a single device), it may indicate the WaitSet is getting lost (possibly due to network timeouts or Zimbra reboots). Upgrading Zimbra and Z-Push to latest versions and applying any backend patches can help stability.
- Adjust Timeout and Process Handling: By default, Z-Push might keep a
Ping
open for e.g. 60 seconds. Longer ping intervals reduce the frequency of new requests (good), but mean PHP processes stay busy longer (potentially bad if many concurrent users). Tuning the ping interval and the PHP max execution time is important. For instance, you might allow PHP to handle a 2-minute execution time so it can hold the connection, but also configure PHP-FPM with enough workers or use a memory-efficient mode to handle many simultaneous long polls. The Jetty integration approach could allow even longer wait times by relying on Jetty’s threads to hold the connection, waking PHP only on events. - Optimize PHP Environment: Use PHP-FPM (with a proper process manager) instead of mod_php, so that each Z-Push request is an isolated PHP process that can block without halting the web server. Configure PHP-FPM with a suitable number of child processes/threads to handle your user count. Monitor for issues like “too many PHP threads” or memory bloat if Ping requests accumulate. If you find the server is spawning many threads for idle connections (z-push creates php threads (not processes) until server crashes | Kopano Community Forum) (z-push creates php threads (not processes) until server crashes | Kopano Community Forum), consider lowering the maximum ping duration or tweaking timeouts so that connections refresh periodically (to avoid PHP from holding resources too long). Also ensure
pcntl
is disabled or used correctly if Z-Push employs it (to avoid any forked process leaks). - Zimbra DoS Filter and Throttling: Zimbra mailboxd has a built-in DoS filter that can interpret a flurry of requests as a potential attack and start dropping connections. A busy Z-Push (especially if misconfigured and opening/closing SOAP sessions rapidly) might trigger this. For example, if a device kept re-establishing a broken WaitSet, you’d see many SOAP calls and possibly messages like “SOAP access limited” or HTTP 503 errors. To prevent Z-Push from being throttled by Zimbra, you can raise or whitelist the Z-Push server in Zimbra config. Common tunings include:
- Increasing
zimbraHttpDosFilterMaxRequestsPerSec
(or disabling the DoS filter during initial sync storms). - Adding the Z-Push server’s IP to
zimbraMailTrustedIP
andzimbraHttpThrottleSafeIPs
. This tells Zimbra to not throttle that source (Z-Push Zimbra Backend / Support Requests / #183 Z-Push creating too many SOAP requests from a single device). For example, one user increased max requests and marked Z-Push’s IP as trusted to resolve SOAP limit exceptions (Z-Push Zimbra Backend / Support Requests / #183 Z-Push creating too many SOAP requests from a single device). Note: Only do this for a controlled internal connection (like localhost or a known proxy server), since it disables protective throttling.
- Increasing
- Caching and State Optimization: The Zimbra backend of Z-Push introduced a message list cache to reduce server load (Z-Push Zimbra Backend - Browse /Release54 at SourceForge.net). Ensure this is enabled (it typically is by default). This cache means if a client issues back-to-back sync requests for the same folder, Z-Push can avoid fetching the entire folder list via SOAP repeatedly. The cache lifetime (default 3600s) can be tuned via
ZIMBRA_LOCAL_CACHE_LIFETIME
in Z-Push config (Z-Push Zimbra Backend - Browse /Release54 at SourceForge.net). If you have plenty of memory, you might keep this higher; if you prefer fresher data always, lower it (at cost of more SOAP calls). Also, store Z-Push state on a fast storage – if using the file state machine, ensure the disk is not slow. For larger deployments, consider the memcached or SQL state backend for Z-Push to speed up read/writes of sync state (this reduces I/O latency). - Concurrency and Multi-Folder Sync: ActiveSync PING can watch multiple folders, but some older Z-Push backends might handle certain folders (Inbox, Sent, etc.) differently. Make sure you’re using a Z-Push backend version that can include all needed folders in one WaitSet to avoid having separate long polls for different folders. Newer Z-Push versions usually do combine them. This maximizes efficiency by using one SOAP waitset for all relevant folders per device.
- Networking Optimizations: Since Z-Push is talking SOAP to Zimbra, enabling HTTP keep-alive and even compression can help. Z-Push’s SOAP client (often uses cURL or PHP streams) should reuse connections when possible. Verify that it’s connecting to Zimbra over HTTPS on localhost (to avoid any network latency). If using a proxy or in a multi-server environment, ensure the path between Z-Push and mailboxd is fast and secure.
- Periodic Review of Logs: Keep an eye on Z-Push logs in verbosity 1 or 2 for any recurring errors or performance issues. For instance, if you see frequent “NO_SUCH_WAITSET” errors (Z-Push Zimbra Backend / Support Requests / #183 Z-Push creating too many SOAP requests from a single device) (Z-Push Zimbra Backend / Support Requests / #183 Z-Push creating too many SOAP requests from a single device), it indicates the waitset is not being maintained between requests – possibly a bug or misconfiguration causing Z-Push to not resume the waitset properly. Updating to the latest Z-Push Zimbra backend (many fixes have been added up to Release 68+ on SourceForge, etc.) can resolve such issues. In one case, a fix was made to handle Jetty 503 errors gracefully in the Z-Push code (Z-Push Zimbra Backend - Browse /Release68 at SourceForge.net), so staying current is key.
In conclusion, Z-Push performance can approach native efficiency with careful tuning, but it will always incur more overhead than the built-in ActiveSync due to the extra translation layer. Running Z-Push on the same server (or within Jetty) eliminates network lag and can let you take advantage of Zimbra’s robust server infrastructure (one less moving part). By using Zimbra’s push notification APIs (WaitSet), caching results, and adjusting server safeguards, you can significantly improve responsiveness and resource usage. Just be mindful that heavy usage might still require more memory or PHP workers compared to native EAS, and plan your server capacity accordingly.
References
- Zimbra Architecture and integration of services (Troubleshooting Course Content Rough Drafts-Zimbra Architecture Component Overview - Zimbra :: Tech Center) (Troubleshooting Course Content Rough Drafts-Zimbra Architecture Component Overview - Zimbra :: Tech Center)
- Community discussion on ActiveSync vs external SOAP (Z-Push) in context of app passwords (Z-Push using Application Passcode is not working as expected · Issue #7 · maldua-suite/zimbra-maldua-2fa · GitHub) (Z-Push using Application Passcode is not working as expected · Issue #7 · maldua-suite/zimbra-maldua-2fa · GitHub)
- Zimbra authentication code showing 2FA vs app password handling for different protocols (Z-Push using Application Passcode is not working as expected · Issue #7 · maldua-suite/zimbra-maldua-2fa · GitHub) (Z-Push using Application Passcode is not working as expected · Issue #7 · maldua-suite/zimbra-maldua-2fa · GitHub)
- Zimbra OSE 2FA extension notes on Z-Push incompatibility (GitHub - maldua-suite/zimbra-maldua-2fa: Two Factor Authentication for Zimbra FOSS)
- Example of rerouting Zimbra’s Jetty to use Z-Push (disabling native EAS) (Configuring Exchange ActiveSync with Autodiscover on Zimbra | by Omar Khalil | Medium)
- Z-Push backend using Zimbra WaitSet for push notifications (Z-Push Zimbra Backend - Browse /Release54 at SourceForge.net) (Z-Push Zimbra Backend / Support Requests / #183 Z-Push creating too many SOAP requests from a single device)
- Z-Push performance tuning (DoS filter and trusted IP config) (Z-Push Zimbra Backend / Support Requests / #183 Z-Push creating too many SOAP requests from a single device)
- Z-Push Zimbra backend changelog (caching implementation for better performance)
Clarification
To clarify explicitly:
No, the integration alone (Jetty or Nginx forwarding requests to Z-Push) does not inherently fix the 2FA issue for Z-Push.
The critical issue is this:
- Zimbra’s built-in ActiveSync accepts application-specific passwords because mailboxd identifies those requests internally as ActiveSync protocol (
proto=activesync
). Hence, app passwords are allowed. - Z-Push, by contrast, logs into Zimbra using the SOAP API, identified internally as
proto=soap
. Zimbra explicitly does not allow application-specific passwords for SOAP logins when 2FA is enabled, as it expects a full 2FA process (regular password + second factor).
Simply routing Z-Push through Jetty or Nginx doesn’t change how Z-Push authenticates—it will still make SOAP AuthRequests, and Zimbra will continue to reject application-specific passwords in a 2FA scenario.
Could Jetty/Nginx integration help with 2FA at all?
Only if you implement additional custom logic or middleware at the Jetty or Nginx layer, such as:
- Jetty Servlet Filter or Authentication Bridge:
You could create a custom Java servlet/filter within Jetty to intercept ActiveSync login requests (from devices), authenticate directly against Zimbra’s internal auth APIs (which do support application-specific passwords), and then pass a valid Zimbra authentication token to Z-Push. In other words, this middleware would authenticate on behalf of Z-Push, bypassing the standard SOAP authentication issue.
- Custom Nginx module (less feasible):
A similar concept could be applied via an Nginx module that pre-authenticates the request (though this would be significantly harder, as Nginx isn’t Java and doesn’t have direct access to Zimbra internals).
Without custom code or extensions, however, merely running Z-Push on Jetty or behind Nginx will not solve the fundamental limitation of Zimbra’s SOAP API with respect to 2FA and application passwords.
In short:
- Native ActiveSync (Zimbra built-in):
✅ Works fine with 2FA and app-specific passwords.
- Z-Push via standard SOAP login:
❌ Fails with app-specific passwords if 2FA is enabled.
- Z-Push via Jetty/Nginx without custom middleware:
❌ Same as standard SOAP login (no improvement).
- Z-Push via Jetty/Nginx with custom authentication middleware:
✅ Could potentially handle 2FA (app-specific passwords) correctly.
This is the nuanced explanation. To support 2FA with Z-Push, custom middleware or code extensions are necessary—mere proxying or bridging via Jetty/Nginx alone won’t do it.
More articles written by me, https://wiki.zimbra.com/wiki/JDunphy-Notes