Dynamic Runtime Policies in eBPF Using Bitmasks
This is a cross-post of https://substack.com/home/post/p-181032541 on eBPFChirp
Whenever one creates runtime security policies in eBPF, it usually looks something like this:
When a certain binary runs and tries to access a resource, we check it against an eBPF policy map, enforce the rules, and call it a day—right?
Not really.
Consider the following example:
sender.shis launched to move some files from machine A to machine B.To do that,
sender.shstartsreadandsend.shas a child process and passes it the files
In this case, when readandsend.sh is called by sender.sh, it should be allowed to read the provided files and connect to machine B’s IP.
But according to the current (static) policy stored in the eBPF map… it can’t.
So why not simply assign those file and network permissions to readandsend.sh as well?
The problem is that when permissions are assigned across different entities in a system, each entity should ideally have the minimal set of permissions required to function.
The primary reason for this is security: if a given entity is compromised, the attacker gains only a limited set of “powers.”
In the example above, we don’t want to grant readandsend.sh access to all files. Instead, we want it to inherit permissions from its parent only for the duration of the operation.
This may not be a common pattern, but supporting it would allow us to create policies that block certain operations by default and only unlock them when an authorized parent process invokes a child. In other words, capabilities remain restricted unless the correct process chain activates them.
So how hard to implement can that be?
And obviously, we can’t just invent a completely new structure. Instead, to make this possible, the eBPF map that holds the policy should:
Support taking the union of two map entries (for inheriting permissions)
Perform that union quickly (ideally O(1), or close to it)
Fit within the eBPF verifier’s limitations
So… this should be easy, right?
The “Easy” Solution
To achieve this, we represent our policy entries as a bitmask, where each bit corresponds to a resource the executable is allowed to access.
Instead of storing the resource identifiers directly, we store them like this:
Instead of a single eBPF map, we use two eBPF maps:
A map that assigns each resource an index (bit position)
A map that stores policies as bitmasks
But why?
When checking access for sender.sh, we:
Fetch the resource’s index
Retrieve the binary’s policy bitmask
Allow access if the bit at that index is set
But when sender.sh calls readandsend.sh, we can simply OR their policy bitmasks to create a combined policy and apply the same checks.
In other words, we dynamically adjust permissions based on who called the binary—while still keeping least privilege.
Here’s a small pseudocode snippet to wrap it up:
I hope you find this resource interesting. Keep an eye out for more updates and developments in eBPF in next week’s newsletter.
Until then, keep 🐝-ing!
Warm regards, Teodor and Neil






