Development Guide
October 4, 2025 · View on GitHub
This guide provides instructions for setting up a development environment and contributing to mori.
Prerequisites
System Requirements
- OS: Linux (kernel 5.10+)
- Architecture: x86_64 or aarch64
- Kernel Features:
CONFIG_BPF_LSM=yCONFIG_CGROUP_BPF=y- cgroup v2 enabled
Verify kernel support:
# Check kernel version
uname -r
# Check if BPF LSM is enabled
$ zcat /proc/config.gz | grep CONFIG_BPF_LSM
CONFIG_BPF_LSM=y
# Check if BPF is in LSM list
cat /sys/kernel/security/lsm
Required Tools
Nightly Toolchain for eBPF (Linux only)
eBPF programs require Rust nightly with BPF target support:
# Install nightly toolchain with rust-src component
rustup toolchain install nightly-x86_64-unknown-linux-gnu \
--profile minimal \
--component rust-src
# Or for aarch64
rustup toolchain install nightly-aarch64-unknown-linux-gnu \
--profile minimal \
--component rust-src
bpf-linker
Required for linking eBPF programs:
cargo install bpf-linker
aya-tool
Required for generating vmlinux.rs from kernel BTF (BPF Type Format):
cargo install bpf-linker bindgen-cli
cargo install --git https://github.com/aya-rs/aya -- aya-tool
vmlinux.rs contains Rust bindings for kernel types and is used by eBPF programs
to safely access kernel structures. This file is automatically generated during
the build process from the running kernel's BTF information (/sys/kernel/btf/vmlinux).
Note: The generated vmlinux.rs is kernel-version specific and is created in
the build output directory (OUT_DIR). You don't need to manually generate it -
the mori-bpf/build.rs script handles this automatically using aya-tool.
BPF Development Tools (Ubuntu/Debian)
sudo apt-get update
sudo apt-get install -y \
libbpf-dev \
linux-tools-common \
linux-tools-$(uname -r) \
clang \
llvm
Building
The build process automatically:
- Compiles eBPF programs in
mori-bpf/using the nightly toolchain - Embeds the compiled eBPF bytecode into the main binary
- Builds the userspace program
Build Artifacts
- Userspace binary:
target/release/mori - eBPF ELF (intermediate):
target/bpfel-unknown-none/release/mori-bpf
Testing
Install cargo-nextest
cargo install cargo-nextest --locked
Run All Tests
# test runner
make test
Code Coverage
# Generate coverage report
make test-cov
eBPF Development
eBPF Program Structure
eBPF programs are in the mori-bpf/ workspace member:
mori-bpf/
├── src/
│ └── main.rs # eBPF programs (network, file control)
├── Cargo.toml
└── build.rs # Generates vmlinux.rs automatically
vmlinux.rs - Kernel Type Bindings
vmlinux.rs is automatically generated by mori-bpf/build.rs during the build
process from the running kernel's BTF (BPF Type Format) information.
Build-time generation:
- The
build.rsscript callsaya-tool generate file pathto create kernel type bindings - Generated types are written to
OUT_DIR/vmlinux.rs - The eBPF code includes it using
include!(concat!(env!("OUT_DIR"), "/vmlinux.rs")) - This ensures bindings always match your current kernel
Required types: Currently, the build script generates bindings for:
file: For file access control LSM hookspath: For file path operations
Adding new types:
If you need additional kernel types, update mori-bpf/build.rs:
# Add new type to the generate command
.args(["generate", "file", "path", "your_new_type"])
Usage in eBPF code:
mod vmlinux {
include!(concat!(env!("OUT_DIR"), "/vmlinux.rs"));
}
use vmlinux::{file, path};
// Access kernel structures with type safety
let file_ptr = unsafe { ctx.arg::<*const file>(0) };
let path_ptr = unsafe { &(*file_ptr).f_path as *const path };
Compiling eBPF Programs
eBPF programs are automatically compiled when building the main project. To compile them separately:
cd mori-bpf
cargo +nightly build --release --target bpfel-unknown-none
Debugging eBPF Programs
Using bpf_printk (eBPF side)
use aya_ebpf::macros::map;
use aya_ebpf::helpers::bpf_printk;
bpf_printk!(b"Debug message: value=%d", value);
View output:
sudo cat /sys/kernel/debug/tracing/trace_pipe
Using aya-log (Recommended)
use aya_log_ebpf::info;
info!(ctx, "Path blocked: {}", path);
User-space logger:
use aya_log::BpfLogger;
BpfLogger::init(&mut bpf).unwrap();
Verifier Errors
If you encounter eBPF verifier errors:
- Check the verifier log in the error message
- Ensure loops have compile-time known bounds
- Avoid variable-offset array access
- Keep stack usage under 512 bytes
- Limit instruction count (1M instructions max)
Common fixes:
- Use fixed-size loops:
for i in 0..CONST_SIZE - Use conditional writes instead of variable offsets:
if i >= offset { array[i] = value } - Split complex programs into smaller helper functions
Running mori
Network Control
# Allow specific domains
sudo ./target/release/mori --allow-network example.com -- curl https://example.com
# Allow all network access
sudo ./target/release/mori --allow-network-all -- curl https://example.com
File Access Control
# Deny read access to a file
sudo ./target/release/mori --deny-file-read /etc/shadow -- cat /etc/shadow
# Deny write access
sudo ./target/release/mori --deny-file-write /tmp/test.txt -- touch /tmp/test.txt
# Deny both read and write
sudo ./target/release/mori --deny-file /etc/hosts -- cat /etc/hosts
Using Config File
# Create config.toml
cat > config.toml <<EOF
[network]
allow = ["example.com", "192.0.2.1"]
[file]
deny_read = ["/etc/shadow", "/home/user/.ssh"]
EOF
# Run with config
sudo ./target/release/mori --config config.toml -- bash
Resources
eBPF Learning Resources
Kernel Documentation
Troubleshooting
Permission Denied Errors
On Linux, mori requires root privileges to:
- Load eBPF programs (CAP_BPF, CAP_SYS_ADMIN)
- Create and manage cgroups (CAP_SYS_ADMIN)
- Attach LSM hooks (CAP_BPF, CAP_NET_ADMIN)
Always run with sudo:
sudo ./target/release/mori --allow-network example.com -- curl https://example.com
cgroup Not Found Errors
Ensure cgroup v2 is mounted:
# Check if cgroup v2 is mounted
mount | grep cgroup2
# If not mounted, mount it
sudo mount -t cgroup2 none /sys/fs/cgroup
License
mori is dual-licensed under the MIT License and GPL-2.0 License. See LICENSE-MIT and LICENSE-GPL for details.