Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Building a Dynamic Trust Engine for ZTNA Policies

Hello! Welcome back to our series on architecting Zero Trust Network Access.

In our last lesson, we designed the complete end-to-end connection flow for a ZTNA service. We established how ZTNA separates the control plane from the data plane, using a "connection brokering" model to grant access to specific applications without ever exposing the internal network. At the heart of that flow was the Policy Engine, the "brain" responsible for making the critical approve/deny decision.

Today, we will open up that black box. This lesson directly addresses the learning outcome: Design the ZTNA policy engine, showing how it integrates identity and device signals into a dynamic trust score for access decisions. We will architect the core logic that turns the "never trust, always verify" principle into a real-time, data-driven system.

1. The Anatomy of a Policy Decision

Before we design the engine, let's establish its place in the overall Zero Trust architecture. The NIST SP 800-207 standard provides a logical model that is essential for our design.

NIST Zero Trust Architecture Components and Policy Flow
This diagram from the NIST SP 800-207 standard illustrates the logical components of a Zero Trust Architecture. The Policy Engine (PE) is the core of the Policy Decision Point (PDP), making decisions based on data from various Policy Information Points (PIPs).

The diagram shows that the access decision process is managed by a Policy Decision Point (PDP). From an architectural standpoint, the PDP is composed of two key functions:

  • Policy Engine (PE): This is the runtime component that evaluates access requests. It processes inputs about the user, device, and resource, and computes a decision in real-time.
  • Policy Administrator (PA): This is the management component. It is responsible for creating, updating, and distributing the access policies that the PE uses. It also handles the connection to external data sources that feed information to the engine.

Let's watch a short video that clearly explains this separation of roles and the overall decision flow.

What Is Zero Trust Architecture (ZTA) ? NIST 800-207 Explained

This video, titled 'What Is Zero Trust Architecture (ZTA) ?', explains the core NIST components. It will help solidify your understanding of the relationship between the Policy Engine, Policy Administrator, and the data sources they rely on.

Please watch the segment from 04:44 to 08:29. As you watch, focus on: The decoupling of the Policy Decision Point (PDP) and Policy Enforcement Point (PEP). The further breakdown of the PDP into the Policy Engine and Policy Administrator. The types of internal and external data sources (e.g., IAM, CDM, Threat Intelligence) that the Policy Engine consumes to make a decision.

With this framework in mind, our task is to design the internals of the Policy Engine. The core function of the PE is to answer one question for every access request: "Based on everything I know right now, how much should I trust this request?"

2. Designing a Dynamic Trust Scoring Model

A simple "allow" or "deny" policy based on static roles is not enough for true Zero Trust. We need a dynamic model that can adapt to changing conditions. The most common way to architect this is by calculating a trust score (or, inversely, a risk score) for each request.

This score is an aggregation of multiple signals. A lower trust score might trigger denial or a "step-up" authentication challenge (like MFA), while a high score allows seamless access.

2.1. Gathering the Signals

First, what data does our engine need to ingest? These signals, which the NIST diagram refers to as inputs from Policy Information Points (PIPs), fall into three main categories:

  • Identity Signals:

    • Authentication Status: Has the user authenticated successfully?
    • MFA Status: Was a strong authentication method used?
    • User Roles/Attributes: What department is the user in? What are their assigned roles? (e.g., developer, finance_analyst)
    • Behavioral History: Is this login attempt from a typical time or location? Have there been recent failed login attempts?
  • Device Signals (Device Posture):

    • Device Management: Is this a corporate-managed device (via an MDM solution) or a personal device (BYOD)?
    • Security Health: Is the OS up to date? Is the disk encrypted? Is the firewall enabled? Is antivirus software running and updated?
    • Compromise Status: Is the device jailbroken or rooted? Has an Endpoint Detection and Response (EDR) agent detected any malicious activity?
  • Contextual Signals:

    • IP Reputation: Is the request coming from a known malicious IP address or an anonymizing proxy (Tor)?
    • Geolocation: Is the user attempting to access from an unusual or disallowed country? Does the request constitute "impossible travel" (e.g., login from New York ten minutes after a login from Tokyo)?
    • Resource Sensitivity: Is the user trying to access a low-risk resource (e.g., company wiki) or a high-risk one (e.g., production database)?

The following video provides an excellent overview of these factors in the context of risk-based authentication, which is the conceptual foundation for our trust scoring engine.

Risk-Based Authentication Explained

The video 'Risk-Based Authentication Explained' by IBM Technology details the various factors that contribute to a dynamic risk assessment. This directly maps to the signals our ZTNA policy engine will use.

Watch from 03:10 to 10:54. Pay close attention to the comprehensive list of factors discussed, including user behavior, device context, geolocation, IP reputation, and transaction sensitivity. Think about how you would architect a system to collect and process each of these signals.

2.2. Calculating the Trust Score

Now that we have our signals, how do we combine them into a single, quantifiable score? A robust and interpretable method is to use a weighted linear combination. This approach allows system architects to define the relative importance of each signal.

A research paper on dynamic Zero Trust provides a great formal model for this. While its focus is on Industrial IoT, the architectural principle is perfectly applicable here.

[PDF] A Context-Aware and Real-Time Dynamic Zero Trust

The paper 'A Context-Aware and Real-Time Dynamic Zero Trust' formalizes the concept of a trust score. We will adapt its model for our ZTNA engine.

Please read the following sections: Section IV: THREAT RISK (starts on page 6). Focus on the introduction and the formula in subsection 'E. Threat Risk Calculation and Categorization'. Also, briefly review subsections A, B, C, and D to understand how each criterion is normalized before being used in the calculation. This is a critical detail for implementation. \nWe will adapt their 'Threat Risk' score into our 'Trust Score' (where a higher score is better).

Based on the paper's model, we can define our trust score calculation as follows:

Where:

  • is the normalized value (from 0 to 1) of a specific signal (e.g., device_posture_ok, mfa_present).
  • is the weight assigned to that signal, representing its importance.
  • The sum of all weights must equal 1.

Example Calculation:

Let's say we define the following signals and weights:

  • User is strongly authenticated (MFA):
  • Device is corporate-managed and compliant:
  • IP address has a good reputation:
  • Geolocation is typical for the user:

For a given request:

  • User used MFA ()
  • Device is non-compliant ()
  • IP reputation is excellent ()
  • Geolocation is unusual ()

The trust score would be:

This score can now be used to make a nuanced access decision.

3. From Score to Decision: Policy Logic and State

A score of 0.69 is meaningless without policy logic to interpret it.

3.1. Threshold-Based Policies

The simplest approach is to use thresholds:

  • TrustScore >= 0.8: Allow Access.
  • 0.5 <= TrustScore < 0.8: Allow, but require Step-Up Authentication (e.g., re-prompt for MFA).
  • TrustScore < 0.5: Deny Access.

3.2. A More Advanced Model: Finite State Machines (FSM)

A more sophisticated architecture, especially for a "first-principles" design, involves maintaining the trust state of a user/device over time using a Finite State Machine (FSM). This avoids re-calculating everything from scratch on every request and allows for more complex logic.

The same research paper provides an excellent model for this.

[PDF] A Context-Aware and Real-Time Dynamic Zero Trust

Let's return to the research paper to see how an FSM can be used to manage trust states.

Please read these two sections: Section III, subsection F: Policy Engine (PE) (page 5). This explains how an FSM can define states like Normal, Alert, and High Risk. Section V, subsection A: Finite State Machine (FSM) Event Generation (page 8). This shows how the calculated risk score triggers events that cause transitions between the FSM states.

In this model, the trust score doesn't directly result in a decision. Instead, it triggers an event that transitions the entity (the user/device pair) between states:

  • A high score keeps the entity in the Normal state.
  • A medium score might move it to an Alert state, triggering increased logging.
  • A low score could transition it to a Quarantined state, where all access is blocked pending investigation.

The actual access policy then becomes simpler: Allow access to resource X if user is in group 'Engineers' AND entity state is 'Normal'. This separation of continuous trust assessment (FSM) from discrete policy enforcement makes the system more robust and scalable.

4. Architectural Patterns for Signal Ingestion

We've designed the logic, but how does the Policy Engine get the data it needs at the moment of decision? This is a critical system design problem with significant trade-offs between latency, data freshness (liveness), and memory usage.

A policy engine like the open-source Open Policy Agent (OPA) is often at the core of ZTNA products. The following article outlines four primary architectural patterns for feeding data into such an engine.

Handling data in OPA policies

This blog post from Aserto, 'Handling data in OPA policies', is a masterclass in the practical system design trade-offs for a policy engine. We will use its patterns to architect our signal ingestion.

Read the article starting from the section 'Modeling data in policies' to the end. For each of the four patterns, understand its core mechanism and the trade-offs regarding Latency, Memory, and Liveness: Embedding data in a policy Piggy-backing on the User mechanism Using the Aserto Resource API (think of this as a generic key-value store) Loading external data

Let's map these patterns to the signals our ZTNA engine needs:

Signal Category Example Signal Best Ingestion Pattern(s) Rationale
Identity User roles, department Piggy-backing on User Mechanism (Cached) User data changes infrequently. Caching provides very low latency at decision time.
Device Posture OS version, disk encryption Resource API / Key-Value Store (Pushed periodically) The device agent can push posture updates every few minutes. The PE reads from this fast, local cache.
Device Posture Real-time EDR alert status Loading External Data (Live API call) For critical, highly dynamic signals, the latency of a direct API call to the EDR system may be an acceptable trade-off.
Contextual IP reputation, threat intel Loading External Data (Live API call to a specialized service) or a frequently updated local cache. Threat intelligence is highly dynamic. A live lookup is often necessary, but a local cache can reduce latency for known IPs.
Static Policy Resource sensitivity scores Embedding Data in Policy The criticality of an application changes very rarely, so this data can be bundled and deployed with the policy itself.

This hybrid approach allows you to balance performance and data freshness, creating a highly efficient and responsive policy engine.

Conclusion

In this lesson, we have moved beyond a high-level view and designed the core architecture of a ZTNA policy engine from first principles. We have shown how it functions as a real-time data processing system that continuously evaluates trust.

Key Takeaways:

  • The Policy Engine is a Trust Calculator: Its primary function is to ingest multiple signals (identity, device, context) and compute a dynamic trust score.
  • Trust Scoring is a Weighted Aggregation: A simple and powerful model is to use a weighted sum of normalized signals, allowing architects to tune the importance of different factors.
  • Decisions Can Be State-Aware: Using a Finite State Machine (FSM) to manage an entity's trust state over time creates a more robust and sophisticated system than simple, stateless thresholding.
  • Signal Ingestion Involves Trade-offs: Architecting the data flow into the engine requires balancing latency and liveness. A hybrid approach using caching, periodic pushes, and live API calls is often optimal.

Preview of the Next Lesson:

We have now designed the control plane's "brain" (the Policy Engine) and understand how it decides who can access what. But how does that decision get enforced for applications running deep inside a private network? In our next lesson, we will design the architecture of ZTNA connectors, focusing on their outbound-only connectivity and secure registration with the control plane. We will build the secure bridge from the ZTNA service edge into the protected enterprise environment.

Can't find a good explanation? Sign up and we'll make it for you

Sign up