-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add eBPF program for controlling device access
This is a simple program which allows mknod, a standard list of devices to be allowed inside the container, and a hashmap mapping a list of devices to allwoed accesses. This allows runtime update on whether a device is allowed inside a container. It is automatically compiled with build.rs.
- Loading branch information
Showing
10 changed files
with
330 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
/target | ||
/ott | ||
/cgroup_device_filter/target | ||
/ott |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
use anyhow::{Context, Result}; | ||
|
||
fn main() -> Result<()> { | ||
// We need to rerun the build script if any files in the cgroup_device_filter change. | ||
for entry in walkdir::WalkDir::new("cgroup_device_filter") | ||
.into_iter() | ||
.filter_entry(|entry| { | ||
entry | ||
.file_name() | ||
.to_str() | ||
.map(|s| s != "target") | ||
.unwrap_or(true) | ||
}) | ||
{ | ||
let entry = entry?; | ||
if entry.file_type().is_file() { | ||
println!( | ||
"cargo:rerun-if-changed={}", | ||
entry.path().to_str().context("file name not UTF-8")? | ||
); | ||
} | ||
} | ||
|
||
// Run cargo to compile the eBPF program. | ||
let status = std::process::Command::new("cargo") | ||
.current_dir("cgroup_device_filter") | ||
.args(["build", "--release"]) | ||
.status()?; | ||
|
||
if !status.success() { | ||
anyhow::bail!("Failed to build eBPF program"); | ||
} | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[build] | ||
target = "bpfel-unknown-none" | ||
|
||
[unstable] | ||
build-std = ["core"] |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[package] | ||
name = "cgroup_device_filter" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
aya-bpf = { git = "https://github.com/aya-rs/aya.git" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[toolchain] | ||
channel = "nightly" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
#![no_std] | ||
#![no_main] | ||
|
||
use aya_bpf::bindings::{ | ||
BPF_DEVCG_ACC_MKNOD, BPF_DEVCG_DEV_BLOCK, BPF_DEVCG_DEV_CHAR, BPF_F_NO_PREALLOC, | ||
}; | ||
use aya_bpf::macros::{cgroup_device, map}; | ||
use aya_bpf::maps::HashMap; | ||
use aya_bpf::programs::DeviceContext; | ||
|
||
#[repr(C)] | ||
#[derive(Clone, Copy, PartialEq, Eq)] | ||
struct Device { | ||
/// Type of device. BPF_DEVCG_DEV_BLOCK or BPF_DEVCG_DEV_CHAR. | ||
ty: u32, | ||
major: u32, | ||
minor: u32, | ||
} | ||
|
||
const DEV_NULL: Device = Device { | ||
ty: BPF_DEVCG_DEV_CHAR, | ||
major: 1, | ||
minor: 3, | ||
}; | ||
|
||
const DEV_ZERO: Device = Device { | ||
ty: BPF_DEVCG_DEV_CHAR, | ||
major: 1, | ||
minor: 5, | ||
}; | ||
|
||
const DEV_FULL: Device = Device { | ||
ty: BPF_DEVCG_DEV_CHAR, | ||
major: 1, | ||
minor: 7, | ||
}; | ||
|
||
const DEV_RANDOM: Device = Device { | ||
ty: BPF_DEVCG_DEV_CHAR, | ||
major: 1, | ||
minor: 8, | ||
}; | ||
|
||
const DEV_URANDOM: Device = Device { | ||
ty: BPF_DEVCG_DEV_CHAR, | ||
major: 1, | ||
minor: 9, | ||
}; | ||
|
||
const DEV_TTY: Device = Device { | ||
ty: BPF_DEVCG_DEV_CHAR, | ||
major: 5, | ||
minor: 0, | ||
}; | ||
|
||
const DEV_CONSOLE: Device = Device { | ||
ty: BPF_DEVCG_DEV_CHAR, | ||
major: 5, | ||
minor: 1, | ||
}; | ||
|
||
const DEV_PTMX: Device = Device { | ||
ty: BPF_DEVCG_DEV_CHAR, | ||
major: 5, | ||
minor: 2, | ||
}; | ||
|
||
#[map(name = "DEVICE_PERM")] | ||
/// Hashmap storing a device -> permission mapping. | ||
/// | ||
/// This is modified from user-space to change permission. | ||
static DEVICE_PERM: HashMap<Device, u32> = HashMap::with_max_entries(256, BPF_F_NO_PREALLOC); | ||
|
||
#[cgroup_device] | ||
fn check_device(ctx: DeviceContext) -> i32 { | ||
// SAFETY: This is a POD supplied by the kernel. | ||
let ctx_dev = unsafe { *ctx.device }; | ||
let dev = Device { | ||
// access_type's lower 16 bits are the device type, upper 16 bits are the access type. | ||
ty: ctx_dev.access_type & 0xFFFF, | ||
major: ctx_dev.major, | ||
minor: ctx_dev.minor, | ||
}; | ||
let access = ctx_dev.access_type >> 16; | ||
|
||
// Always allow mknod, we restrict on access not on creation. | ||
// This is consistent with eBPF genereated by Docker. | ||
if matches!(dev.ty, BPF_DEVCG_DEV_BLOCK | BPF_DEVCG_DEV_CHAR) && access == BPF_DEVCG_ACC_MKNOD { | ||
return 1; | ||
} | ||
|
||
// Allow default devices for containers | ||
// https://github.com/opencontainers/runtime-spec/blob/main/config-linux.md | ||
match dev { | ||
DEV_NULL | DEV_ZERO | DEV_FULL | DEV_RANDOM | DEV_URANDOM => return 1, | ||
DEV_TTY | DEV_CONSOLE | DEV_PTMX => return 1, | ||
// Pseudo-PTY | ||
Device { | ||
ty: BPF_DEVCG_DEV_CHAR, | ||
major: 136, | ||
minor: _, | ||
} => return 1, | ||
_ => (), | ||
} | ||
|
||
// For extra devices, check the map. | ||
// SAFETY: we have BPF_F_NO_PREALLOC enabled so the map is safe to access concurrently. | ||
let device_perm = unsafe { DEVICE_PERM.get(&dev).copied() }; | ||
match device_perm { | ||
Some(perm) => (perm & access == access) as i32, | ||
None => 0, | ||
} | ||
} | ||
|
||
#[panic_handler] | ||
fn panic(_info: &core::panic::PanicInfo) -> ! { | ||
loop {} | ||
} |