Security Context Enrichment: Tagging All Data
Security Context Enrichment: Making ABAC Work
You've designed your groups, policies, and boundaries. Now the hard part: every piece of data must be tagged with dt.security_context for ABAC to actually filter anything. This module covers how to enrich every data type β hosts, services, logs, metrics, traces, entities, and Kubernetes workloads.
β οΈ This is the #1 reason ABAC implementations fail: the policies are correct, but the data isn't tagged. If a log record doesn't have dt.security_context, no policy can filter it β the user either sees everything or nothing.
The Enrichment Challenge
Dynatrace recommends setting up permissions along organizational lines and deployment scopes. The primary attributes you can use without any extra setup:
Attribute Available On Use Case
ββββββββββββββββββββββ βββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββ
dt.host_group.id All telemetry from OneAgent Team isolation by host group
k8s.cluster.name All K8s telemetry Cluster-level isolation
k8s.namespace.name All K8s namespace telemetry Namespace-level isolation
dt.security_context Must be explicitly set Fine-grained custom isolation
π‘ If your isolation needs align with host groups, K8s clusters, or K8s namespaces, you can use those attributes DIRECTLY in policy boundaries β no extra enrichment needed. Only use dt.security_context when you need custom isolation that doesn't map to these deployment concepts.
Three Levels of Permission Setup
Level Complexity Use When
ββββββββββββββ ββββββββββ ββββββββββββββββββββββββββββββββββββββββββββββββββ
Bucket-level Low Route data to buckets, restrict by bucket name
Deployment Medium Use dt.host_group.id, k8s.cluster/namespace directly
Security ctx High Custom isolation (team codes, business units, etc.)
Method 1: OneAgent Host Properties (VM/Bare Metal)
For hosts monitored by OneAgent, set the security context as a host property:
# Set security context on the host (Linux)
sudo /opt/dynatrace/oneagent/agent/tools/oneagentctl \
--set-host-property=dt.security_context=SV-PAYMENTS.PRD
# Windows
.\oneagentctl.exe --set-host-property=dt.security_context=SV-PAYMENTS.PRD
# Verify
sudo /opt/dynatrace/oneagent/agent/tools/oneagentctl --get-host-properties
β οΈ Use --set-host-property (NOT --set-host-tag). Host properties and host tags are different things in Dynatrace. The dt.security_context must be set as a host PROPERTY for it to propagate to all telemetry data (metrics, spans, events, logs) collected by OneAgent on that host.
What Propagates Automatically
Host property: dt.security_context=SV-PAYMENTS.PRD
β propagates to ALL telemetry from that host:
βββ Metrics (dt.host.cpu.*, dt.host.memory.*, etc.)
βββ Spans from services on that host
βββ Events from that host
βββ Logs collected from that host
βββ Entities (host, process groups, services)
At Install Time (Recommended for New Deployments)
# Set during OneAgent installation
sudo /bin/sh Dynatrace-OneAgent-Linux.sh \
--set-host-group=payments-prod \
--set-host-property=dt.security_context=SV-PAYMENTS.PRD \
--set-host-property=dt.cost.costcenter=CC-1234 \
--set-host-property=dt.cost.product=payment-gateway
π The dt.security_context value can be hierarchical: department-A/department-AB/team-C. Use MATCH in policies to match hierarchically (e.g., MATCH ("department-A") matches all sub-departments).
Method 2: Entity Security Context via Settings (MZ Bridge)
Dynatrace provides a built-in bridge from management zones to security context for entities. This is configured via:
Settings β Topology model β Grail security context for monitored entities
Schema: builtin:monitoredentities.grail.security.context
By default, Dynatrace uses management zones to set dt.security_context on entities. This means during migration, your existing MZs automatically provide entity-level security context!
# Terraform: override entity security context source
resource "dynatrace_grail_security_context" "host" {
entity_type = "HOST"
destination_property = "hostGroupName" # derive from host group
}
resource "dynatrace_grail_security_context" "service" {
entity_type = "SERVICE"
destination_property = "managementZones" # derive from MZ (default)
}
π‘ This is the migration bridge: your existing management zones automatically set dt.security_context on entities. You don't need to manually tag every entity β just ensure your MZs are correct, and entity-level ABAC works immediately.
β οΈ This only covers ENTITY access (storage:entities:read). It does NOT automatically tag logs, metrics, or spans. For telemetry data, you still need host properties, K8s enrichment, or OpenPipeline.
Method 3: Kubernetes Metadata Enrichment (Recommended for K8s)
For Kubernetes, the Dynatrace Operator enriches ALL telemetry with K8s metadata. Two approaches:
Option A: Namespace Labels via Enrichment Rules (Recommended)
Map existing namespace labels to dt.security_context:
# Your existing namespace already has labels:
apiVersion: v1
kind: Namespace
metadata:
name: payments
labels:
team: payments
cost-center: CC-1234
# Terraform: map namespace label β dt.security_context
resource "dynatrace_kubernetes_enrichment" "security" {
scope = "environment"
rules {
rule {
type = "LABEL"
source = "team"
target = "dt.security_context"
}
rule {
type = "LABEL"
source = "cost-center"
target = "dt.cost.costcenter"
}
}
}
This enriches ALL telemetry: metrics, events, logs, spans, AND entities from that namespace.
Option B: Dedicated Pod Annotations (Fallback Only)
# Pod annotation (only when namespace labels won't work)
apiVersion: v1
kind: Pod
metadata:
annotations:
metadata.dynatrace.com/dt.security_context: SV-PAYMENTS.PRD
metadata.dynatrace.com/dt.cost.costcenter: CC-1234
metadata.dynatrace.com/dt.cost.product: payment-gateway
β οΈ Pod annotations do NOT provide full enrichment. They do NOT enrich: Kubernetes metrics, Kubernetes events, Kubernetes Smartscape entities, or Prometheus metrics. Use namespace labels via enrichment rules (Option A) whenever possible.
Enrichment Hierarchy (Operator 1.3.0+)
Priority Source Enriches
ββββββββ ββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββ
1 (high) Pod annotation Pod + containers (NOT K8s metrics/events)
2 Namespace annotation/label Namespace + workloads + services + pods
3 (low) Node annotation Node only
Rule: Pod annotations override namespace annotations.
Namespace annotations are copied down to pods if not already set.
Method 4: OpenPipeline Security Context Processors
For data that doesn't come from OneAgent or K8s (external logs, API-ingested events, third-party spans), use OpenPipeline's dedicated Set Security Context processor:
OpenPipeline β [Data Type] β Pipeline β Permission tab β Set Security Context
1. Define matching condition: matchesValue(http.route, "/basket")
2. Set dt.security_context value: "TeamA" (literal or from another field)
3. Repeat for each data type (logs, metrics, spans)
π The Security Context processor is on the Permission tab in OpenPipeline β not the Processing tab. It's a dedicated processor type specifically for setting dt.security_context.
Use Cases for OpenPipeline Enrichment
Scenario Matching Condition
ββββββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββ
Logs from external syslog (no OneAgent) Match on source IP or hostname
Business events from API ingest Match on event.type or source
Spans from OpenTelemetry (no OneAgent) Match on service.name
Metrics from cloud integrations (GCP/Azure) Match on cloud.account or project
The Complete Enrichment Strategy
Infrastructure Type Primary Method Terraform Resource
ββββββββββββββββββββββ ββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββ
VMs (Linux/Windows) oneagentctl --set-host-property (manual or Ansible)
Kubernetes K8s metadata enrichment rules dynatrace_kubernetes_enrichment
Entities (MZ bridge) Settings: Grail security context dynatrace_grail_security_context
External/API data OpenPipeline Security Context dynatrace_openpipeline_*
Verifying Enrichment
After setting up enrichment, verify that data is actually tagged:
// Check hosts have security context (as entity property)
fetch dt.entity.host
| fields entity.name, dt.security_context
| filter isNotNull(dt.security_context)
// Check logs have security context
fetch logs, from:now()-1h
| filter isNotNull(dt.security_context)
| summarize count(), by:{dt.security_context}
// Check spans have security context
fetch spans, from:now()-1h
| filter isNotNull(dt.security_context)
| summarize count(), by:{dt.security_context}
// CRITICAL: Find UNTAGGED data (gaps in your enrichment)
fetch logs, from:now()-1h
| filter isNull(dt.security_context)
| summarize cnt=count(), by:{dt.host_group.id}
| sort cnt desc
// Check which host groups have no security context
fetch logs, from:now()-1h
| filter isNull(dt.security_context) AND isNotNull(dt.host_group.id)
| summarize cnt=count(), by:{dt.host_group.id}
| sort cnt desc
β οΈ The "find untagged data" query is critical. These are your ABAC blind spots β data that no boundary can filter. Run this BEFORE enabling restrictive policies.
Alternative: Use Host Group Directly (Simpler)
If your team isolation maps cleanly to host groups, you can skip dt.security_context entirely and use dt.host_group.id in your boundaries:
// Boundary using host group directly (no enrichment needed!)
storage:dt.host_group.id = "payments-prod";
// Or with MATCH for hierarchical host groups:
storage:dt.host_group.id MATCH ("payments-*");
This works because dt.host_group.id is automatically available on all telemetry from OneAgent β no extra configuration needed.
π‘ Dynatrace's official recommendation: start with deployment-level attributes (host group, K8s namespace, K8s cluster). Only add dt.security_context when these don't provide enough granularity. Simpler is better.
Entity vs Telemetry Permissions (Critical Distinction)
A common misconception: "If I restrict entity access, the user can't see that entity's data."
WRONG. Entity permissions and telemetry permissions are INDEPENDENT:
// This restricts which ENTITIES the user can query:
ALLOW storage:entities:read
WHERE storage:dt.security_context MATCH ("SV-PAYMENTS");
// But it does NOT restrict logs, metrics, or traces!
// You need SEPARATE permissions for each data type:
ALLOW storage:logs:read
WHERE storage:dt.security_context MATCH ("SV-PAYMENTS");
ALLOW storage:metrics:read
WHERE storage:dt.security_context MATCH ("SV-PAYMENTS");
ALLOW storage:spans:read
WHERE storage:dt.security_context MATCH ("SV-PAYMENTS");
β οΈ This is explicitly stated in the official Dynatrace docs: "Unlike management zones, an IAM policy that is set up to filter entities will NOT filter related metrics, logs, or traces. These entity filters only control which entities can be queried via DQL." This is the most common ABAC mistake.
The Role Policy Pattern (Solve It Once)
Instead of writing separate policies per data type, write ONE comprehensive role policy that covers everything:
// Analyst role policy β covers ALL data types in one statement
ALLOW storage:logs:read,
storage:metrics:read,
storage:events:read,
storage:spans:read,
storage:entities:read,
storage:bizevents:read,
storage:security.events:read;
Then bind with a boundary that restricts ALL of them at once:
// Boundary: storage:dt.security_context MATCH ("SV-PAYMENTS.PRD")
// This single boundary restricts ALL permissions in the policy
π‘ This is exactly what the V3.1 model does: one "role policy" with all data permissions, bound with a boundary. The boundary is the single point of control for data isolation.
Timeline for Enrichment Rollout
Week Action Coverage
ββββ ββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββ
1 Verify MZβentity bridge is active Entities (automatic)
2 Set host properties on all VMs Host telemetry (metrics, logs, spans)
3 Configure K8s metadata enrichment All K8s workload telemetry
4 Add OpenPipeline rules for gaps External logs, API events
5 Verify with DQL queries Find remaining gaps
6 Enable ABAC policies (pilot team) Test with one team first
7 Roll out to all teams Full access control
β οΈ Don't enable restrictive ABAC policies until enrichment is verified. If you restrict access before data is tagged, users will lose visibility. Tag first, verify, then restrict.
π Knowledge Check
Q: What's the difference between --set-host-tag and --set-host-property for security context?
A: --set-host-property is the correct command for dt.security_context. Host properties propagate to ALL telemetry data (metrics, spans, events, logs). Host tags are metadata on the entity only β they don't propagate to telemetry records in Grail. The official Dynatrace docs explicitly use --set-host-property.
Q: A team reports they can't see any logs after ABAC was enabled. What's the most likely cause?
A: The logs aren't tagged with dt.security_context. The ABAC policy filters on this attribute, but if the logs don't have it, the filter matches nothing. Check with: fetch logs | filter isNull(dt.security_context) | limit 10. Then check if the host has the property set: oneagentctl --get-host-properties.
Q: You restrict entity access for a team. Can they still query metrics from those entities?
A: YES. Entity permissions and telemetry permissions are independent. The official docs state: "an IAM policy that filters entities will NOT filter related metrics, logs, or traces." You need separate permissions for each data type, or use one combined role policy with a boundary.
Q: For Kubernetes, should you use pod annotations or namespace labels for security context?
A: Namespace labels via enrichment rules (recommended). Pod annotations do NOT enrich K8s metrics, K8s events, K8s Smartscape entities, or Prometheus metrics. Namespace labels provide full enrichment across all telemetry types.
Q: Can you skip dt.security_context entirely and just use host groups in boundaries?
A: Yes! If your isolation maps to host groups, use storage:dt.host_group.id = "my-group" directly in boundaries. No extra enrichment needed β host group is automatically available on all OneAgent telemetry. Only add dt.security_context when host groups don't provide enough granularity.
Q: During migration, how do entities get their security context without manual tagging?
A: Dynatrace automatically maps management zones to dt.security_context on entities via the builtin:monitoredentities.grail.security.context setting. Your existing MZs provide entity-level ABAC out of the box. But this only covers entities β telemetry data still needs explicit enrichment.