Skip to content

Setting Up Policy-Aware dABAC (End to End)

The Managing Policies tutorial is a reference — it explains every tab in the Policy section on its own terms. This tutorial is the walkthrough: it takes one real goal — "make a set of files decryptable on desktops but never on phones" — and carries it from an empty Policy section all the way to enforced, key-level restriction, then generalises the pattern into a handful of reusable blueprints you can copy.

Read Managing Policies — The Policies tab first if you have never opened the Policy section; this tutorial assumes you know where the tabs are and focuses on the order you use them in.


The one idea to keep in your head

Possession of an attribute's key is the access decision. A device can decrypt exactly what it holds keys for. A Policy does not add a check that runs on the device — it changes what the conductor hands the device in the first place. When a governed attribute's policy isn't satisfied by a connecting device's environment, the conductor leaves that attribute's key out of the payload, before it signs it. There is no client-side gate to bypass because the secret was never delivered.

Two consequences fall out of that and shape everything below:

  • ntkDeploy authors and previews; the conductor enforces. You mint attributes, record device environments, write the Policy, and run Simulate against fleet — all in ntkDeploy. But the switch that actually starts withholding keys is POLICY_ENFORCEMENT_MODE on the conductor (the ntk_dabac deployment), not a button in this app. Steps 6–8 below cross that boundary deliberately, and call out which system you are in each time.
  • Enforcement is per-connection, not per-deployment. A Policy never blocks anything ntkDeploy writes to a share — that is preflight, gated by Assignments. A Policy only changes what a device receives the next time it reconnects to the conductor.

What you'll build

Scenario — Finance-Confidential, desktop-only. Your organisation keeps a "Finance-Confidential" folder. Finance analysts must be able to open it on their managed desktops, but the same people's phones must never be able to decrypt it — even a rooted phone running a modified app. Nothing about who the person is changes; only where they connect from decides whether the key is delivered.

By the end you will have:

  • a dedicated subject attribute (finance-confidential) assigned to the finance team;
  • an environment attribute (env:platform-desktop) that the conductor matches against each device;
  • Device Profiles recording which fingerprints are desktops;
  • a Policy that delivers finance-confidential only to desktop connections;
  • a verified simulate → dry-run → enforce rollout, so you never make a file undecryptable by surprise.

Prerequisites

  • Policy Manager reachable. Open Settings, enter the Policy Manager URL, click Validate, and confirm the header connectivity badge is healthy. If the Policy section reads "Policy management is unavailable", fix the endpoint and click Retry.
  • Access to the conductor's configuration. Steps 7–8 set an environment variable on the ntk_dabac conductor deployment. If you don't operate the conductor yourself, you will hand those two steps to whoever does — do steps 1–6 first and give them the go-ahead.
  • The sensitive data already lives under its own attribute. This is the load-bearing precondition (see Step 1). A file is decryptable by any one of the attributes it was encrypted under, so a policy can only isolate content that is encrypted under a dedicated attribute and no other. If Finance-Confidential shares an attribute with general content, no policy can isolate it — fix the encryption selection first.

Step 1 — Classify the data under a dedicated attribute

This step happens in ntkDesktop / ntkMobile, not ntkDeploy, but it comes first because everything else is inert without it.

The Finance-Confidential Source (desktop) / Vault (mobile) must require its own attribute — call it finance-confidential — as its own AND group in the encryption selection, so that opening those files requires that specific key and no substitute. If the same files are also encrypted under, say, an all-employees attribute the phone holds, the phone opens them via that key regardless of any policy. The policy layer does not remove this data-hygiene requirement; it depends on it.

Caution: Restriction only holds if the file's decryptability requires the restricted attribute and nothing else the device also holds unlocks it. Put the restricted attribute in its own AND group of the Source/Vault selection. This is the single most common way a policy "silently does nothing" — the key is withheld correctly, but a second attribute the device still holds opens the file anyway.


Step 2 — Mint the subject attribute and assign it

In ntkDeploy → PolicyAttributes:

  1. Click New.
  2. Label: finance-confidential. Scope: leave it Subject (the default). Default TTL seconds: the lifetime a grant stays valid after assignment — e.g. 86400 for 24 hours. Shorter TTLs tighten how long a pre-policy key survives on a device (see Honest limits).
  3. Save. The 32-character hex Attribute ID is generated for you; copy it — this is the value the Policy's Resource will point at.

Then, in PolicyAssignments, grant finance-confidential to each finance analyst exactly as you do today. This is unchanged by policies: the people who should ever have finance access still hold the subject attribute. The policy will decide, per connection, whether their device actually receives its key.

Note: Scope is immutable after creation and server-enforced. If you pick the wrong scope, delete the attribute and re-create it — you cannot flip Subject ↔ Environment on an existing record.


Step 3 — Mint the environment attribute

Still in PolicyAttributes:

  1. Click New.
  2. Label: env:platform-desktop — the colon is intentional and legal. The label must match one of the reserved env:* conventions exactly (case-insensitive), or the conductor will never satisfy it. Scope: switch to Environment.
  3. Save.

An Environment-scoped attribute is never assigned to a person — the server rejects that with environment_scope_unassignable. Instead the conductor computes, per connecting device, which env:* attributes that device satisfies from its Device Profile. The full set of recognised labels:

Label Matches a device whose Device Profile has…
env:platform-desktop Platform = desktop
env:platform-mobile Platform = mobile
env:platform-server Platform = server
env:managed-true Managed = on
env:managed-false Managed = off
env:trust-low Trust Level = low
env:trust-standard Trust Level = standard
env:trust-high Trust Level = high

Mint the env:* attributes your policies will reference once for the whole org; you reuse them across every policy. For this scenario you only need env:platform-desktop.

Tip: A device with no Device Profile — or one the conductor can't resolve — satisfies none of these labels and therefore fails any requirement set that names one. Unprofiled devices fail closed on governed attributes once enforcement is on. Step 4 exists precisely so your legitimate desktops don't get caught by that.


Step 4 — Record each device's environment

In PolicyDevice Profiles, create one record per enrolled device (or bulk-import them — see Managing Policies — CSV import):

  1. Click New.
  2. Fingerprint: the device's 64-character lowercase hex fingerprint (immutable once set).
  3. Platform: desktop for the analysts' laptops; mobile for their phones.
  4. Managed / Trust Level: set as appropriate — not needed for this policy, but they power the env:managed-* and env:trust-* blueprints later.
  5. Save.

These are admin-asserted facts, the same trust tier as the fingerprint allowlist — not a hardware attestation. platform=mobile means "an administrator recorded this fingerprint as a phone," not "this is provably a phone." Treat the Device Profiles list as security-relevant configuration.

Warning: A desktop you forget to profile will be treated as unprofiled and lose finance-confidential under enforcement, exactly like a phone. Before enforcing, make sure every device that should keep access has a correct Device Profile. Step 6's simulation is how you catch the ones you missed.


Step 5 — Author the Policy

In PolicyPolicies:

  1. Click New.
  2. Resource: use the picker to select the finance-confidential attribute. The read-only Selected Resource Attribute ID fills with its 32-hex ID — that's the field that's saved.
  3. Person: pick anyone to record as author. This is bookkeeping only — it does not scope the policy. Once saved, this policy governs finance-confidential for every device that connects, regardless of who owns it.
  4. Requirement Sets: add one set (Requirement set 1 — all required (AND)) containing the single attribute env:platform-desktop.
  5. Save.

You've now said: "the finance-confidential key may be delivered only to a connection whose environment is desktop." Reading the requirement-set grammar:

  • Within a set, attributes are AND — every one must be satisfied.
  • Across sets, it's OR — the resource is delivered the moment any one set is fully satisfied.
  • So [[env:platform-desktop]] means "desktop"; [[env:platform-desktop],[env:platform-server]] means "desktop OR server"; [[env:platform-desktop, env:managed-true]] means "desktop AND managed."

The bracket notation used throughout this tutorial is documentation shorthand: a requirement set actually stores each attribute's 32-character hex Attribute ID, not its label — the requirement-set picker resolves labels to IDs for you, exactly as the Resource picker does. [[env:platform-desktop]] means "the Attribute ID of the definition labelled env:platform-desktop."

Important: Requirement sets are matched against the connecting device's environment, not against the assigned person's own subject attributes. Build them out of Environment-scoped attributes. A requirement set full of subject attributes will almost never behave the way you expect, because the conductor evaluates the device, not the person.

If two or more policies name the same Resource, they are deny-overrides: every one must pass for the attribute to be delivered.


Step 6 — Simulate against the fleet (ntkDeploy)

Before anything becomes undecryptable, click Simulate against fleet on the policy form (enabled once a valid Resource is selected). This calls the Policy Manager's /policies/simulate endpoint using the same evaluation code the conductor runs at connection time, so the preview cannot drift from real enforcement.

You'll get one of:

  • "No policy restricts this resource — all N enrolled devices would receive it." (no governing policy yet)
  • "No enrolled devices found to simulate against." (no Device Profiles exist — go back to Step 4)
  • "X of Y enrolled devices would receive this attribute; Z withheld," followed by a per-device list (platform, managed, trust) with a check or block icon each.

Read the withheld list carefully. Every phone in the finance team should appear as withheld — that's the goal. A desktop appearing as withheld means you missed its Device Profile (Step 4) or mislabelled its platform. "Enrolled" here means "has a Device Profile record," independent of device-key enrollment status.

Note: Simulation is a static preview against your Device Profiles. It changes nothing about delivery and emits no audit events. It answers "who would be affected," so you fix mistakes before they bite.


Step 7 — Dry-run on the conductor

This step is on the conductor (ntk_dabac), not ntkDeploy — it and Step 8 are the only two steps outside this app.

Note — two server-side switches, not one. Enforcement needs both: the policy_manager must have policy delivery enabled (POLICY_DELIVERY_ENABLED=true, default off) so that snapshots even carry a policies section, and the conductor must set POLICY_ENFORCEMENT_MODE so it acts on them. Delivery without enforcement is inert, and enforcement without delivery has nothing to act on. Your conductor operator sets both through the ntk_dabac installer/reconfigure tooling — never from ntkDeploy.

Set the enforcement mode on the conductor deployment:

POLICY_ENFORCEMENT_MODE=dryrun

In dryrun, the conductor evaluates every policy on every reconnection but still delivers every key normally — nothing is withheld yet. For each attribute that would have been withheld, it emits a server-side audit event:

policy_attribute_withheld_dryrun   (result: delivered_dryrun)
detail: attributeId=<F> fingerprint=<device> envAttrs=<n> mode=dryrun

Where Step 6's simulation is a static snapshot of your Device Profiles, dry-run watches real connections as they happen. Leave it running long enough for your fleet to reconnect, then review the conductor's audit stream: the set of policy_attribute_withheld_dryrun events is exactly what enforce mode will withhold. If a desktop shows up here, stop and fix its Device Profile before proceeding.

Note: POLICY_ENFORCEMENT_MODE accepts off (the default), dryrun, and enforce. An unrecognised value is treated as off and logged at conductor startup — enforcement is only ever on when an operator has typed it exactly. These policy_attribute_withheld* events are conductor events; they are not shown in ntkDeploy's own Audit Log, which records this admin app's actions.


Step 8 — Enforce

Once the dry-run audit stream shows only the devices you intend to restrict, flip the conductor to:

POLICY_ENFORCEMENT_MODE=enforce

Now, on each device's next reconnection, the conductor drops any attribute whose policy the device's environment fails from the payload before it signs it, and emits:

policy_attribute_withheld            (result: withheld)

For the finance team, a desktop connection resolves to env:platform-desktop, satisfies the requirement set, and receives finance-confidential normally. A phone connection resolves to env:platform-mobile, satisfies no set, and its next assignment fetch returns a payload one attribute shorter — no error, no new field, nothing for the client to interpret differently. On the wire it is indistinguishable from a device that was simply never assigned the attribute. The Finance-Confidential files fail to decrypt on the phone exactly as if that phone had never been assigned the attribute — which, key-wise, is now true.

Warning — enforcement is not instant and does not claw back. A device that is already connected keeps what it was given until it reconnects; there is no push-revocation. Withholding stops future delivery — it does not remove a key a device already holds, and it does not un-sync ciphertext already on the device. To bound how long a pre-policy key survives, use a shorter Default TTL on governed attributes (Step 2) and revoke-and-reprovision assignments; the effect is bounded by reconnect latency, not immediate.

Rolling back is symmetric: set POLICY_ENFORCEMENT_MODE=dryrun (or off) to stop withholding, or delete the Policy in ntkDeploy to make the attribute ungoverned again — either way, affected devices receive the attribute again on their next reconnection.


The enforcement ladder at a glance

Stage Where you do it What it does Delivery effect Audit event
Simulate ntkDeploy policy form (Simulate against fleet/policies/simulate) Static preview against current Device Profiles None none
Dry-run Conductor POLICY_ENFORCEMENT_MODE=dryrun Evaluates real connections, withholds nothing None (keys still delivered) policy_attribute_withheld_dryrun
Enforce Conductor POLICY_ENFORCEMENT_MODE=enforce Drops failed attributes before signing Keys withheld on next reconnect policy_attribute_withheld

Climb the ladder in order. Simulate catches configuration mistakes for free; dry-run confirms against live reconnections; enforce is the only rung that actually changes what devices can decrypt.


Blueprint examples

Each blueprint is the same five-move pattern from the walkthrough — dedicated subject attribute, env:* attribute(s), Device Profiles, Policy, simulate → dry-run → enforce — with a different requirement set. For each, the connection trace shows why a compliant device passes and a non-compliant one is withheld.

Blueprint A — Desktop-only (the seed use case)

Restated as a recipe:

  1. Mint subject attribute finance-confidential; assign it to the finance team.
  2. Encrypt the Finance-Confidential Source/Vault so it requires finance-confidential as its own AND group.
  3. Mint env:platform-desktop (Scope = Environment).
  4. Author the Policy: Resource = finance-confidential; requirement set = [[env:platform-desktop]].
  5. Simulate, dry-run, enforce.

Connection trace

  • Laptop: authenticated → environment resolves to env:platform-desktop → requirement set [env:platform-desktop] satisfied → passfinance-confidential signed and delivered.
  • Phone: same person, same subject assignment → environment resolves to env:platform-mobile → no set satisfied → fail → attribute dropped before signing → policy_attribute_withheld emitted. The folder goes dark on mobile; desktop is untouched.

Variant — desktop or server. Use two requirement sets: [[env:platform-desktop], [env:platform-server]]. Delivery succeeds if the connection is desktop or server; mobile still fails both.

Blueprint B — Managed-devices-only

Goal: an it-admin-tools attribute (config templates, break-glass runbooks) may only decrypt on centrally managed devices, so it never lands on a BYOD laptop.

  1. Mint subject attribute it-admin-tools; assign it to the IT team; encrypt the tooling Source/Vault to require it as its own AND group.
  2. Mint env:managed-true (Scope = Environment).
  3. In Device Profiles, set Managed = on for the corporate fleet and off for personal devices.
  4. Policy: Resource = it-admin-tools; requirement set = [[env:managed-true]].

Connection trace

  • Corporate laptop (Managed = on) → satisfies env:managed-truepass → delivered.
  • Personal laptop (Managed = off, or unprofiled) → does not satisfy env:managed-truefail → withheld.

Blueprint C — High-trust-only

Goal: a prod-secrets attribute may only decrypt on devices you've classified high trust, regardless of platform.

  1. Mint subject attribute prod-secrets; assign to the on-call/SRE group; encrypt the secrets Vault to require it as its own AND group.
  2. Mint env:trust-high (Scope = Environment).
  3. In Device Profiles, set Trust Level = high only on hardened, audited machines.
  4. Policy: Resource = prod-secrets; requirement set = [[env:trust-high]].

Connection trace

  • Hardened workstation (Trust = high) → satisfies env:trust-highpass → delivered.
  • Standard laptop (Trust = standard/low) → fail → withheld — even though the same person is fully assigned prod-secrets.

Blueprint D — Two conditions at once (AND within a set)

Goal: source-code may decrypt only on devices that are both desktop and managed — a managed phone still can't, and an unmanaged desktop still can't.

  1. Mint subject attribute source-code; assign to engineering; encrypt the code Source to require it as its own AND group.
  2. Mint (or reuse) env:platform-desktop and env:managed-true.
  3. In Device Profiles, set both Platform = desktop and Managed = on on eligible machines.
  4. Policy: Resource = source-code; one requirement set holding both attributes: [[env:platform-desktop, env:managed-true]].

Connection trace

  • Managed desktop → satisfies both members of the set → pass → delivered.
  • Unmanaged desktop → satisfies env:platform-desktop but not env:managed-true → set not fully held → fail → withheld.
  • Managed phone → satisfies env:managed-true but not env:platform-desktopfail → withheld.

Tip: "AND within a set" (one set, several attributes) tightens a rule; "OR across sets" (several sets) loosens it. Blueprint A's desktop-or-server variant loosens; Blueprint D tightens. Compose both in one policy when you need "(desktop AND managed) OR (server AND high-trust)": [[env:platform-desktop, env:managed-true], [env:platform-server, env:trust-high]].

Blueprint E — Layering two policies (deny-overrides)

Goal: regulated-data must be both on a managed device and high trust, expressed as two separate policies so different teams can own each condition independently.

  1. Policy 1: Resource = regulated-data; requirement set = [[env:managed-true]].
  2. Policy 2: Resource = regulated-data; requirement set = [[env:trust-high]].

Because multiple policies on one resource are deny-overrides, delivery requires both to pass — a managed-but-standard-trust device is withheld by Policy 2, and a high-trust-but-unmanaged device is withheld by Policy 1. This is a way to compose independently-authored conditions; note it behaves like AND across policies, which is the opposite of OR across requirement sets within one policy. When one team owns "managed" and another owns "trust tier," two policies keep those rules editable in isolation.


Honest limits

State these plainly to any stakeholder — they are properties of the design, not bugs:

  • Non-retroactive. A device that received a governed attribute's key before the policy existed keeps it until the key's TTL/EndDate lapses or the device re-fetches and discards it. Any plaintext already exported to the device is unaffected. Mitigate with shorter TTLs and revoke-and-reprovision; the bound is reconnect latency, not instant.
  • No clawback of ciphertext. Withholding stops future key delivery; encrypted files already synced to a device stay there and simply never decrypt.
  • Attribute hygiene is load-bearing. A file encrypted under the restricted attribute and a second attribute the device still holds decrypts via the second one, policy notwithstanding. The restriction holds only when decryptability requires the restricted attribute alone (Step 1).
  • Device facts are admin-asserted, not attested. Platform, managed, and trust are what an administrator recorded, at the same trust tier as the fingerprint allowlist — not hardware attestation.
  • Bypass resistance comes from non-delivery. A rooted device running a modified client still can't open a withheld attribute: the conductor never transmitted the key, and no client code can invent a key it was never given. There is no client-side check to bypass because the secret was never delivered.

Next Steps