Skip to the content.

Policy Schema Reference

Policy rules are loaded from a YAML file by PolicyEngine. The path to the file is set via policy_path in SenseGNATSettings, or passed directly to PolicyEngine.from_yaml(path).

Module: sensegnat/policy/engine.py


Top-level structure

groups:    # optional
  <group-name>:
    ...

subjects:  # optional
  <subject-id>:
    ...

Both groups and subjects are optional. An empty or absent policy file is valid; it results in no rules for any subject.


groups.<name>

Defines rules that apply to all members of the named group.

groups:
  <name>:
    members:               # list[str]  — subject IDs belonging to this group
    allowed_destinations:  # list[str]  — IP addresses treated as known-good
    allowed_ports:         # list[int]  — port numbers treated as known-good
    allowed_protocols:     # list[str]  — protocol names treated as known-good

Fields

Field Type Required Description
members list[str] No Subject IDs in this group. Used by PolicyEngine.peer_members() to resolve peer profiles for PeerDeviationDetector.
allowed_destinations list[str] No IP addresses that are known-good for all group members.
allowed_ports list[int] No Port numbers that are known-good for all group members.
allowed_protocols list[str] No Protocol names that are known-good for all group members.

All fields are optional. Omitted list fields default to empty.


subjects.<id>

Defines rules that apply to a single subject. The <id> must match the canonical subject_id (source_user or source_host) as used in the pipeline.

subjects:
  <subject-id>:
    peer_group:            # str       — group to compare against for peer deviation
    allowed_destinations:  # list[str] — adds to (not replaces) group destinations
    allowed_ports:         # list[int] — adds to (not replaces) group ports
    allowed_protocols:     # list[str] — adds to (not replaces) group protocols

Fields

Field Type Required Description
peer_group str No Name of the group this subject belongs to. Sets BehaviorProfile.peer_group. Used to look up peer profiles for PeerDeviationDetector.
allowed_destinations list[str] No Subject-specific destinations added to the group’s allow-list.
allowed_ports list[int] No Subject-specific ports added to the group’s allow-list.
allowed_protocols list[str] No Subject-specific protocols added to the group’s allow-list.

Resolution rules

When PolicyEngine resolves rules for a subject, it unions subject-level and group-level entries.

resolved_destinations(subject) =
    subjects.<subject>.allowed_destinations
    ∪ groups.<peer_group>.allowed_destinations

The same union applies to allowed_ports and allowed_protocols.

A destination is “allowed” if it appears in either the group rules or the subject rules.

Subject rules do not replace group rules. There is no override or exclusion mechanism; only addition.

Resolution example

Given:

groups:
  engineering:
    allowed_destinations: [10.0.0.1, 203.0.113.10]
    allowed_ports: [22, 443]

subjects:
  alice:
    peer_group: engineering
    allowed_destinations: [198.51.100.44]
    allowed_ports: [8443]

Resolved for alice:

Missing policy

If a subject has no entry in subjects, PolicyEngine.allowed_destinations(subject_id) returns an empty frozenset. PolicyViolationDetector treats an empty frozenset as “no policy defined” and does not fire.


PolicyEngine methods

Method Returns Description
peer_group(subject_id) str \| None The peer_group value from the subject entry, or None.
peer_members(group) list[str] The members list for the named group.
allowed_destinations(subject_id) frozenset[str] Resolved union of subject + group destinations.
allowed_ports(subject_id) frozenset[int] Resolved union of subject + group ports.
allowed_protocols(subject_id) frozenset[str] Resolved union of subject + group protocols.

Profile seeding

Before telemetry events are processed, ProfileBuilder calls PolicyEngine to seed BehaviorProfile objects. The resolved allowed_destinations, allowed_ports, and allowed_protocols for each known subject are written directly into common_destinations, common_ports, and common_protocols. This means policy-allowed addresses do not trigger RareDestinationDetector on first contact.


Complete annotated example

# Policy file for SenseGNAT.
# Loaded by PolicyEngine.from_yaml(path) or via policy_path in settings.

groups:

  engineering:
    # All members share these rules.
    members: [alice, bob]
    allowed_destinations:
      - 203.0.113.10   # internal CI server
      - 10.0.0.1       # corporate gateway
    allowed_ports: [22, 80, 443, 8080]
    allowed_protocols: [tcp]

  finance:
    members: [carol]
    allowed_destinations:
      - 203.0.113.50   # finance SaaS endpoint
    allowed_ports: [443]
    allowed_protocols: [tcp]

subjects:

  alice:
    # alice is in the engineering group (inherits group rules)
    peer_group: engineering
    # alice has one additional approved destination not in the group list
    allowed_destinations:
      - 198.51.100.44  # alice's approved external vendor
    # alice is also allowed port 8443 (in addition to group ports)
    allowed_ports: [8443]
    # alice's resolved destinations: {203.0.113.10, 10.0.0.1, 198.51.100.44}
    # alice's resolved ports:        {22, 80, 443, 8080, 8443}
    # alice's resolved protocols:    {tcp}

  bob:
    # bob is in engineering; no subject-level additions
    peer_group: engineering
    # bob's resolved destinations: {203.0.113.10, 10.0.0.1}
    # bob's resolved ports:        {22, 80, 443, 8080}
    # bob's resolved protocols:    {tcp}

  carol:
    peer_group: finance
    # carol has no subject-level additions; inherits finance group rules only
    # carol's resolved destinations: {203.0.113.50}
    # carol's resolved ports:        {443}
    # carol's resolved protocols:    {tcp}

Loader

PolicyEngine.from_yaml(path: Path) -> PolicyEngine

Reads and yaml.safe_loads the file at path. Returns an empty PolicyEngine if the file is empty or contains only null. Does not validate field types; invalid entries (e.g., a string where a list is expected) will raise standard Python errors when the engine attempts to iterate them.