Ferritaas Incident Intelligence
Defect Intelligence Report Bug 0894201 Severity: High (P2) Subsystem: fcnacd / ztna_eval

Zero Trust Posture Check Cache Expiration Dropping Active SSH/RDP Web Sessions

Few things rattle support escalations quite like persistent "session disconnected" errors during interactive terminal sessions or database queries. In FortiGate ZTNA environments paired with FortiClient EMS, active SSH, RDP, and long-lived WebSocket sessions are abruptly severed with a TCP RST every 15 to 30 minutes because the NAC daemon clears posture tags synchronously upon cache TTL expiry.

Technical Root Cause Analysis

Zero Trust Network Access models replace perimeter VPNs with continuous policy evaluation: user identity and endpoint posture tags (such as compliance status, OS build, active AV, or corporate domain join) are validated not just at connection setup, but continuously throughout session lifetime.

In FortiOS, the FortiClient NAC daemon (fcnacd) synchronizes posture tags from FortiClient EMS and populates local dynamic address tables in the kernel. Because querying EMS for every single TCP packet would introduce catastrophic latency, the system caches posture tags with an expiration timer (typically 900–1800 seconds).

In affected builds, when this timer expires, fcnacd executes a synchronous cache invalidation: it purges the endpoint's tag set from memory before initiating the background HTTPS query to EMS. During the 500ms to 2500ms round-trip window while waiting for EMS to respond, incoming packets for long-running TCP connections (such as SSH tunnels, RDP over ZTNA, or terminal sessions) hit the firewall access proxy policy. Because the policy requires specific tags (e.g. EMS_Compliant_Corporate) that are temporarily absent, the policy evaluation fails and FortiOS issues an immediate TCP RST to both client and server.

[fcnacd 3108] ztna_tag_cache_expire: client 10.220.14.88 [FCT-MAC: 3c:22:fb:80:a1:09] TTL expired (1800s) [fcnacd 3108] flush_dynamic_tag_entry: purged 3 tags (Corporate_Laptop, AntiVirus_Active, Low_Vulnerability) [fcnacd 3108] ems_telemetry_query_start: requesting updated tags from EMS-01 (10.100.20.15:8013)... [kernel] id=20085 trace_id=141 func=ztna_eval_policy_match:842 sub=firewall action=deny [kernel] ztna_rule="Admin-SSH-Proxy" src=10.220.14.88:51240 dst=10.100.1.50:22 proto=tcp [kernel] reason="required tag 'Corporate_Laptop' not present on client record" [kernel] action=send_rst session_id=09fa812b terminated [fcnacd 3108] ems_telemetry_query_done: received 3 tags in 1120ms; re-populating dynamic table

Firmware Trajectory: Discovered in firmware 7.2.3, an initial partial mitigation in firmware 7.4.0 simply increased the default cache duration. While this reduced drop frequency, it merely postponed the inevitable disconnect and risked allowing non-compliant machines to linger longer. Permanent verified resolution was shipped in firmware 7.2.8+ and 7.4.3+ via asynchronous "stale-while-revalidate" background polling, ensuring established sessions remain untouched while fresh telemetry is fetched.

Affected Firmware & Blast Radius Matrix

FortiOS Branch Vulnerable Builds Confirmed Clean Build Status & Workaround
FortiOS 7.2 7.2.3 – 7.2.7 7.2.8+ Apply posture grace period override
FortiOS 7.4 7.4.0 – 7.4.2 7.4.3+ Upgrade to 7.4.3+ or tune EMS keepalives
FortiOS 7.0 7.0.x Branch Clean Not Affected Legacy 7.0 EMS connector lacks synchronous cache flush

Platform Impact: Any FortiGate firewall (physical or VM) enforcing Zero Trust dynamic tag policies on ZTNA TCP Access Proxies (SSH, RDP, HTTPS) connected to FortiClient EMS 7.0 or 7.2.

Step 01: Free Verification CLI (Safe Read-Only)

Run these read-only diagnostic commands on your FortiGate CLI to inspect EMS connector health, dynamic tag cache entries, and verify whether posture tag evictions coincide with connection drops:

Diagnostic Commands

# 1. View active endpoint posture tags synchronized from FortiClient EMS
diagnose endpoint fds-tag list

# 2. Check FortiClient EMS connector synchronization status and latency
diagnose test application fcnacd 2

# 3. List active dynamic address objects populated by ZTNA tags
diagnose firewall dynamic-address list

# 4. Inspect registered client endpoint records and posture status
diagnose endpoint record list

# 5. Check fcnacd daemon memory and process health
diagnose test application fcnacd 1

Remediation & Workaround Steps (Teaser Preview)

Access the complete interactive remediation procedures inside the Ferrite platform:

Step 02: Identify Impacted ZTNA Rules & Tag Mappings

Filter firewall access proxy policies to identify rules enforcing dynamic EMS posture tags without grace intervals.

🔒 Interactive CLI Available in Ferrite Runbook #19

Step 03: Configure Posture Evaluation Grace Interval

Enable set posture-check-grace-period 300 to prevent synchronous drops during telemetry refresh windows.

🔒 Interactive CLI Available in Ferrite Runbook #19

Step 04: Surgical Recycling of EMS Connector Daemon

Re-initialize the fcnacd daemon cleanly without disconnecting active hardware-accelerated user traffic.

🔒 Interactive CLI Available in Ferrite Runbook #19
⚡ Ferrite Platform Superpowers

Execute the Complete Runbook with Live Browser Automation

Connect your FortiGate via browser console (Web Serial) or secure SSH bridge. Validate CLI steps in real-time, generate automated recovery scripts, and generate TAC P1 escalation dossiers in seconds.

Live Browser Automation Direct terminal execution with live step-by-step verification.
📟
1-Click Tera Term (.ttl) Generate pre-validated scripts for air-gapped jump hosts.
🛡️
Zero-Trust Scrubber Automatically sanitize serial numbers, IPs, and keys in browser RAM.
📄
TAC P1 Escalation Dossier Export clean diagnostic proof with SHA-256 integrity signatures.

Frequently Asked Questions

Why do active SSH and RDP sessions drop every 15 to 30 minutes over ZTNA?

The FortiOS NAC daemon (fcnacd) synchronously purges posture tags from memory when their TTL timer expires. During the 1–2 second window while awaiting fresh telemetry from FortiClient EMS, active sessions fail policy posture matching and are terminated with TCP RST.

What is the firmware trajectory for Bug 0894201?

Discovered in firmware 7.2.3, an initial partial mitigation in 7.4.0 simply increased the TTL timer. Verified permanent resolution was shipped in firmware 7.2.8+ and 7.4.3+ with asynchronous stale-while-revalidate background polling.

Is there a non-disruptive workaround while waiting to upgrade?

Yes. Setting an endpoint posture evaluation grace interval of 300 seconds prevents policy drops while EMS telemetry refreshes in the background.