Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c1fabb7

Browse files
Altug Bozkurtaltugbozkurt07
Altug Bozkurt
authored andcommittedMar 21, 2025·
lsm: cgroup attachment type support
1 parent 39e40ba commit c1fabb7

File tree

17 files changed

+601
-10
lines changed

17 files changed

+601
-10
lines changed
 

‎Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ rbpf = { version = "0.3.0", default-features = false }
9292
rustdoc-json = { version = "0.9.0", default-features = false }
9393
rustup-toolchain = { version = "0.1.5", default-features = false }
9494
rustversion = { version = "1.0.0", default-features = false }
95+
scopeguard = { version = "1.2.0" }
9596
syn = { version = "2", default-features = false }
9697
tempfile = { version = "3", default-features = false }
9798
test-case = { version = "3.1.0", default-features = false }

‎aya-ebpf-macros/src/lib.rs

+45
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod fentry;
1010
mod fexit;
1111
mod kprobe;
1212
mod lsm;
13+
mod lsm_cgroup;
1314
mod map;
1415
mod perf_event;
1516
mod raw_tracepoint;
@@ -34,6 +35,7 @@ use fentry::FEntry;
3435
use fexit::FExit;
3536
use kprobe::{KProbe, KProbeKind};
3637
use lsm::Lsm;
38+
use lsm_cgroup::LsmCgroup;
3739
use map::Map;
3840
use perf_event::PerfEvent;
3941
use proc_macro::TokenStream;
@@ -326,6 +328,49 @@ pub fn lsm(attrs: TokenStream, item: TokenStream) -> TokenStream {
326328
.into()
327329
}
328330

331+
/// Marks a function as an LSM program that can be attached to cgroups.
332+
/// This program will only trigger for workloads in the attached cgroups.
333+
/// Used to implement security policy and audit logging.
334+
///
335+
/// The hook name is the first and only argument to the macro.
336+
///
337+
/// LSM probes can be attached to the kernel's security hooks to implement mandatory
338+
/// access control policy and security auditing.
339+
///
340+
/// LSM probes require a kernel compiled with `CONFIG_BPF_LSM=y` and `CONFIG_DEBUG_INFO_BTF=y`.
341+
/// In order for the probes to fire, you also need the BPF LSM to be enabled through your
342+
/// kernel's boot paramters (like `lsm=lockdown,yama,bpf`).
343+
///
344+
/// # Minimum kernel version
345+
///
346+
/// The minimum kernel version required to use this feature is 6.0.
347+
///
348+
/// # Examples
349+
///
350+
/// ```no_run
351+
/// use aya_ebpf::{macros::lsm_cgroup, programs::LsmContext};
352+
///
353+
/// #[lsm_cgroup(hook = "file_open")]
354+
/// pub fn file_open(ctx: LsmContext) -> i32 {
355+
/// match unsafe { try_file_open(ctx) } {
356+
/// Ok(ret) => ret,
357+
/// Err(ret) => ret,
358+
/// }
359+
/// }
360+
///
361+
/// unsafe fn try_file_open(_ctx: LsmContext) -> Result<i32, i32> {
362+
/// Err(0)
363+
/// }
364+
/// ```
365+
#[proc_macro_attribute]
366+
pub fn lsm_cgroup(attrs: TokenStream, item: TokenStream) -> TokenStream {
367+
match LsmCgroup::parse(attrs.into(), item.into()) {
368+
Ok(prog) => prog.expand(),
369+
Err(err) => err.into_compile_error(),
370+
}
371+
.into()
372+
}
373+
329374
/// Marks a function as a [BTF-enabled raw tracepoint][1] eBPF program that can be attached at
330375
/// a pre-defined kernel trace point.
331376
///

‎aya-ebpf-macros/src/lsm.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ impl Lsm {
4444
} else {
4545
section_prefix.into()
4646
};
47+
let fn_name = &sig.ident;
4748
// LSM probes need to return an integer corresponding to the correct
4849
// policy decision. Therefore we do not simply default to a return value
4950
// of 0 as in other program types.
50-
let fn_name = &sig.ident;
5151
quote! {
5252
#[no_mangle]
5353
#[link_section = #section_name]

‎aya-ebpf-macros/src/lsm_cgroup.rs

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use std::borrow::Cow;
2+
3+
use proc_macro2::TokenStream;
4+
use quote::quote;
5+
use syn::{ItemFn, Result};
6+
7+
use crate::args::{err_on_unknown_args, pop_string_arg};
8+
9+
pub(crate) struct LsmCgroup {
10+
item: ItemFn,
11+
hook: Option<String>,
12+
}
13+
14+
impl LsmCgroup {
15+
pub(crate) fn parse(attrs: TokenStream, item: TokenStream) -> Result<Self> {
16+
let item = syn::parse2(item)?;
17+
let mut args = syn::parse2(attrs)?;
18+
let hook = pop_string_arg(&mut args, "hook");
19+
err_on_unknown_args(&args)?;
20+
21+
Ok(Self { item, hook })
22+
}
23+
24+
pub(crate) fn expand(&self) -> TokenStream {
25+
let Self { item, hook } = self;
26+
let ItemFn {
27+
attrs: _,
28+
vis,
29+
sig,
30+
block: _,
31+
} = item;
32+
let section_prefix = "lsm_cgroup";
33+
let section_name: Cow<'_, _> = if let Some(name) = hook {
34+
format!("{}/{}", section_prefix, name).into()
35+
} else {
36+
section_prefix.into()
37+
};
38+
let fn_name = &sig.ident;
39+
// LSM probes need to return an integer corresponding to the correct
40+
// policy decision. Therefore we do not simply default to a return value
41+
// of 0 as in other program types.
42+
quote! {
43+
#[no_mangle]
44+
#[link_section = #section_name]
45+
#vis fn #fn_name(ctx: *mut ::core::ffi::c_void) -> i32 {
46+
return #fn_name(::aya_ebpf::programs::LsmContext::new(ctx));
47+
48+
#item
49+
}
50+
}
51+
}
52+
}
53+
54+
#[cfg(test)]
55+
mod tests {
56+
use syn::parse_quote;
57+
58+
use super::*;
59+
60+
#[test]
61+
fn test_lsm_cgroup() {
62+
let prog = LsmCgroup::parse(
63+
parse_quote! {
64+
hook = "bprm_committed_creds",
65+
},
66+
parse_quote! {
67+
fn bprm_committed_creds(ctx: &mut ::aya_ebpf::programs::LsmContext) -> i32 {
68+
0
69+
}
70+
},
71+
)
72+
.unwrap();
73+
let expanded = prog.expand();
74+
let expected = quote! {
75+
#[no_mangle]
76+
#[link_section = "lsm_cgroup/bprm_committed_creds"]
77+
fn bprm_committed_creds(ctx: *mut ::core::ffi::c_void) -> i32 {
78+
return bprm_committed_creds(::aya_ebpf::programs::LsmContext::new(ctx));
79+
80+
fn bprm_committed_creds(ctx: &mut ::aya_ebpf::programs::LsmContext) -> i32 {
81+
0
82+
}
83+
}
84+
};
85+
assert_eq!(expected.to_string(), expanded.to_string());
86+
}
87+
}

‎aya-obj/src/obj.rs

+26-4
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ pub enum ProgramSection {
275275
Lsm {
276276
sleepable: bool,
277277
},
278+
LsmCgroup,
278279
BtfTracePoint,
279280
FEntry {
280281
sleepable: bool,
@@ -436,6 +437,7 @@ impl FromStr for ProgramSection {
436437
"raw_tp" | "raw_tracepoint" => RawTracePoint,
437438
"lsm" => Lsm { sleepable: false },
438439
"lsm.s" => Lsm { sleepable: true },
440+
"lsm_cgroup" => LsmCgroup,
439441
"fentry" => FEntry { sleepable: false },
440442
"fentry.s" => FEntry { sleepable: true },
441443
"fexit" => FExit { sleepable: false },
@@ -2186,10 +2188,7 @@ mod tests {
21862188
assert_matches!(
21872189
obj.programs.get("foo"),
21882190
Some(Program {
2189-
section: ProgramSection::Lsm {
2190-
sleepable: false,
2191-
..
2192-
},
2191+
section: ProgramSection::Lsm { sleepable: false },
21932192
..
21942193
})
21952194
);
@@ -2221,6 +2220,29 @@ mod tests {
22212220
);
22222221
}
22232222

2223+
#[test]
2224+
fn test_parse_section_lsm_cgroup() {
2225+
let mut obj = fake_obj();
2226+
fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
2227+
2228+
assert_matches!(
2229+
obj.parse_section(fake_section(
2230+
EbpfSectionKind::Program,
2231+
"lsm_cgroup/foo",
2232+
bytes_of(&fake_ins()),
2233+
None
2234+
)),
2235+
Ok(())
2236+
);
2237+
assert_matches!(
2238+
obj.programs.get("foo"),
2239+
Some(Program {
2240+
section: ProgramSection::LsmCgroup,
2241+
..
2242+
})
2243+
);
2244+
}
2245+
22242246
#[test]
22252247
fn test_parse_section_btf_tracepoint() {
22262248
let mut obj = fake_obj();

‎aya/src/bpf.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ use crate::{
2424
programs::{
2525
BtfTracePoint, CgroupDevice, CgroupSkb, CgroupSkbAttachType, CgroupSock, CgroupSockAddr,
2626
CgroupSockopt, CgroupSysctl, Extension, FEntry, FExit, Iter, KProbe, LircMode2, Lsm,
27-
PerfEvent, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier,
28-
SkLookup, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
27+
LsmCgroup, PerfEvent, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint,
28+
SchedClassifier, SkLookup, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint,
29+
UProbe, Xdp,
2930
},
3031
sys::{
3132
bpf_load_btf, is_bpf_cookie_supported, is_bpf_global_data_supported,
@@ -400,6 +401,7 @@ impl<'a> EbpfLoader<'a> {
400401
| ProgramSection::FEntry { sleepable: _ }
401402
| ProgramSection::FExit { sleepable: _ }
402403
| ProgramSection::Lsm { sleepable: _ }
404+
| ProgramSection::LsmCgroup
403405
| ProgramSection::BtfTracePoint
404406
| ProgramSection::Iter { sleepable: _ } => {
405407
return Err(EbpfError::BtfError(err))
@@ -645,6 +647,9 @@ impl<'a> EbpfLoader<'a> {
645647
}
646648
Program::Lsm(Lsm { data })
647649
}
650+
ProgramSection::LsmCgroup => Program::LsmCgroup(LsmCgroup {
651+
data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
652+
}),
648653
ProgramSection::BtfTracePoint => Program::BtfTracePoint(BtfTracePoint {
649654
data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
650655
}),

‎aya/src/programs/lsm_cgroup.rs

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//! LSM probes.
2+
3+
use std::os::fd::AsFd;
4+
5+
use aya_obj::{
6+
btf::{Btf, BtfKind},
7+
generated::{bpf_attach_type::BPF_LSM_CGROUP, bpf_prog_type::BPF_PROG_TYPE_LSM},
8+
};
9+
10+
use crate::{
11+
programs::{define_link_wrapper, load_program, FdLink, FdLinkId, ProgramData, ProgramError},
12+
sys::{bpf_link_create, BpfLinkCreateArgs, LinkTarget, SyscallError},
13+
};
14+
15+
/// A program that attaches to Linux LSM hooks with per-cgroup attachment type. Used to implement security policy and
16+
/// audit logging.
17+
///
18+
/// LSM probes can be attached to the kernel's [security hooks][1] to implement mandatory
19+
/// access control policy and security auditing.
20+
///
21+
/// LSM probes require a kernel compiled with `CONFIG_BPF_LSM=y` and `CONFIG_DEBUG_INFO_BTF=y`.
22+
/// In order for the probes to fire, you also need the BPF LSM to be enabled through your
23+
/// kernel's boot paramters (like `lsm=lockdown,yama,bpf`).
24+
///
25+
/// # Minimum kernel version
26+
///
27+
/// The minimum kernel version required to use this feature is 6.0.
28+
///
29+
/// # Examples
30+
///
31+
/// ```no_run
32+
/// # #[derive(thiserror::Error, Debug)]
33+
/// # enum LsmError {
34+
/// # #[error(transparent)]
35+
/// # BtfError(#[from] aya::BtfError),
36+
/// # #[error(transparent)]
37+
/// # Program(#[from] aya::programs::ProgramError),
38+
/// # #[error(transparent)]
39+
/// # Ebpf(#[from] aya::EbpfError),
40+
/// # }
41+
/// # let mut bpf = Ebpf::load_file("ebpf_programs.o")?;
42+
/// use aya::{Ebpf, programs::LsmCgroup, BtfError, Btf};
43+
/// use std::fs::File;
44+
///
45+
/// let btf = Btf::from_sys_fs()?;
46+
/// let file = File::open("/sys/fs/cgroup/unified").unwrap();
47+
/// let program: &mut LsmCgroup = bpf.program_mut("lsm_prog").unwrap().try_into()?;
48+
/// program.load("security_bprm_exec", &btf)?;
49+
/// program.attach(file)?;
50+
/// # Ok::<(), LsmError>(())
51+
/// ```
52+
///
53+
/// [1]: https://elixir.bootlin.com/linux/latest/source/include/linux/lsm_hook_defs.h
54+
#[derive(Debug)]
55+
#[doc(alias = "BPF_PROG_TYPE_LSM")]
56+
pub struct LsmCgroup {
57+
pub(crate) data: ProgramData<LsmLink>,
58+
}
59+
60+
impl LsmCgroup {
61+
/// Loads the program inside the kernel.
62+
///
63+
/// # Arguments
64+
///
65+
/// * `lsm_hook_name` - full name of the LSM hook that the program should
66+
/// be attached to
67+
pub fn load(&mut self, lsm_hook_name: &str, btf: &Btf) -> Result<(), ProgramError> {
68+
self.data.expected_attach_type = Some(BPF_LSM_CGROUP);
69+
let type_name = format!("bpf_lsm_{lsm_hook_name}");
70+
self.data.attach_btf_id =
71+
Some(btf.id_by_type_name_kind(type_name.as_str(), BtfKind::Func)?);
72+
load_program(BPF_PROG_TYPE_LSM, &mut self.data)
73+
}
74+
75+
/// Attaches the program.
76+
///
77+
/// The returned value can be used to detach, see [LsmCgroup::detach].
78+
pub fn attach<T: AsFd>(&mut self, cgroup: T) -> Result<LsmLinkId, ProgramError> {
79+
let prog_fd = self.fd()?;
80+
let prog_fd = prog_fd.as_fd();
81+
let cgroup_fd = cgroup.as_fd();
82+
let attach_type = self.data.expected_attach_type.unwrap();
83+
let btf_id = self.data.attach_btf_id.ok_or(ProgramError::NotLoaded)?;
84+
let link_fd = bpf_link_create(
85+
prog_fd,
86+
LinkTarget::Fd(cgroup_fd),
87+
attach_type,
88+
0,
89+
Some(BpfLinkCreateArgs::TargetBtfId(btf_id)),
90+
)
91+
.map_err(|(_, io_error)| SyscallError {
92+
call: "bpf_link_create",
93+
io_error,
94+
})?;
95+
96+
self.data.links.insert(LsmLink::new(FdLink::new(link_fd)))
97+
}
98+
}
99+
100+
define_link_wrapper!(
101+
/// The link used by [LsmCgroup] programs.
102+
LsmLink,
103+
/// The type returned by [LsmCgroup::attach]. Can be passed to [LsmCgroup::detach].
104+
LsmLinkId,
105+
FdLink,
106+
FdLinkId,
107+
LsmCgroup,
108+
);

‎aya/src/programs/mod.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ pub mod kprobe;
5656
pub mod links;
5757
pub mod lirc_mode2;
5858
pub mod lsm;
59+
pub mod lsm_cgroup;
5960
pub mod perf_attach;
6061
pub mod perf_event;
6162
pub mod raw_trace_point;
@@ -105,6 +106,7 @@ pub use crate::programs::{
105106
links::{CgroupAttachMode, Link, LinkOrder},
106107
lirc_mode2::LircMode2,
107108
lsm::Lsm,
109+
lsm_cgroup::LsmCgroup,
108110
perf_event::{PerfEvent, PerfEventScope, PerfTypeId, SamplePolicy},
109111
probe::ProbeKind,
110112
raw_trace_point::RawTracePoint,
@@ -303,6 +305,8 @@ pub enum Program {
303305
RawTracePoint(RawTracePoint),
304306
/// A [`Lsm`] program
305307
Lsm(Lsm),
308+
/// A [`LsmCgroup`] program
309+
LsmCgroup(LsmCgroup),
306310
/// A [`BtfTracePoint`] program
307311
BtfTracePoint(BtfTracePoint),
308312
/// A [`FEntry`] program
@@ -340,6 +344,7 @@ impl Program {
340344
Self::PerfEvent(_) => ProgramType::PerfEvent,
341345
Self::RawTracePoint(_) => ProgramType::RawTracePoint,
342346
Self::Lsm(_) => ProgramType::Lsm,
347+
Self::LsmCgroup(_) => ProgramType::Lsm,
343348
// The following program types are a subset of `TRACING` programs:
344349
//
345350
// - `BPF_TRACE_RAW_TP` (`BtfTracePoint`)
@@ -379,6 +384,7 @@ impl Program {
379384
Self::PerfEvent(p) => p.pin(path),
380385
Self::RawTracePoint(p) => p.pin(path),
381386
Self::Lsm(p) => p.pin(path),
387+
Self::LsmCgroup(p) => p.pin(path),
382388
Self::BtfTracePoint(p) => p.pin(path),
383389
Self::FEntry(p) => p.pin(path),
384390
Self::FExit(p) => p.pin(path),
@@ -410,6 +416,7 @@ impl Program {
410416
Self::PerfEvent(mut p) => p.unload(),
411417
Self::RawTracePoint(mut p) => p.unload(),
412418
Self::Lsm(mut p) => p.unload(),
419+
Self::LsmCgroup(mut p) => p.unload(),
413420
Self::BtfTracePoint(mut p) => p.unload(),
414421
Self::FEntry(mut p) => p.unload(),
415422
Self::FExit(mut p) => p.unload(),
@@ -443,6 +450,7 @@ impl Program {
443450
Self::PerfEvent(p) => p.fd(),
444451
Self::RawTracePoint(p) => p.fd(),
445452
Self::Lsm(p) => p.fd(),
453+
Self::LsmCgroup(p) => p.fd(),
446454
Self::BtfTracePoint(p) => p.fd(),
447455
Self::FEntry(p) => p.fd(),
448456
Self::FExit(p) => p.fd(),
@@ -477,6 +485,7 @@ impl Program {
477485
Self::PerfEvent(p) => p.info(),
478486
Self::RawTracePoint(p) => p.info(),
479487
Self::Lsm(p) => p.info(),
488+
Self::LsmCgroup(p) => p.info(),
480489
Self::BtfTracePoint(p) => p.info(),
481490
Self::FEntry(p) => p.info(),
482491
Self::FExit(p) => p.info(),
@@ -788,6 +797,7 @@ impl_program_unload!(
788797
LircMode2,
789798
PerfEvent,
790799
Lsm,
800+
LsmCgroup,
791801
RawTracePoint,
792802
BtfTracePoint,
793803
FEntry,
@@ -829,6 +839,7 @@ impl_fd!(
829839
LircMode2,
830840
PerfEvent,
831841
Lsm,
842+
LsmCgroup,
832843
RawTracePoint,
833844
BtfTracePoint,
834845
FEntry,
@@ -935,6 +946,7 @@ impl_program_pin!(
935946
LircMode2,
936947
PerfEvent,
937948
Lsm,
949+
LsmCgroup,
938950
RawTracePoint,
939951
BtfTracePoint,
940952
FEntry,
@@ -974,8 +986,9 @@ impl_from_pin!(
974986
SkMsg,
975987
CgroupSysctl,
976988
LircMode2,
977-
PerfEvent,
978989
Lsm,
990+
LsmCgroup,
991+
PerfEvent,
979992
RawTracePoint,
980993
BtfTracePoint,
981994
FEntry,
@@ -1031,6 +1044,7 @@ impl_try_from_program!(
10311044
LircMode2,
10321045
PerfEvent,
10331046
Lsm,
1047+
LsmCgroup,
10341048
RawTracePoint,
10351049
BtfTracePoint,
10361050
FEntry,
@@ -1058,6 +1072,7 @@ impl_info!(
10581072
LircMode2,
10591073
PerfEvent,
10601074
Lsm,
1075+
LsmCgroup,
10611076
RawTracePoint,
10621077
BtfTracePoint,
10631078
FEntry,

‎init/src/main.rs

+8
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ fn run() -> anyhow::Result<()> {
8181
data: None,
8282
target_mode: None,
8383
},
84+
Mount {
85+
source: "cgroup2",
86+
target: "/sys/fs/cgroup",
87+
fstype: "cgroup2",
88+
flags: nix::mount::MsFlags::empty(),
89+
data: None,
90+
target_mode: None,
91+
},
8492
] {
8593
match target_mode {
8694
None => {

‎test/integration-ebpf/src/test.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
use aya_ebpf::{
55
bindings::xdp_action,
6-
macros::{kprobe, kretprobe, tracepoint, uprobe, uretprobe, xdp},
7-
programs::{ProbeContext, RetProbeContext, TracePointContext, XdpContext},
6+
macros::{kprobe, kretprobe, lsm, lsm_cgroup, tracepoint, uprobe, uretprobe, xdp},
7+
programs::{LsmContext, ProbeContext, RetProbeContext, TracePointContext, XdpContext},
88
};
99

1010
#[xdp]
@@ -44,6 +44,16 @@ pub fn test_uretprobe(_ctx: RetProbeContext) -> u32 {
4444
0
4545
}
4646

47+
#[lsm_cgroup(hook = "socket_bind")]
48+
pub fn test_lsmcgroup(_ctx: LsmContext) -> i32 {
49+
0
50+
}
51+
52+
#[lsm(hook = "socket_bind")]
53+
pub fn test_lsm(_ctx: LsmContext) -> i32 {
54+
-1
55+
}
56+
4757
#[cfg(not(test))]
4858
#[panic_handler]
4959
fn panic(_info: &core::panic::PanicInfo) -> ! {

‎test/integration-test/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ netns-rs = { workspace = true }
2424
object = { workspace = true, features = ["elf", "read_core", "std"] }
2525
rand = { workspace = true, features = ["thread_rng"] }
2626
rbpf = { workspace = true }
27+
scopeguard = { workspace = true }
2728
test-case = { workspace = true }
2829
test-log = { workspace = true, features = ["log"] }
2930
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "time"] }
3031
xdpilone = { workspace = true }
3132

33+
3234
[build-dependencies]
3335
anyhow = { workspace = true }
3436
aya-build = { path = "../../aya-build" }

‎test/integration-test/src/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod info;
55
mod iter;
66
mod load;
77
mod log;
8+
mod lsm;
89
mod raw_tracepoint;
910
mod rbpf;
1011
mod relocations;
+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use std::{
2+
fs::{write, File},
3+
io::ErrorKind,
4+
net::TcpListener,
5+
path::Path,
6+
};
7+
8+
use aya::{
9+
programs::{lsm_cgroup::LsmCgroup, Lsm},
10+
util::KernelVersion,
11+
Btf, Ebpf,
12+
};
13+
#[test]
14+
#[ignore = "LSM programs need a special kernel config, which is not supported by GitHub runners[waiting on PR: 1063]."]
15+
fn lsm_cgroup() {
16+
const CGROUP_PATH: &str = "/sys/fs/cgroup/lsm_cgroup_test";
17+
18+
let kernel_version = KernelVersion::current().unwrap();
19+
if kernel_version < KernelVersion::new(6, 0, 0) {
20+
eprintln!("skipping lsm_cgroup test on kernel {kernel_version:?}");
21+
return;
22+
}
23+
24+
let mut bpf: Ebpf = Ebpf::load(crate::TEST).unwrap();
25+
let prog: &mut LsmCgroup = bpf
26+
.program_mut("test_lsmcgroup")
27+
.unwrap()
28+
.try_into()
29+
.unwrap();
30+
let btf = Btf::from_sys_fs().expect("could not get btf from sys");
31+
prog.load("socket_bind", &btf).unwrap();
32+
33+
assert_matches::assert_matches!(TcpListener::bind("127.0.0.1:12345"), Ok(_));
34+
35+
let cgroup_dir = Path::new(CGROUP_PATH);
36+
std::fs::create_dir_all(CGROUP_PATH).expect("could not create cgroup dir");
37+
38+
let proc_path = cgroup_dir.join("cgroup.procs");
39+
40+
let _guard = scopeguard::guard((), |()| {
41+
std::fs::remove_file(&proc_path).unwrap();
42+
std::fs::remove_dir_all(cgroup_dir).unwrap();
43+
});
44+
45+
let link_id = prog.attach(File::open(cgroup_dir).unwrap()).unwrap();
46+
47+
let _guard = scopeguard::guard((), |()| {
48+
prog.detach(link_id).unwrap();
49+
});
50+
51+
assert_matches::assert_matches!(TcpListener::bind("127.0.0.1:12345"), Ok(_));
52+
53+
let pid = std::process::id();
54+
write(&proc_path, format!("{}\n", pid)).expect("could not write into procs file");
55+
56+
assert_matches::assert_matches!(TcpListener::bind("127.0.0.1:12345"), Err(e) => assert_eq!(
57+
e.kind(), ErrorKind::PermissionDenied));
58+
59+
60+
}
61+
62+
#[test]
63+
#[ignore = "LSM programs need a special kernel config, which is not supported by GitHub runners[waiting on PR: 1063]."]
64+
fn lsm() {
65+
let kernel_version = KernelVersion::current().unwrap();
66+
if kernel_version < KernelVersion::new(5, 7, 0) {
67+
eprintln!("skipping lsm test on kernel {kernel_version:?}");
68+
return;
69+
}
70+
71+
let mut bpf: Ebpf = Ebpf::load(crate::TEST).unwrap();
72+
let prog: &mut Lsm = bpf.program_mut("test_lsm").unwrap().try_into().unwrap();
73+
let btf = Btf::from_sys_fs().expect("could not get btf from sys");
74+
prog.load("socket_bind", &btf).unwrap();
75+
76+
assert_matches::assert_matches!(TcpListener::bind("127.0.0.1:12345"), Ok(_));
77+
78+
prog.attach().unwrap();
79+
80+
assert_matches::assert_matches!(TcpListener::bind("127.0.0.1:12345"), Err(e) => assert_eq!(
81+
e.kind(), ErrorKind::PermissionDenied)
82+
);
83+
}

‎xtask/public-api/aya-ebpf-macros.txt

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub proc macro aya_ebpf_macros::#[fexit]
1212
pub proc macro aya_ebpf_macros::#[kprobe]
1313
pub proc macro aya_ebpf_macros::#[kretprobe]
1414
pub proc macro aya_ebpf_macros::#[lsm]
15+
pub proc macro aya_ebpf_macros::#[lsm_cgroup]
1516
pub proc macro aya_ebpf_macros::#[map]
1617
pub proc macro aya_ebpf_macros::#[perf_event]
1718
pub proc macro aya_ebpf_macros::#[raw_tracepoint]

‎xtask/public-api/aya-obj.txt

+2
Original file line numberDiff line numberDiff line change
@@ -7658,6 +7658,7 @@ pub aya_obj::obj::ProgramSection::KRetProbe
76587658
pub aya_obj::obj::ProgramSection::LircMode2
76597659
pub aya_obj::obj::ProgramSection::Lsm
76607660
pub aya_obj::obj::ProgramSection::Lsm::sleepable: bool
7661+
pub aya_obj::obj::ProgramSection::LsmCgroup
76617662
pub aya_obj::obj::ProgramSection::PerfEvent
76627663
pub aya_obj::obj::ProgramSection::RawTracePoint
76637664
pub aya_obj::obj::ProgramSection::SchedClassifier
@@ -8520,6 +8521,7 @@ pub aya_obj::ProgramSection::KRetProbe
85208521
pub aya_obj::ProgramSection::LircMode2
85218522
pub aya_obj::ProgramSection::Lsm
85228523
pub aya_obj::ProgramSection::Lsm::sleepable: bool
8524+
pub aya_obj::ProgramSection::LsmCgroup
85238525
pub aya_obj::ProgramSection::PerfEvent
85248526
pub aya_obj::ProgramSection::RawTracePoint
85258527
pub aya_obj::ProgramSection::SchedClassifier

‎xtask/public-api/aya.txt

+199
Large diffs are not rendered by default.

‎xtask/src/run.rs

+2
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,8 @@ pub fn run(opts: Options) -> Result<()> {
413413
//
414414
// Heed the advice and boot with noapic. We don't know why this happens.
415415
kernel_args.push(" noapic");
416+
// We need to enable LSM bpf to be able to run LSM integration tests.
417+
kernel_args.push(" lsm=lockdown,capability,bpf");
416418
qemu.args(["-no-reboot", "-nographic", "-m", "512M", "-smp", "2"])
417419
.arg("-append")
418420
.arg(kernel_args)

0 commit comments

Comments
 (0)
Please sign in to comment.