Protect sensitive environment variables on Linux using eBPF.
envblock is a companion to dirblock. While dirblock protects files, envblock protects the environment variables that many 2026 supply-chain attacks (Axios, Trivy/TeamPCP, Bitwarden CLI, Shai-Hulud variants, etc.) aggressively steal.
Developer, admin, and CI/CD workflows still pass a lot of authority through environment variables: cloud keys, GitHub tokens, npm tokens, Vault tokens, kube credentials, AI provider keys, Terraform variables, deployment credentials, and more.
The risky process is often not the trusted top-level tool. It is what that tool spawns: hooks, plugins, package scripts, interpreters, shell snippets, test runners, linters, build steps, credential helpers, postinstall scripts, and CI job steps.
envblock limits that ambient inheritance. A process may carry a secret only
when the target executable is trusted to receive it and the checked caller chain
is trusted to carry it. That lets a developer shell, admin terminal, or CI job
keep powerful variables available for known tools without automatically handing
them to every descendant process.
The goal is not to hide secrets from the user. It is to stop ambient credentials from becoming ambient authority for every subprocess.
envblock protects exported environment variables as they cross execve into
child processes. It does not manage where secrets come from: .env files,
direnv, CI variable stores, Vault, cloud secret managers, encrypted files, and
language libraries are all outside its source-of-secret model.
That boundary is still useful for CI/CD. Once a runner, shell, or deploy job has received secrets as environment variables, envblock can reduce RCE blast radius by poisoning those values for unapproved child executables.
- Runs on
execve— poisons sensitive variables before they are passed to the new process. - Configurable munging patterns (e.g. keep
AKIAprefix on AWS keys, then fill the rest with garbage). - Sends random printable junk by default so stolen credentials look real but are useless.
- Reuses the same ancestry/profile concept as dirblock.
- Very low overhead.
envblock needs a Linux kernel with BTF available at
/sys/kernel/btf/vmlinux, plus a toolchain capable of building BPF CO-RE
programs and a libbpf-linked userspace loader.
Tested build/load environments:
- Arch Linux, kernel
6.19.12-arch1-1 - Arch Linux, kernel
7.0.3-arch1-1 - Ubuntu 22.04.5 LTS, kernel
6.8.0-111-genericwith libbpf1.4.0installed under/usr/local - Ubuntu 24.04.4 LTS, kernel
6.8.0-111-generic
See UBUNTU_NOTES.md for detailed Ubuntu 22.04/24.04 setup, including the libbpf ABI mismatch on 22.04 and how to work around it.
Arch Linux:
sudo pacman -S --needed bpf clang llvm libbpf elfutils zlib pkgconf base-develUbuntu 24.04:
sudo apt update
sudo apt install -y \
clang llvm linux-tools-common linux-tools-$(uname -r) \
libbpf-dev libelf-dev zlib1g-dev pkg-config build-essentialOn Ubuntu, if bpftool is not on PATH, run make with the kernel tools
directory first:
PATH="/usr/lib/linux-tools/$(uname -r):$PATH" makeenvblock is installed by hand so every privileged step is explicit: you clone and review the source, build it, and grant capabilities yourself.
Build and test from the repository root:
git clone https://github.com/roku-oss/envblock
cd envblock
make
sudo setcap cap_bpf,cap_perfmon,cap_sys_admin,cap_sys_ptrace+eip ./envblock
make test
./envblock --config ./config/envblock.tomlRe-run setcap after every rebuild because linking replaces ./envblock and
clears file capabilities. make test checks for required capabilities and
prints the setcap command if they are missing. The checked-in sample config
starts in dry_run mode.
When you are ready to daily-drive envblock, install the binary, starter
config, and wrappers into your user environment:
make install
make install-wrappers \
WRAPPER_DIR="$HOME/.local/share/envblock/wrappers" \
WRAPPED_TOOLS="aws kubectl k9s gh git glab npm node terraform docker podman codex claude vault"
sudo setcap cap_bpf,cap_perfmon,cap_sys_admin,cap_sys_ptrace+eip "$HOME/.local/bin/envblock"
export PATH="$HOME/.local/share/envblock/wrappers:$HOME/.local/bin:$PATH"
envblockmake install installs envblock to ~/.local/bin/envblock and installs
config/envblock.toml to ~/.config/envblock/envblock.toml, replacing any
existing user config.
Put the wrapper directory before ~/.local/bin and before any real tools it
wraps. In shell startup files, place the wrapper PATH line after other tools that
prepend to PATH so they do not move ahead of envblock wrappers later.
If you are using an AI coding assistant to build, test, tune, or configure
envblock, start with this prompt:
Read AI_HELPER.md and use it to help me set up envblock on this machine.
Start from config/envblock.toml, then copy or adapt it to:
~/.config/envblock/envblock.toml
Key sections:
[profiles]— reusable executable groups for entry and chain roles[env] sensitive— exact variable names to protect[env.munging]— how to mangle each variable (highly recommended)[env.entry]— which targets may directly receive each real value[env.chain]— which callers/ancestors may carry each real value
See CONFIGURING.md for the policy model and worked configuration examples, and WRAPPERS.md for wrapper setup.
- At startup, userspace reads the config, precomputes sensitive
VAR=lookups, and stats role-configured executables into(mount_id, inode)identities with entry and chain bitmasks. - On
execve/execveat, eBPF stashesenvp, then evaluates policy fromfexit/do_open_execatafter the kernel has opened the target executable. - For sensitive variables, eBPF requires the target executable to be trusted
under
[env.entry]and every checked caller/ancestor executable to be trusted under[env.chain]. - If not allowed, it preserves any configured literal prefix and overwrites the rest with random printable characters before the child receives the environment.
This makes stolen environment variables much less useful to attackers while keeping most legitimate workflows working.
See ARCHITECTURE.md for the full picture, with diagrams, including how wrappers fit in for script- and runtime-backed tools.
Allowlisted executable paths are trusted by the file identity that exists when
envblock starts or reloads, not by path string alone. For example, if
~/.local/bin/myApp is allowlisted, envblock.cpp resolves and stats that path
and sends its inode identity to BPF.
If that binary is later overwritten, whether by a normal update or a malicious
replacement, the new file gets a different inode. The running BPF policy will
not automatically trust it. That is intentional: replacement breaks trust until
the user explicitly reloads or restarts envblock.
To intentionally accept updated allowlisted executables:
kill -HUP <envblock-pid>For the recommended dry-run rollout, run envblock in a tmux session with
dry_run = true, watch the would-poison logs, update the config as needed, and
reload only when you intentionally accept the executable set.
Install standard per-user wrappers:
make install-wrappers \
WRAPPER_DIR="$HOME/.local/share/envblock/wrappers" \
WRAPPED_TOOLS="aws kubectl k9s gh git glab npm node terraform docker podman codex claude vault"make install-wrappers installs a wrapper only for tools that resolve to
interpreted scripts. ELF binaries are skipped (they can be trusted directly)
and names not found in PATH are skipped, so hosts without terraform or
podman do not get confusing dead wrappers.
Append this near the end of ~/.bashrc, after other tools that prepend to
PATH:
# set PATH so it includes envblock wrappers
if [ -d "$HOME/.local/share/envblock/wrappers" ] ; then
PATH="$HOME/.local/share/envblock/wrappers:$PATH"
fiFor setup, policy examples, and custom wrapper creation, see WRAPPERS.md. Administrators rolling wrappers out across users or CI should also read ADMIN_GUIDE.md.
This README describes the single-user workflow. For multi-user hosts, containers, and CI/CD runners, see ADMIN_GUIDE.md.
Requires:
clang+ LLVM (for BPF)libbpf-devbpftool- Kernel with BTF (
/sys/kernel/btf/vmlinux)
makeSee ARCHITECTURE.md for how envblock works conceptually, and CONTRIBUTING.md for BPF map layout and development workflow.
This is currently an experimental companion to dirblock. See test_results.md for current validation coverage and eBPF.md for verifier limits and tuning guidance.
Contributions and ideas welcome (especially around ancestry integration and config generation).
Apache-2.0. See LICENSE.