Specification
JSON policy formats, matching rules, and supported operations.
Policy Structure
A policy is a JSON object. Only mode is required; add operation sections as needed:
{
"mode": "observe",
"path": {
"open": [
"all|%workspace%"
]
}
}Omitted or empty sections and operations grant no permissions. A mode-only enforce policy treats every covered operation as a violation; hook capabilities determine enforcement.
Limits: 1,024 brace combinations per rule, 8,192 bytes per expanded expression register, and 262,144 bytes of authored policy content in Team Server.
Operation Index
| Section | Operations |
|---|---|
container | run, socket |
path | chroot, create, delete, execute, link, mount, open, overwrite, pivot, quota, rename, symlink, write |
ip | bind, connect |
unix | bind, connect |
socket | packet, raw, inject, sniff, stdio |
netlink | bind |
vsock | bind, connect |
hook | ptrace, traceme, wx, preload |
mmap | file |
kernel | ebpf, module, read |
ioctl | cmd |
privilege | escalate |
task | kill, rlimit, schedule, nice, pgroup |
sysv | shmem, msgqueue |
Common Patterns
These patterns apply across operation formats.
The all Wildcard
The string "all" matches any value for that position in a rule:
"connect": ["all"]Use all only where the broad match is intentional.
Pipe-Delimited Rules
Most rules use pipes (|) to separate components:
component1|component2|component3Example:
/usr/bin/curl|api.example.com|443The <anonymous> Value
The special value <anonymous> denotes a target that has no on-disk path. It appears across several operations, for example, fileless/memfd execution in path.execute and anonymous memory mappings in mmap.file. Authorize it only when your build legitimately performs such operations.
Brace Expansion
Use braces to match multiple values:
{value1,value2,value3}Examples:
{80,443}matches port 80 or 443{/bin,/usr/bin}/bashmatches/bin/bashor/usr/bin/bash{http,https}://example.commatches either protocol
Variables can be used inside braces:
{%workspace%,%home%}/binmatches the workspace or homebinsubdirectory{%home%/.local/bin,/usr/bin}/appmatches the app binary in either location
Whitespace and Escaping
Whitespace inside a value is significant. /srv/My Project/build names a directory called
My Project, and matches only that directory.
Whitespace is ignored only where the grammar already has a delimiter, so these are equivalent:
all|/usr/bin/bash
all | /usr/bin/bash
/usr/{bin,local/bin}/node
/usr/{ bin , local/bin }/nodeThe characters \ { } , % | are structure. To use one literally, escape it with a
backslash:
\\ a literal backslash
\{ a literal opening brace
\} a literal closing brace
\, a literal comma
\% a literal percent sign
\| a literal pipe, which does NOT separate components
\ a backslash followed by a space, keeping a space at the very
start or end of a value that would otherwise be trimmedAny other escape is rejected, so a mistyped path fails loudly instead of silently matching nothing.
JSON requires an additional level of escaping
A policy file is JSON, which has its own backslash escaping. One literal backslash in a path is
written as four in the file: "all|/srv/back\\\\slash" is the rule for /srv/back\slash.
No component may be empty. all| and a||b are rejected, because an empty component would match
everything rather than nothing.
Runtime Variables
Use %workspace%, %home%, %hostname%, %uid%, and %user% for job-specific values, for example %workspace%/build or %home%/.config. See runtime values and variable scopes.
Undefined Variables
Team Server expands custom variables from effective system/policy scope. It leaves the five runtime variables for the Sensor to bind at job start. Direct local policies support only those runtime variables.
Unresolved names reject the policy rather than expanding to empty paths:
unresolved variable 'cache' in '%cache%/files'%workspace%, %hostname%, and %uid% always have values. If the caller’s verified UID has no host account, %home% and %user% use literal <undefined>.
Derived Policies
The Sensor omits empty rule lists and simplifies observed rules. It can replace transient paths, including package-manager staging paths, with stable parents; path.rename widens source/destination together only when observed pairs support both. Redundancy removal compares every component, including numeric network containment. Brace consolidation preserves observed relationships without adding unobserved combinations.
Directory Prefix Matching
Every file-path argument supports directory prefix matching: a rule specifying a directory path matches any path beneath it. For example, /opt/myapp/tmp matches /opt/myapp/tmp/script12345.
One identity-style field is matched exactly rather than as a directory prefix: the path.quota device name.
Mode
Required field.
{
"mode": "observe"
}| Value | Description |
|---|---|
derive | Monitor all behavior and auto-generate policy rules. The Runtime Sensor outputs a derived policy when the job completes. |
observe | Log violations without blocking. Use for testing and refinement. |
enforce | Deny unauthorized operations through LSM hooks and, by default, terminate offending tasks on LSM and fallback paths. ENDURA_SENSOR_KILL_TASK=false disables termination while retaining direct denial. |
Policies are deny-by-default in enforce mode. Follow the policy lifecycle before enabling enforcement.
Container Operations
container.run
Specifies which container images can be executed.
Format: image|tag
"container": {
"run": [
"registry.example.com/web/nginx|1.0.0",
"gcr.io/myproject/myapp|v1.0",
"quay.io/podman/hello|all"
]
}container.socket
Controls privileged socket exposure to containers.
Format: image|tag
"container": {
"socket": [
"registry.example.com/build/docker|1.0.0",
"gcr.io/myproject/builder|all",
"all|production"
]
}Container-runtime socket access grants host-level control; permit it only when required.
File System Operations
path.execute
Specifies which programs can be executed.
Format: grandparent_path|parent_path|target_path
The fields are the caller’s parent executable, the calling executable, and the target executable. For example, /bin/sudo|/bin/bash|/usr/bin/whoami describes sudo → bash → whoami. These are executable identities; use file-access rules for Makefiles and source inputs. Derive script/interpreter chains from actual runs.
"path": {
"execute": [
"/bin/sudo|/bin/bash|/usr/bin/whoami",
"all|/bin/bash|%workspace%/scripts/build.sh",
"{/bin,/usr/bin}/bash|all|{/bin,/usr/bin}/{ls,cat,grep}"
]
}The special value <anonymous> matches fileless execution via memfd_create.
path.create
Controls file and directory creation.
Format: process_path|target_path
"path": {
"create": [
"/bin/touch|/var/log/app.log",
"/usr/bin/mkdir|%workspace%/build",
"all|/tmp"
]
}This operation covers creation of ordinary files, directories, cron entries, rc scripts, and service files.
path.delete
Controls file and directory deletion.
Format: process_path|target_path
"path": {
"delete": [
"/bin/rm|%workspace%/build",
"/usr/bin/make|%workspace%/obj",
"all|/tmp"
]
}path.link
Controls hard link creation.
Format: process_path|source_path|destination_path
"path": {
"link": [
"/bin/ln|%workspace%|%workspace%",
"/usr/bin/cp|%workspace%|%workspace%",
"all|all|/tmp"
]
}Hard links create additional directory entries that point to the same inode.
Hard links can create alternate paths to protected files; restrict them when controls depend on one location.
path.mount
Controls filesystem mount operations.
Format: process_path|source|mount_point
"path": {
"mount": [
"/bin/mount|/dev/sda1|/mnt",
"/bin/mount|/tmp/overlay|/tmp/merged",
"/usr/bin/runc|all|all"
]
}Mounts can replace visible content. Permit only required sources and mount points.
path.open
Controls file opens, including opens for reading or writing. This operation does not filter by the requested access mode. Receiving a file descriptor through a Unix socket can also trigger this check.
Format: process_path|file_path
"path": {
"open": [
"/bin/cat|/etc/passwd",
"/usr/bin/app|/var/lib/app/config.yaml",
"all|%workspace%"
]
}Use narrow process and path values for sensitive files such as /etc/shadow, SSH keys, and credentials.
path.overwrite
Controls writable O_TRUNC opens of existing, nonempty files. Empty files do not trigger it; path.write covers appends and in-place changes.
Format: process_path|file_path
"path": {
"overwrite": [
"/usr/bin/gcc|%workspace%/build/output.o",
"/usr/bin/cp|%workspace%",
"all|/tmp"
]
}path.write
Controls file modification.
Format: process_path|file_path
"path": {
"write": [
"/usr/bin/gcc|%workspace%/build/output.o",
"/usr/bin/npm|%workspace%/node_modules",
"all|/tmp"
]
}Restrict writes to system files, configuration, and binaries to the processes that require them.
path.quota
Controls disk quota operations.
Format: process_path|device|quota_command
"path": {
"quota": [
"/usr/bin/quota|sda1|getquota",
"all|sda1|setquota"
]
}Supported quota commands are sync, quotaon, quotaoff, getfmt, getinfo, setinfo, getquota, setquota, and getnextquota.
Quota changes can exhaust storage; restrict them to filesystem administrators.
path.rename
Controls file and directory renaming.
Format: process_path|old_path|new_path
"path": {
"rename": [
"/bin/mv|%workspace%|%workspace%",
"/usr/bin/install|all|/usr/local/bin",
"all|%workspace%|%workspace%"
]
}Rename operations can replace a target path atomically.
Authorize rename separately when replacing a target with a completed file.
path.symlink
Controls symbolic link creation.
Format: process_path|link_path
"path": {
"symlink": [
"/bin/ln|%workspace%",
"/usr/bin/npm|%workspace%/node_modules",
"all|/tmp"
]
}Unlike hard links, symbolic links can cross filesystem boundaries and point to targets that do not yet exist.
Symbolic links redirect path-based operations; restrict permitted link paths.
path.pivot
Controls pivot_root operations that change the filesystem root.
Format: process_path|old_root_path|new_root_path
"path": {
"pivot": [
"/usr/bin/runc|/tmp/old|/tmp/new",
"/usr/bin/containerd|all|all"
]
}Permit pivot_root only for processes that intentionally replace their filesystem root.
path.chroot
Controls chroot operations.
Format: process_path|chroot_path
"path": {
"chroot": [
"/usr/sbin/sshd|/var/chroot/ssh",
"/usr/bin/app|all"
]
}IP Networking
Control TCP/IP socket operations for both IPv4 and IPv6. Numeric ports range from 0 through 65535, inclusive. For ip.bind, port 0 asks the kernel to select an available ephemeral port.
ip.bind
Specifies which processes can listen for inbound connections.
Format: executable_path|ip_address/CIDR|port
"ip": {
"bind": [
"/usr/bin/nginx|0.0.0.0/0|{80,443}",
"/usr/bin/node|{127.0.0.1/32,::1/128}|3000",
"/usr/bin/node|::/128|0",
"all|::/0|8080"
]
}ip.connect
Specifies which outbound connections are permitted.
Format: executable_path|ip_address_or_hostname/CIDR|port
"ip": {
"connect": [
"/usr/bin/curl|{api.example.com,140.82.112.0/20,github-api-ip4}|443",
"all|140.82.112.0/20|443",
"/usr/bin/apt-get|archive.ubuntu.com|{80,443}"
]
}Hostnames and Provider Selectors
Brace members can be CIDRs, hostnames, provider selectors, or all; each is validated independently. all makes narrower alternatives redundant.
At each job start, ordinary hostnames resolve to exact IPv4/IPv6 addresses as absolute DNS names, without search domains. Use fully qualified names. Lookup failure does not reject the policy: ip.connect can learn addresses later; ip.bind remains unresolved until reload.
Live ip.connect learning preserves executable/port restrictions within the current job. It never expands ip.bind after load:
| Source | Coverage and limits |
|---|---|
| Packet DNS | Correlated UDP port 53 A/AAAA queries and answers; unambiguous CNAME chains up to eight aliases; up to 64 answer records per response. TCP, TLS, and HTTPS DNS packets are excluded. |
| Host resolver hooks | Successful host-glibc getaddrinfo results expand rules before the application resumes. Includes hosts files, resolve NSS, nscd/SSSD caches, and encrypted-DNS results returned through that function. |
Resolver hooks use PID 1’s glibc through configured host procfs. Private container libc, musl, static resolvers, gethostbyname*, and direct NSS calls are excluded. Restart the Sensor after replacing host glibc.
Each resolver call examines up to 32 result nodes, captures up to eight unique addresses, and considers the first eight stored hostname templates matching exact executables or all. Storage order need not match policy order. Keep each hostname within eight templates for reliable resolver expansion; directory-prefix and additional templates depend on packet DNS.
A hostname permits at most 16 live executable/port templates; a job permits 4,096 total. Exceeding these rejects loading with domain <domain> exceeds 16 live ip.connect templates or policy exceeds 4096 live ip.connect templates.
Provider selectors use startup CIDR feeds for AWS, Azure, GCP, GitHub, and Linode, never hostname lookup. Ranges remain fixed until restart; failed feeds leave empty selectors. Derivation retains observed DNS names and adds provider aliases only for downloaded ranges. Otherwise it uses an observed or reverse-DNS name, or exact /32//128 address.
See feed access and trust. Use explicit CIDRs when coverage must not depend on DNS or feeds.
Unix Domain Sockets
unix.bind
Specifies which processes can create Unix sockets.
Format: process_path|socket_path
"unix": {
"bind": [
"/usr/bin/app|/tmp/app.sock",
"/usr/bin/postgres|/var/run/postgresql/.s.PGSQL.5432"
]
}unix.connect
Specifies which processes can connect to Unix sockets.
Format: process_path|socket_path
"unix": {
"connect": [
"/usr/bin/docker|/var/run/docker.sock",
"/usr/bin/psql|/var/run/postgresql/.s.PGSQL.5432"
]
}Advanced Socket Operations
Control raw sockets, packet manipulation, and process I/O backed by an AF_INET or AF_INET6 socket, features commonly used by network tools and potentially by attack tools.
socket.packet
Controls packet-level network access at the data link layer.
Format: process_path
"socket": {
"packet": [
"/usr/bin/tcpdump",
"/usr/bin/wireshark"
]
}Provides raw Ethernet frame access. Used by packet capture tools.
socket.raw
Controls raw IP socket creation.
Format: process_path
"socket": {
"raw": [
"/usr/bin/ping",
"/usr/bin/traceroute"
]
}Used for ICMP tools, port scanning, and custom protocols. Bypasses the TCP/UDP layer.
socket.inject
Controls packet injection capabilities.
Format: process_path
"socket": {
"inject": [
"/usr/bin/hping3",
"/usr/bin/nmap"
]
}Enables IP header modification, broadcast, and TX ring operations.
socket.sniff
Controls promiscuous mode (network sniffing).
Format: process_path
"socket": {
"sniff": [
"/usr/bin/tcpdump",
"/usr/bin/tshark"
]
}Promiscuous mode exposes other processes’ traffic; restrict it to approved diagnostics.
socket.stdio
Controls execution with stdin, stdout, or stderr backed by an AF_INET or AF_INET6 socket.
Format: process_path|target_path
process_path performs execution; target_path is the executed program.
"socket": {
"stdio": [
"/usr/sbin/xinetd|/usr/sbin/in.telnetd",
"/usr/lib/systemd/systemd|/usr/libexec/myservice",
"all|/usr/sbin/sshd"
]
}This covers reverse shells (bash -i >& /dev/tcp/..., nc -e, socat) and legitimate socket-activated services (inetd/xinetd, Accept=yes systemd units, sshd children). Permit required pairs explicitly. Unix domain socket-backed streams are excluded.
Review network-backed streams and permit only required service/session pairs.
Netlink Sockets
Control kernel-userspace communication for network configuration.
netlink.bind
Specifies which processes can create netlink sockets.
Format: process_path
"netlink": {
"bind": [
"/usr/bin/ip",
"/usr/bin/ss",
"/usr/sbin/NetworkManager"
]
}Used by network tools like ip, ss, and network managers for kernel communication.
Virtual Sockets (vsock)
Control VM-to-host and VM-to-VM communication.
vsock.bind
Specifies which processes can bind vsock ports.
Format: process_path|port
"vsock": {
"bind": [
"/usr/bin/app|1234",
"/usr/bin/app|all"
]
}vsock.connect
Specifies which processes can connect via vsock.
Format: process_path|port
"vsock": {
"connect": [
"/usr/bin/app|1234"
]
}Used in VMware, KVM, and Firecracker for VM-host communication.
Process Hooks and Debugging
Control debugging tools and code injection mechanisms, commonly abused in attacks.
hook.ptrace
Controls process tracing operations.
Format: tracer_path|target_path
"hook": {
"ptrace": [
"/usr/bin/strace|all",
"/usr/bin/gdb|/usr/bin/myapp"
]
}This operation covers debugger attachment by tools such as gdb, strace, and ltrace.
hook.traceme
Controls anti-debugging via PTRACE_TRACEME. Detects processes that request their parent trace them, a technique used by malware to prevent debuggers and security tools from attaching.
Format: process_path
"hook": {
"traceme": [
"/usr/bin/myapp",
"%workspace%/build/output"
]
}hook.ptrace covers PTRACE_ATTACH/PTRACE_SEIZE; hook.traceme covers PTRACE_TRACEME. Authorize independently.
hook.wx
Controls writable and executable memory (W+X).
Format: process_path
"hook": {
"wx": [
"/usr/bin/java",
"/usr/bin/node",
"/usr/bin/python3"
]
}Writable/executable memory permits dynamic code execution; allow only required JIT runtimes or programs.
hook.preload
Controls execution pairs where the target is invoked with LD_PRELOAD set.
Format: current_path|target_path
"hook": {
"preload": [
"/usr/bin/bash|/usr/bin/myapp",
"all|/usr/bin/node",
"/usr/bin/make|{/usr/bin/gcc,/usr/bin/g++}"
]
}LD_PRELOAD can replace target functions; allow only required execution pairs.
Memory Operations
mmap.file
Controls mappings requested with both PROT_WRITE and PROT_EXEC; other mappings are excluded. Use hook.wx for W+X transitions through mprotect.
Format: process_path|mapped_file_path
"mmap": {
"file": [
"/usr/bin/app|/lib/x86_64-linux-gnu/libc.so.6",
"/usr/bin/app|<anonymous>"
]
}The special value <anonymous> matches anonymous mappings requested with both write and execute permissions.
Kernel Operations
kernel.ebpf
Specifies which processes can load eBPF programs.
Format: process_path
"kernel": {
"ebpf": [
"/usr/bin/bpftrace",
"/usr/bin/bpftool"
]
}Used for kernel tracing, packet filtering, and security monitoring.
kernel.module
Controls kernel requests to load a module by name, such as an operation that triggers automatic loading of a network module. It does not describe every explicit module-loading syscall.
Format: process_path|module_name
"kernel": {
"module": [
"/sbin/modprobe|nfs",
"/usr/sbin/ip|dummy",
"all|overlay"
]
}Kernel modules have unrestricted system access; permit only required processes and module names.
kernel.read
Controls which module files the kernel may read during module loading.
Format: process_path|module_path
"kernel": {
"read": [
"/sbin/insmod|/lib/modules/6.1/kernel/drivers/net/dummy.ko",
"/usr/bin/kmod|%workspace%/mymodule.ko",
"all|/lib/modules"
]
}kernel.read evaluates the module file read during loading. kernel.module evaluates a kernel request for a module name. These are separate checks; do not assume that permitting one covers the other or that every loading path triggers both.
IOCTL Operations
ioctl.cmd
Specifies permitted IOCTL commands.
Format: process_path|device_path|command_number
"ioctl": {
"cmd": [
"/usr/bin/app|/dev/tty|all",
"all|/dev/null|{21523,21584}",
"/usr/bin/app|all|12345"
]
}Use decimal command numbers. Hexadecimal strings such as 0x5401 are invalid.
Privilege Escalation
Control selected Linux capability checks.
privilege.escalate
Controls supported capability checks. It evaluates use, grants no capability bits, and cannot override operating-system denial.
Format: executable_path|{capabilities}
"privilege": {
"escalate": [
"/usr/bin/sudo|{setuid,setgid}",
"/usr/bin/chown|chown",
"/usr/bin/ip|netadmin"
]
}Supported capability names are lowercase and do not use the CAP_ prefix or underscores:
| Capability | Description |
|---|---|
chown | Change file ownership |
dacoverride | Bypass discretionary file-access checks |
fowner | Bypass checks that require matching the file owner |
setgid | Change process group identities |
setuid | Change process user identities |
netadmin | Perform network administration |
sysadmin | Perform broad system-administration operations |
Task/Process Management
task.kill
Specifies which processes can terminate others.
Format: killer_path|target_path
"task": {
"kill": [
"/usr/bin/kill|all",
"/usr/bin/systemctl|all",
"/usr/bin/app|/usr/bin/app"
]
}Monitors attempts to kill security tools and critical processes.
task.rlimit
Controls resource limit modifications.
Format: current_path|target_path|resource_type
"task": {
"rlimit": [
"/usr/bin/systemd|all|nofile",
"/usr/bin/app|all|{nofile,nproc}"
]
}Resource types:
| Resource | Description |
|---|---|
cpu | CPU time limit |
fsize | Maximum file size |
data | Process data-segment size |
stack | Stack size limit |
core | Core-dump size |
rss | Resident set size |
nproc | Number of processes |
nofile | Number of open file descriptors |
memlock | Locked memory |
as | Address-space size |
locks | File locks |
sigpending | Pending signals |
msgqueue | POSIX message-queue bytes |
nice | Process nice value |
rtprio | Real-time priority |
rttime | Real-time CPU time |
nlimits | Kernel resource-limit count |
task.schedule
Controls scheduling priority modifications.
Format: current_path|target_path
"task": {
"schedule": [
"/usr/bin/chrt|all",
"/usr/bin/app|all"
]
}task.nice
Controls nice value (CPU priority) modifications.
Format: current_path|target_path
"task": {
"nice": [
"/usr/bin/nice|all",
"/usr/bin/renice|all"
]
}Nice values range from -20 (highest priority) to +19 (lowest priority).
task.pgroup
Controls process group modifications.
Format: current_path|target_path
"task": {
"pgroup": [
"/bin/bash|all",
"/usr/bin/app|/usr/bin/app"
]
}System V IPC
sysv.shmem
Controls shared memory operations.
Format: process_path|memory_key
"sysv": {
"shmem": [
"/usr/bin/postgres|5432",
"/usr/bin/app|123456",
"all|789012"
]
}sysv.msgqueue
Controls message queue operations.
Format: process_path|message_key
"sysv": {
"msgqueue": [
"/usr/bin/app|123456",
"/usr/bin/worker|all",
"all|789012"
]
}