Projects STRLCPY ebpfguard Commits 41b55bb7
🤬
  • Add `sb_mount`, `sb_remount` and `sb_umount` LSM hooks

    Add the ability to control mount operations and add a simple example.
  • Loading...
  • Michal Rostecki committed with vadorovsky 1 year ago
    41b55bb7
    1 parent f8915ceb
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■ ■
    README.md
    skipped 44 lines
    45 45   
    46 46  * [`bprm_check_security`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L62)
    47 47  * [`file_open`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L620)
    48  -* [`task_fix_setuid`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L709)
     48 +* [`sb_mount`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L128)
     49 +* [`sb_remount`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L147)
     50 +* [`sb_umount`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L159)
    49 51  * [`socket_bind`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L904)
    50 52  * [`socket_connect`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L912)
     53 +* [`task_fix_setuid`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L709)
    51 54   
    52 55  ## Examples
    53 56   
    skipped 34 lines
    88 91  ```bash
    89 92  [2023-04-22T20:51:01Z INFO file_open] file_open: pid=3001 subject=980333 path=9632
    90 93  [2023-04-22T20:51:03Z INFO file_open] file_open: pid=3010 subject=980298 path=9633
     94 +```
     95 +#### mount
     96 + 
     97 +The [mount](https://github.com/deepfence/ebpfguard/tree/main/examples/file_open)
     98 +example shows how to define a policy for `sb_mount`, `sb_remount` and
     99 +`sb_umount` LSM hooks as Rust code. It denies the mount operations for all
     100 +processes except for the optionally given one.
     101 + 
     102 +To try it out, let's create two directories:
     103 + 
     104 +```bash
     105 +$ mkdir /tmp/test1
     106 +$ mkdir /tmp/test2
     107 +```
     108 + 
     109 +Then run our example policy program, first without providing any binary to
     110 +allow mount for (so it's denied for all processes):
     111 + 
     112 +```bash
     113 +$ RUST_LOG=info cargo xtask run --example mount
     114 +```
     115 + 
     116 +Let's try to bind mount the first directory to the second one. It should
     117 +fail with the following error:
     118 + 
     119 +```bash
     120 +sudo mount --bind /tmp/test1 /tmp/test2
     121 +mount: /tmp/test2: permission denied.
     122 + dmesg(1) may have more information after failed mount system call.
     123 +```
     124 + 
     125 +And the policy program should show a log like:
     126 + 
     127 +```bash
     128 +[2023-04-23T21:02:58Z INFO mount] sb_mount: pid=17363 subject=678150
     129 +```
     130 + 
     131 +Now let's try to allow mount operations for the mount binary:
     132 + 
     133 +```bash
     134 +$ RUST_LOG=info cargo xtask run --example mount -- --allow /usr/bin/mount
     135 +```
     136 + 
     137 +And try to bind mount the first directory to the second one again. It should
     138 +succeed this time:
     139 + 
     140 +```bash
     141 +$ sudo mount --bind /tmp/test1 /tmp/test2
     142 +$ mount | grep test
     143 +tmpfs on /tmp/test2 type tmpfs (rw,nosuid,nodev,seclabel,nr_inodes=1048576,inode64)
    91 144  ```
    92 145   
    93 146  #### `task_fix_setuid`
    skipped 69 lines
  • ■ ■ ■ ■ ■
    ebpfguard/src/alerts.rs
    skipped 44 lines
    45 45  }
    46 46   
    47 47  #[derive(Debug)]
    48  -pub struct TaskFixSetuid {
     48 +pub struct SbMount {
    49 49   pub pid: u32,
    50 50   pub subject: PolicySubject,
    51  - pub old_uid: u32,
    52  - pub old_gid: u32,
    53  - pub new_uid: u32,
    54  - pub new_gid: u32,
     51 +}
     52 + 
     53 +impl Alert for SbMount {}
     54 + 
     55 +impl From<alerts::SbMount> for SbMount {
     56 + fn from(alert: alerts::SbMount) -> Self {
     57 + Self {
     58 + pid: alert.pid,
     59 + subject: PolicySubject::Binary(PathBuf::from(alert.binprm_inode.to_string())),
     60 + }
     61 + }
     62 +}
     63 + 
     64 +#[derive(Debug)]
     65 +pub struct SbRemount {
     66 + pub pid: u32,
     67 + pub subject: PolicySubject,
     68 +}
     69 + 
     70 +impl Alert for SbRemount {}
     71 + 
     72 +impl From<alerts::SbRemount> for SbRemount {
     73 + fn from(alert: alerts::SbRemount) -> Self {
     74 + Self {
     75 + pid: alert.pid,
     76 + subject: PolicySubject::Binary(PathBuf::from(alert.binprm_inode.to_string())),
     77 + }
     78 + }
     79 +}
     80 + 
     81 +#[derive(Debug)]
     82 +pub struct SbUmount {
     83 + pub pid: u32,
     84 + pub subject: PolicySubject,
    55 85  }
    56 86   
    57  -impl Alert for TaskFixSetuid {}
     87 +impl Alert for SbUmount {}
    58 88   
    59  -impl From<alerts::TaskFixSetuid> for TaskFixSetuid {
    60  - fn from(alert: alerts::TaskFixSetuid) -> Self {
     89 +impl From<alerts::SbUmount> for SbUmount {
     90 + fn from(alert: alerts::SbUmount) -> Self {
    61 91   Self {
    62 92   pid: alert.pid,
    63 93   subject: PolicySubject::Binary(PathBuf::from(alert.binprm_inode.to_string())),
    64  - old_uid: alert.old_uid,
    65  - old_gid: alert.old_gid,
    66  - new_uid: alert.new_uid,
    67  - new_gid: alert.new_gid,
    68 94   }
    69 95   }
    70 96  }
    skipped 41 lines
    112 138   }
    113 139  }
    114 140   
     141 +#[derive(Debug)]
     142 +pub struct TaskFixSetuid {
     143 + pub pid: u32,
     144 + pub subject: PolicySubject,
     145 + pub old_uid: u32,
     146 + pub old_gid: u32,
     147 + pub new_uid: u32,
     148 + pub new_gid: u32,
     149 +}
     150 + 
     151 +impl Alert for TaskFixSetuid {}
     152 + 
     153 +impl From<alerts::TaskFixSetuid> for TaskFixSetuid {
     154 + fn from(alert: alerts::TaskFixSetuid) -> Self {
     155 + Self {
     156 + pid: alert.pid,
     157 + subject: PolicySubject::Binary(PathBuf::from(alert.binprm_inode.to_string())),
     158 + old_uid: alert.old_uid,
     159 + old_gid: alert.old_gid,
     160 + new_uid: alert.new_uid,
     161 + new_gid: alert.new_gid,
     162 + }
     163 + }
     164 +}
     165 + 
  • ■ ■ ■ ■ ■ ■
    ebpfguard/src/hooks/bprm_check_security.rs
     1 +use aya::{
     2 + maps::{AsyncPerfEventArray, MapData},
     3 + programs::lsm::LsmLink,
     4 +};
     5 +use ebpfguard_common::alerts as ebpf_alerts;
     6 +use tokio::sync::mpsc::Receiver;
     7 + 
     8 +use crate::{alerts, error::EbpfguardError};
     9 + 
     10 +use super::perf_array_alerts;
     11 + 
     12 +pub struct BprmCheckSecurity {
     13 + #[allow(dead_code)]
     14 + pub(crate) program_link: Option<LsmLink>,
     15 + pub(crate) perf_array: AsyncPerfEventArray<MapData>,
     16 +}
     17 + 
     18 +impl BprmCheckSecurity {
     19 + pub async fn alerts(&mut self) -> Result<Receiver<alerts::BprmCheckSecurity>, EbpfguardError> {
     20 + perf_array_alerts::<ebpf_alerts::BprmCheckSecurity, alerts::BprmCheckSecurity>(
     21 + &mut self.perf_array,
     22 + )
     23 + .await
     24 + }
     25 +}
     26 + 
  • ■ ■ ■ ■ ■ ■
    ebpfguard/src/hooks/file_open.rs
     1 +use aya::{
     2 + maps::{AsyncPerfEventArray, HashMap, MapData},
     3 + programs::lsm::LsmLink,
     4 +};
     5 +use ebpfguard_common::{alerts as ebpf_alerts, policy as ebpf_policy};
     6 +use tokio::sync::mpsc::Receiver;
     7 + 
     8 +use crate::{alerts, error::EbpfguardError, policy};
     9 + 
     10 +use super::{perf_array_alerts, INODE_SUBJECT_MAP};
     11 + 
     12 +pub struct FileOpen {
     13 + #[allow(dead_code)]
     14 + pub(crate) program_link: Option<LsmLink>,
     15 + pub(crate) allowed_map: HashMap<MapData, u64, ebpf_policy::Paths>,
     16 + pub(crate) denied_map: HashMap<MapData, u64, ebpf_policy::Paths>,
     17 + pub(crate) perf_array: AsyncPerfEventArray<MapData>,
     18 +}
     19 + 
     20 +impl FileOpen {
     21 + pub async fn add_policy(&mut self, policy: policy::FileOpen) -> Result<(), EbpfguardError> {
     22 + let bin_inode = {
     23 + let mut map = INODE_SUBJECT_MAP.lock().await;
     24 + map.resolve_path(policy.subject)?
     25 + };
     26 + 
     27 + let allow: ebpf_policy::Paths = policy.allow.into();
     28 + let deny: ebpf_policy::Paths = policy.deny.into();
     29 + 
     30 + self.allowed_map.insert(bin_inode, allow, 0)?;
     31 + self.denied_map.insert(bin_inode, deny, 0)?;
     32 + 
     33 + Ok(())
     34 + }
     35 + 
     36 + pub async fn list_policies(&self) -> Result<Vec<policy::FileOpen>, EbpfguardError> {
     37 + let mut policies = Vec::new();
     38 + 
     39 + for res in self.allowed_map.iter() {
     40 + let (bin_inode, allow) = res?;
     41 + let deny = self.denied_map.get(&bin_inode, 0)?;
     42 + 
     43 + let subject = {
     44 + let map = INODE_SUBJECT_MAP.lock().await;
     45 + map.resolve_inode(bin_inode)
     46 + };
     47 + 
     48 + policies.push(policy::FileOpen {
     49 + subject,
     50 + allow: allow.into(),
     51 + deny: deny.into(),
     52 + });
     53 + }
     54 + 
     55 + Ok(policies)
     56 + }
     57 + 
     58 + pub async fn alerts(&mut self) -> Result<Receiver<alerts::FileOpen>, EbpfguardError> {
     59 + perf_array_alerts::<ebpf_alerts::FileOpen, alerts::FileOpen>(&mut self.perf_array).await
     60 + }
     61 +}
     62 + 
  • ■ ■ ■ ■ ■ ■
    ebpfguard/src/hooks/mod.rs
     1 +use std::fmt::Debug;
     2 + 
     3 +use aya::{
     4 + maps::{AsyncPerfEventArray, MapData},
     5 + util::online_cpus,
     6 +};
     7 +use bytes::BytesMut;
     8 +use ebpfguard_common::alerts as ebpf_alerts;
     9 +use once_cell::sync::Lazy;
     10 +use tokio::{
     11 + sync::{
     12 + mpsc::{self, Receiver},
     13 + Mutex,
     14 + },
     15 + task,
     16 +};
     17 + 
     18 +use crate::{alerts, error::EbpfguardError, policy, InodeSubjectMap};
     19 + 
     20 +pub mod bprm_check_security;
     21 +pub mod file_open;
     22 +pub mod sb_mount;
     23 +pub mod sb_remount;
     24 +pub mod sb_umount;
     25 +pub mod socket_bind;
     26 +pub mod socket_connect;
     27 +pub mod task_fix_setuid;
     28 + 
     29 +use bprm_check_security::BprmCheckSecurity;
     30 +use file_open::FileOpen;
     31 +use sb_mount::SbMount;
     32 +use socket_bind::SocketBind;
     33 +use socket_connect::SocketConnect;
     34 +use task_fix_setuid::TaskFixSetuid;
     35 + 
     36 +static INODE_SUBJECT_MAP: Lazy<Mutex<InodeSubjectMap>> =
     37 + Lazy::new(|| Mutex::new(InodeSubjectMap::default()));
     38 + 
     39 +pub struct All {
     40 + pub bprm_check_security: BprmCheckSecurity,
     41 + pub file_open: FileOpen,
     42 + pub sb_mount: SbMount,
     43 + pub sb_remount: sb_remount::SbRemount,
     44 + pub sb_umount: sb_umount::SbUmount,
     45 + pub socket_bind: SocketBind,
     46 + pub socket_connect: SocketConnect,
     47 + pub task_fix_setuid: TaskFixSetuid,
     48 +}
     49 + 
     50 +impl All {
     51 + pub async fn add_policy(&mut self, policy: policy::Policy) -> Result<(), EbpfguardError> {
     52 + match policy {
     53 + policy::Policy::FileOpen(policy) => self.file_open.add_policy(policy).await?,
     54 + policy::Policy::SbMount(policy) => self.sb_mount.add_policy(policy).await?,
     55 + policy::Policy::SbRemount(policy) => self.sb_remount.add_policy(policy).await?,
     56 + policy::Policy::SbUmount(policy) => self.sb_umount.add_policy(policy).await?,
     57 + policy::Policy::SocketBind(policy) => self.socket_bind.add_policy(policy).await?,
     58 + policy::Policy::SocketConnect(policy) => self.socket_connect.add_policy(policy).await?,
     59 + policy::Policy::TaskFixSetuid(policy) => {
     60 + self.task_fix_setuid.add_policy(policy).await?
     61 + }
     62 + }
     63 + 
     64 + Ok(())
     65 + }
     66 +}
     67 + 
     68 +pub async fn perf_array_alerts<E, U>(
     69 + perf_array: &mut AsyncPerfEventArray<MapData>,
     70 +) -> Result<Receiver<U>, EbpfguardError>
     71 +where
     72 + E: ebpf_alerts::Alert,
     73 + U: alerts::Alert + Debug + Send + From<E> + 'static,
     74 +{
     75 + let (tx, rx) = mpsc::channel(32);
     76 + 
     77 + let cpus = online_cpus()?;
     78 + for cpu_id in cpus {
     79 + let tx = tx.clone();
     80 + let mut buf = perf_array.open(cpu_id, None)?;
     81 + 
     82 + task::spawn(async move {
     83 + let mut buffers = (0..10)
     84 + .map(|_| BytesMut::with_capacity(1024))
     85 + .collect::<Vec<_>>();
     86 + loop {
     87 + let events = buf.read_events(&mut buffers).await.unwrap();
     88 + for buf in buffers.iter_mut().take(events.read) {
     89 + let alert: U = {
     90 + let ptr = buf.as_ptr() as *const E;
     91 + let alert = unsafe { ptr.read_unaligned() };
     92 + alert.into()
     93 + };
     94 + tx.send(alert).await.unwrap();
     95 + }
     96 + }
     97 + });
     98 + }
     99 + 
     100 + Ok(rx)
     101 +}
     102 + 
  • ■ ■ ■ ■ ■ ■
    ebpfguard/src/hooks/sb_mount.rs
     1 +use aya::{
     2 + maps::{AsyncPerfEventArray, HashMap, MapData},
     3 + programs::lsm::LsmLink,
     4 +};
     5 +use ebpfguard_common::alerts as ebpf_alerts;
     6 +use tokio::sync::mpsc::Receiver;
     7 + 
     8 +use crate::{alerts, error::EbpfguardError, policy};
     9 + 
     10 +use super::{perf_array_alerts, INODE_SUBJECT_MAP};
     11 + 
     12 +pub struct SbMount {
     13 + #[allow(dead_code)]
     14 + pub(crate) program_link: Option<LsmLink>,
     15 + pub(crate) allowed_map: HashMap<MapData, u64, u8>,
     16 + pub(crate) denied_map: HashMap<MapData, u64, u8>,
     17 + pub(crate) perf_array: AsyncPerfEventArray<MapData>,
     18 +}
     19 + 
     20 +impl SbMount {
     21 + pub async fn add_policy(&mut self, policy: policy::SbMount) -> Result<(), EbpfguardError> {
     22 + let bin_inode = {
     23 + let mut map = INODE_SUBJECT_MAP.lock().await;
     24 + map.resolve_path(policy.subject)?
     25 + };
     26 + 
     27 + if policy.allow {
     28 + self.allowed_map.insert(bin_inode, 0, 0)?;
     29 + } else {
     30 + self.denied_map.insert(bin_inode, 0, 0)?;
     31 + }
     32 + 
     33 + Ok(())
     34 + }
     35 + 
     36 + pub async fn list_policies(&self) -> Result<Vec<policy::SbMount>, EbpfguardError> {
     37 + let mut policies = Vec::new();
     38 + 
     39 + for res in self.allowed_map.iter() {
     40 + let (bin_inode, _) = res?;
     41 + 
     42 + let subject = {
     43 + let map = INODE_SUBJECT_MAP.lock().await;
     44 + map.resolve_inode(bin_inode)
     45 + };
     46 + 
     47 + policies.push(policy::SbMount {
     48 + subject,
     49 + allow: true,
     50 + });
     51 + }
     52 + 
     53 + for res in self.denied_map.iter() {
     54 + let (bin_inode, _) = res?;
     55 + 
     56 + let subject = {
     57 + let map = INODE_SUBJECT_MAP.lock().await;
     58 + map.resolve_inode(bin_inode)
     59 + };
     60 + 
     61 + policies.push(policy::SbMount {
     62 + subject,
     63 + allow: false,
     64 + });
     65 + }
     66 + 
     67 + Ok(policies)
     68 + }
     69 + 
     70 + pub async fn alerts(&mut self) -> Result<Receiver<alerts::SbMount>, EbpfguardError> {
     71 + perf_array_alerts::<ebpf_alerts::SbMount, alerts::SbMount>(&mut self.perf_array).await
     72 + }
     73 +}
     74 + 
  • ■ ■ ■ ■ ■ ■
    ebpfguard/src/hooks/sb_remount.rs
     1 +use aya::{
     2 + maps::{AsyncPerfEventArray, HashMap, MapData},
     3 + programs::lsm::LsmLink,
     4 +};
     5 +use ebpfguard_common::alerts as ebpf_alerts;
     6 +use tokio::sync::mpsc::Receiver;
     7 + 
     8 +use crate::{alerts, error::EbpfguardError, policy};
     9 + 
     10 +use super::{perf_array_alerts, INODE_SUBJECT_MAP};
     11 + 
     12 +pub struct SbRemount {
     13 + #[allow(dead_code)]
     14 + pub(crate) program_link: Option<LsmLink>,
     15 + pub(crate) allowed_map: HashMap<MapData, u64, u8>,
     16 + pub(crate) denied_map: HashMap<MapData, u64, u8>,
     17 + pub(crate) perf_array: AsyncPerfEventArray<MapData>,
     18 +}
     19 + 
     20 +impl SbRemount {
     21 + pub async fn add_policy(&mut self, policy: policy::SbRemount) -> Result<(), EbpfguardError> {
     22 + let bin_inode = {
     23 + let mut map = INODE_SUBJECT_MAP.lock().await;
     24 + map.resolve_path(policy.subject)?
     25 + };
     26 + 
     27 + if policy.allow {
     28 + self.allowed_map.insert(bin_inode, 0, 0)?;
     29 + } else {
     30 + self.denied_map.insert(bin_inode, 0, 0)?;
     31 + }
     32 + 
     33 + Ok(())
     34 + }
     35 + 
     36 + pub async fn list_policies(&self) -> Result<Vec<policy::SbRemount>, EbpfguardError> {
     37 + let mut policies = Vec::new();
     38 + 
     39 + for res in self.allowed_map.iter() {
     40 + let (bin_inode, _) = res?;
     41 + 
     42 + let subject = {
     43 + let map = INODE_SUBJECT_MAP.lock().await;
     44 + map.resolve_inode(bin_inode)
     45 + };
     46 + 
     47 + policies.push(policy::SbRemount {
     48 + subject,
     49 + allow: true,
     50 + });
     51 + }
     52 + 
     53 + for res in self.denied_map.iter() {
     54 + let (bin_inode, _) = res?;
     55 + 
     56 + let subject = {
     57 + let map = INODE_SUBJECT_MAP.lock().await;
     58 + map.resolve_inode(bin_inode)
     59 + };
     60 + 
     61 + policies.push(policy::SbRemount {
     62 + subject,
     63 + allow: false,
     64 + });
     65 + }
     66 + 
     67 + Ok(policies)
     68 + }
     69 + 
     70 + pub async fn alerts(&mut self) -> Result<Receiver<alerts::SbRemount>, EbpfguardError> {
     71 + perf_array_alerts::<ebpf_alerts::SbRemount, alerts::SbRemount>(&mut self.perf_array).await
     72 + }
     73 +}
     74 + 
  • ■ ■ ■ ■ ■ ■
    ebpfguard/src/hooks/sb_umount.rs
     1 +use aya::{
     2 + maps::{AsyncPerfEventArray, HashMap, MapData},
     3 + programs::lsm::LsmLink,
     4 +};
     5 +use ebpfguard_common::alerts as ebpf_alerts;
     6 +use tokio::sync::mpsc::Receiver;
     7 + 
     8 +use crate::{alerts, error::EbpfguardError, policy};
     9 + 
     10 +use super::{perf_array_alerts, INODE_SUBJECT_MAP};
     11 + 
     12 +pub struct SbUmount {
     13 + #[allow(dead_code)]
     14 + pub(crate) program_link: Option<LsmLink>,
     15 + pub(crate) allowed_map: HashMap<MapData, u64, u8>,
     16 + pub(crate) denied_map: HashMap<MapData, u64, u8>,
     17 + pub(crate) perf_array: AsyncPerfEventArray<MapData>,
     18 +}
     19 + 
     20 +impl SbUmount {
     21 + pub async fn add_policy(&mut self, policy: policy::SbUmount) -> Result<(), EbpfguardError> {
     22 + let bin_inode = {
     23 + let mut map = INODE_SUBJECT_MAP.lock().await;
     24 + map.resolve_path(policy.subject)?
     25 + };
     26 + 
     27 + if policy.allow {
     28 + self.allowed_map.insert(bin_inode, 0, 0)?;
     29 + } else {
     30 + self.denied_map.insert(bin_inode, 0, 0)?;
     31 + }
     32 + 
     33 + Ok(())
     34 + }
     35 + 
     36 + pub async fn list_policies(&self) -> Result<Vec<policy::SbUmount>, EbpfguardError> {
     37 + let mut policies = Vec::new();
     38 + 
     39 + for res in self.allowed_map.iter() {
     40 + let (bin_inode, _) = res?;
     41 + 
     42 + let subject = {
     43 + let map = INODE_SUBJECT_MAP.lock().await;
     44 + map.resolve_inode(bin_inode)
     45 + };
     46 + 
     47 + policies.push(policy::SbUmount {
     48 + subject,
     49 + allow: true,
     50 + });
     51 + }
     52 + 
     53 + for res in self.denied_map.iter() {
     54 + let (bin_inode, _) = res?;
     55 + 
     56 + let subject = {
     57 + let map = INODE_SUBJECT_MAP.lock().await;
     58 + map.resolve_inode(bin_inode)
     59 + };
     60 + 
     61 + policies.push(policy::SbUmount {
     62 + subject,
     63 + allow: false,
     64 + });
     65 + }
     66 + 
     67 + Ok(policies)
     68 + }
     69 + 
     70 + pub async fn alerts(&mut self) -> Result<Receiver<alerts::SbUmount>, EbpfguardError> {
     71 + perf_array_alerts::<ebpf_alerts::SbUmount, alerts::SbUmount>(&mut self.perf_array).await
     72 + }
     73 +}
     74 + 
  • ■ ■ ■ ■ ■ ■
    ebpfguard/src/hooks/socket_bind.rs
     1 +use aya::{
     2 + maps::{AsyncPerfEventArray, HashMap, MapData},
     3 + programs::lsm::LsmLink,
     4 +};
     5 +use ebpfguard_common::{alerts as ebpf_alerts, policy as ebpf_policy};
     6 +use tokio::sync::mpsc::Receiver;
     7 + 
     8 +use crate::{alerts, error::EbpfguardError, policy};
     9 + 
     10 +use super::{perf_array_alerts, INODE_SUBJECT_MAP};
     11 + 
     12 +pub struct SocketBind {
     13 + #[allow(dead_code)]
     14 + pub(crate) program_link: Option<LsmLink>,
     15 + pub(crate) allowed_map: HashMap<MapData, u64, ebpf_policy::Ports>,
     16 + pub(crate) denied_map: HashMap<MapData, u64, ebpf_policy::Ports>,
     17 + pub(crate) perf_array: AsyncPerfEventArray<MapData>,
     18 +}
     19 + 
     20 +impl SocketBind {
     21 + pub async fn add_policy(&mut self, policy: policy::SocketBind) -> Result<(), EbpfguardError> {
     22 + let bin_inode = {
     23 + let mut map = INODE_SUBJECT_MAP.lock().await;
     24 + map.resolve_path(policy.subject)?
     25 + };
     26 + 
     27 + let allow: ebpf_policy::Ports = policy.allow.into();
     28 + let deny: ebpf_policy::Ports = policy.deny.into();
     29 + 
     30 + self.allowed_map.insert(bin_inode, allow, 0)?;
     31 + self.denied_map.insert(bin_inode, deny, 0)?;
     32 + 
     33 + Ok(())
     34 + }
     35 + 
     36 + pub async fn list_policies(&self) -> Result<Vec<policy::SocketBind>, EbpfguardError> {
     37 + let mut policies = Vec::new();
     38 + 
     39 + for res in self.allowed_map.iter() {
     40 + let (bin_inode, allow) = res?;
     41 + let deny = self.denied_map.get(&bin_inode, 0)?;
     42 + 
     43 + let subject = {
     44 + let map = INODE_SUBJECT_MAP.lock().await;
     45 + map.resolve_inode(bin_inode)
     46 + };
     47 + 
     48 + policies.push(policy::SocketBind {
     49 + subject,
     50 + allow: allow.into(),
     51 + deny: deny.into(),
     52 + });
     53 + }
     54 + 
     55 + Ok(policies)
     56 + }
     57 + 
     58 + pub async fn alerts(&mut self) -> Result<Receiver<alerts::SocketBind>, EbpfguardError> {
     59 + perf_array_alerts::<ebpf_alerts::SocketBind, alerts::SocketBind>(&mut self.perf_array).await
     60 + }
     61 +}
     62 + 
  • ■ ■ ■ ■ ■ ■
    ebpfguard/src/hooks/socket_connect.rs
     1 +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
     2 + 
     3 +use aya::{
     4 + maps::{AsyncPerfEventArray, HashMap, MapData},
     5 + programs::lsm::LsmLink,
     6 +};
     7 +use ebpfguard_common::{
     8 + alerts as ebpf_alerts,
     9 + policy::{self as ebpf_policy, IpAddrs},
     10 +};
     11 +use tokio::sync::mpsc::Receiver;
     12 + 
     13 +use crate::{alerts, error::EbpfguardError, policy};
     14 + 
     15 +use super::{perf_array_alerts, INODE_SUBJECT_MAP};
     16 + 
     17 +pub struct SocketConnect {
     18 + #[allow(dead_code)]
     19 + pub(crate) program_link: Option<LsmLink>,
     20 + pub(crate) allowed_map_v4: HashMap<MapData, u64, ebpf_policy::Ipv4Addrs>,
     21 + pub(crate) denied_map_v4: HashMap<MapData, u64, ebpf_policy::Ipv4Addrs>,
     22 + pub(crate) allowed_map_v6: HashMap<MapData, u64, ebpf_policy::Ipv6Addrs>,
     23 + pub(crate) denied_map_v6: HashMap<MapData, u64, ebpf_policy::Ipv6Addrs>,
     24 + pub(crate) perf_array: AsyncPerfEventArray<MapData>,
     25 +}
     26 + 
     27 +impl SocketConnect {
     28 + pub async fn add_policy(
     29 + &mut self,
     30 + policy: policy::SocketConnect,
     31 + ) -> Result<(), EbpfguardError> {
     32 + let bin_inode = {
     33 + let mut map = INODE_SUBJECT_MAP.lock().await;
     34 + map.resolve_path(policy.subject)?
     35 + };
     36 + 
     37 + let (allow_v4, allow_v6) = policy.allow.into_ebpf();
     38 + let (deny_v4, deny_v6) = policy.deny.into_ebpf();
     39 + 
     40 + self.allowed_map_v4.insert(bin_inode, allow_v4, 0)?;
     41 + self.denied_map_v4.insert(bin_inode, deny_v4, 0)?;
     42 + self.allowed_map_v6.insert(bin_inode, allow_v6, 0)?;
     43 + self.denied_map_v6.insert(bin_inode, deny_v6, 0)?;
     44 + 
     45 + Ok(())
     46 + }
     47 + 
     48 + pub async fn list_policies(&self) -> Result<Vec<policy::SocketConnect>, EbpfguardError> {
     49 + let mut policies = Vec::new();
     50 + 
     51 + for res in self.allowed_map_v4.iter() {
     52 + let (bin_inode, allow_v4) = res?;
     53 + let deny_v4 = self.denied_map_v4.get(&bin_inode, 0)?;
     54 + let allow_v6 = self.allowed_map_v6.get(&bin_inode, 0)?;
     55 + let deny_v6 = self.denied_map_v6.get(&bin_inode, 0)?;
     56 + 
     57 + let subject = {
     58 + let map = INODE_SUBJECT_MAP.lock().await;
     59 + map.resolve_inode(bin_inode)
     60 + };
     61 + 
     62 + let allow = if allow_v4.all() && allow_v6.all() {
     63 + policy::Addresses::All
     64 + } else {
     65 + let mut addrs = Vec::new();
     66 + for addr in allow_v4.addrs.iter() {
     67 + if *addr == 0 {
     68 + break;
     69 + }
     70 + addrs.push(IpAddr::V4(Ipv4Addr::from(addr.to_owned())));
     71 + }
     72 + for addr in allow_v6.addrs.iter() {
     73 + if *addr == [0u8; 16] {
     74 + break;
     75 + }
     76 + addrs.push(IpAddr::V6(Ipv6Addr::from(addr.to_owned())));
     77 + }
     78 + policy::Addresses::Addresses(addrs)
     79 + };
     80 + let deny = if deny_v4.all() && deny_v6.all() {
     81 + policy::Addresses::All
     82 + } else {
     83 + let mut addrs = Vec::new();
     84 + for addr in deny_v4.addrs.iter() {
     85 + if *addr == 0 {
     86 + break;
     87 + }
     88 + addrs.push(IpAddr::V4(Ipv4Addr::from(addr.to_owned())));
     89 + }
     90 + for addr in deny_v6.addrs.iter() {
     91 + if *addr == [0u8; 16] {
     92 + break;
     93 + }
     94 + addrs.push(IpAddr::V6(Ipv6Addr::from(addr.to_owned())));
     95 + }
     96 + policy::Addresses::Addresses(addrs)
     97 + };
     98 + 
     99 + policies.push(policy::SocketConnect {
     100 + subject,
     101 + allow,
     102 + deny,
     103 + });
     104 + }
     105 + 
     106 + Ok(policies)
     107 + }
     108 + 
     109 + pub async fn alerts(&mut self) -> Result<Receiver<alerts::SocketConnect>, EbpfguardError> {
     110 + perf_array_alerts::<ebpf_alerts::SocketConnect, alerts::SocketConnect>(&mut self.perf_array)
     111 + .await
     112 + }
     113 +}
     114 + 
  • ■ ■ ■ ■ ■ ■
    ebpfguard/src/hooks/task_fix_setuid.rs
     1 +use aya::{
     2 + maps::{AsyncPerfEventArray, HashMap, MapData},
     3 + programs::lsm::LsmLink,
     4 +};
     5 +use ebpfguard_common::alerts as ebpf_alerts;
     6 +use tokio::sync::mpsc::Receiver;
     7 + 
     8 +use crate::{alerts, error::EbpfguardError, policy};
     9 + 
     10 +use super::{perf_array_alerts, INODE_SUBJECT_MAP};
     11 + 
     12 +pub struct TaskFixSetuid {
     13 + #[allow(dead_code)]
     14 + pub(crate) program_link: Option<LsmLink>,
     15 + pub(crate) allowed_map: HashMap<MapData, u64, u8>,
     16 + pub(crate) denied_map: HashMap<MapData, u64, u8>,
     17 + pub(crate) perf_array: AsyncPerfEventArray<MapData>,
     18 +}
     19 + 
     20 +impl TaskFixSetuid {
     21 + pub async fn add_policy(
     22 + &mut self,
     23 + policy: policy::TaskFixSetuid,
     24 + ) -> Result<(), EbpfguardError> {
     25 + let bin_inode = {
     26 + let mut map = INODE_SUBJECT_MAP.lock().await;
     27 + map.resolve_path(policy.subject)?
     28 + };
     29 + 
     30 + if policy.allow {
     31 + self.allowed_map.insert(bin_inode, 0, 0)?;
     32 + } else {
     33 + self.denied_map.insert(bin_inode, 0, 0)?;
     34 + }
     35 + 
     36 + Ok(())
     37 + }
     38 + 
     39 + pub async fn list_policies(&self) -> Result<Vec<policy::TaskFixSetuid>, EbpfguardError> {
     40 + let mut policies = Vec::new();
     41 + 
     42 + for res in self.allowed_map.iter() {
     43 + let (bin_inode, _) = res?;
     44 + 
     45 + let subject = {
     46 + let map = INODE_SUBJECT_MAP.lock().await;
     47 + map.resolve_inode(bin_inode)
     48 + };
     49 + 
     50 + policies.push(policy::TaskFixSetuid {
     51 + subject,
     52 + allow: true,
     53 + });
     54 + }
     55 + 
     56 + for res in self.denied_map.iter() {
     57 + let (bin_inode, _) = res?;
     58 + 
     59 + let subject = {
     60 + let map = INODE_SUBJECT_MAP.lock().await;
     61 + map.resolve_inode(bin_inode)
     62 + };
     63 + 
     64 + policies.push(policy::TaskFixSetuid {
     65 + subject,
     66 + allow: false,
     67 + });
     68 + }
     69 + 
     70 + Ok(policies)
     71 + }
     72 + 
     73 + pub async fn alerts(&mut self) -> Result<Receiver<alerts::TaskFixSetuid>, EbpfguardError> {
     74 + perf_array_alerts::<ebpf_alerts::TaskFixSetuid, alerts::TaskFixSetuid>(&mut self.perf_array)
     75 + .await
     76 + }
     77 +}
     78 + 
  • ■ ■ ■ ■ ■ ■
    ebpfguard/src/hooks.rs
    1  -use std::{
    2  - fmt::Debug,
    3  - net::{IpAddr, Ipv4Addr, Ipv6Addr},
    4  -};
    5  - 
    6  -use aya::{
    7  - maps::{AsyncPerfEventArray, HashMap, MapData},
    8  - programs::lsm::LsmLink,
    9  - util::online_cpus,
    10  -};
    11  -use bytes::BytesMut;
    12  -use ebpfguard_common::{
    13  - alerts as ebpf_alerts,
    14  - policy::{self as ebpf_policy, IpAddrs},
    15  -};
    16  -use once_cell::sync::Lazy;
    17  -use tokio::{
    18  - sync::{
    19  - mpsc::{self, Receiver},
    20  - Mutex,
    21  - },
    22  - task,
    23  -};
    24  - 
    25  -use crate::{alerts, error::EbpfguardError, policy, InodeSubjectMap};
    26  - 
    27  -static INODE_SUBJECT_MAP: Lazy<Mutex<InodeSubjectMap>> =
    28  - Lazy::new(|| Mutex::new(InodeSubjectMap::default()));
    29  - 
    30  -pub struct All {
    31  - pub bprm_check_security: BprmCheckSecurity,
    32  - pub file_open: FileOpen,
    33  - pub task_fix_setuid: TaskFixSetuid,
    34  - pub socket_bind: SocketBind,
    35  - pub socket_connect: SocketConnect,
    36  -}
    37  - 
    38  -impl All {
    39  - pub async fn add_policy(&mut self, policy: policy::Policy) -> Result<(), EbpfguardError> {
    40  - match policy {
    41  - policy::Policy::FileOpen(policy) => self.file_open.add_policy(policy).await?,
    42  - policy::Policy::TaskFixSetuid(policy) => {
    43  - self.task_fix_setuid.add_policy(policy).await?
    44  - }
    45  - policy::Policy::SocketBind(policy) => self.socket_bind.add_policy(policy).await?,
    46  - policy::Policy::SocketConnect(policy) => self.socket_connect.add_policy(policy).await?,
    47  - }
    48  - 
    49  - Ok(())
    50  - }
    51  -}
    52  - 
    53  -pub struct BprmCheckSecurity {
    54  - #[allow(dead_code)]
    55  - pub(crate) program_link: Option<LsmLink>,
    56  - pub(crate) perf_array: AsyncPerfEventArray<MapData>,
    57  -}
    58  - 
    59  -impl BprmCheckSecurity {
    60  - pub async fn alerts(&mut self) -> Result<Receiver<alerts::BprmCheckSecurity>, EbpfguardError> {
    61  - perf_array_alerts::<ebpf_alerts::BprmCheckSecurity, alerts::BprmCheckSecurity>(
    62  - &mut self.perf_array,
    63  - )
    64  - .await
    65  - }
    66  -}
    67  - 
    68  -pub struct FileOpen {
    69  - #[allow(dead_code)]
    70  - pub(crate) program_link: Option<LsmLink>,
    71  - pub(crate) allowed_map: HashMap<MapData, u64, ebpf_policy::Paths>,
    72  - pub(crate) denied_map: HashMap<MapData, u64, ebpf_policy::Paths>,
    73  - pub(crate) perf_array: AsyncPerfEventArray<MapData>,
    74  -}
    75  - 
    76  -impl FileOpen {
    77  - pub async fn add_policy(&mut self, policy: policy::FileOpen) -> Result<(), EbpfguardError> {
    78  - let bin_inode = {
    79  - let mut map = INODE_SUBJECT_MAP.lock().await;
    80  - map.resolve_path(policy.subject)?
    81  - };
    82  - 
    83  - let allow: ebpf_policy::Paths = policy.allow.into();
    84  - let deny: ebpf_policy::Paths = policy.deny.into();
    85  - 
    86  - self.allowed_map.insert(bin_inode, allow, 0)?;
    87  - self.denied_map.insert(bin_inode, deny, 0)?;
    88  - 
    89  - Ok(())
    90  - }
    91  - 
    92  - pub async fn list_policies(&self) -> Result<Vec<policy::FileOpen>, EbpfguardError> {
    93  - let mut policies = Vec::new();
    94  - 
    95  - for res in self.allowed_map.iter() {
    96  - let (bin_inode, allow) = res?;
    97  - let deny = self.denied_map.get(&bin_inode, 0)?;
    98  - 
    99  - let subject = {
    100  - let map = INODE_SUBJECT_MAP.lock().await;
    101  - map.resolve_inode(bin_inode)
    102  - };
    103  - 
    104  - policies.push(policy::FileOpen {
    105  - subject,
    106  - allow: allow.into(),
    107  - deny: deny.into(),
    108  - });
    109  - }
    110  - 
    111  - Ok(policies)
    112  - }
    113  - 
    114  - pub async fn alerts(&mut self) -> Result<Receiver<alerts::FileOpen>, EbpfguardError> {
    115  - perf_array_alerts::<ebpf_alerts::FileOpen, alerts::FileOpen>(&mut self.perf_array).await
    116  - }
    117  -}
    118  - 
    119  -pub struct TaskFixSetuid {
    120  - #[allow(dead_code)]
    121  - pub(crate) program_link: Option<LsmLink>,
    122  - pub(crate) allowed_map: HashMap<MapData, u64, u8>,
    123  - pub(crate) denied_map: HashMap<MapData, u64, u8>,
    124  - pub(crate) perf_array: AsyncPerfEventArray<MapData>,
    125  -}
    126  - 
    127  -impl TaskFixSetuid {
    128  - pub async fn add_policy(
    129  - &mut self,
    130  - policy: policy::TaskFixSetuid,
    131  - ) -> Result<(), EbpfguardError> {
    132  - let bin_inode = {
    133  - let mut map = INODE_SUBJECT_MAP.lock().await;
    134  - map.resolve_path(policy.subject)?
    135  - };
    136  - 
    137  - if policy.allow {
    138  - self.allowed_map.insert(bin_inode, 0, 0)?;
    139  - } else {
    140  - self.denied_map.insert(bin_inode, 0, 0)?;
    141  - }
    142  - 
    143  - Ok(())
    144  - }
    145  - 
    146  - pub async fn list_policies(&self) -> Result<Vec<policy::TaskFixSetuid>, EbpfguardError> {
    147  - let mut policies = Vec::new();
    148  - 
    149  - for res in self.allowed_map.iter() {
    150  - let (bin_inode, _) = res?;
    151  - 
    152  - let subject = {
    153  - let map = INODE_SUBJECT_MAP.lock().await;
    154  - map.resolve_inode(bin_inode)
    155  - };
    156  - 
    157  - policies.push(policy::TaskFixSetuid {
    158  - subject,
    159  - allow: true,
    160  - });
    161  - }
    162  - 
    163  - for res in self.denied_map.iter() {
    164  - let (bin_inode, _) = res?;
    165  - 
    166  - let subject = {
    167  - let map = INODE_SUBJECT_MAP.lock().await;
    168  - map.resolve_inode(bin_inode)
    169  - };
    170  - 
    171  - policies.push(policy::TaskFixSetuid {
    172  - subject,
    173  - allow: false,
    174  - });
    175  - }
    176  - 
    177  - Ok(policies)
    178  - }
    179  - 
    180  - pub async fn alerts(&mut self) -> Result<Receiver<alerts::TaskFixSetuid>, EbpfguardError> {
    181  - perf_array_alerts::<ebpf_alerts::TaskFixSetuid, alerts::TaskFixSetuid>(&mut self.perf_array)
    182  - .await
    183  - }
    184  -}
    185  - 
    186  -pub struct SocketBind {
    187  - #[allow(dead_code)]
    188  - pub(crate) program_link: Option<LsmLink>,
    189  - pub(crate) allowed_map: HashMap<MapData, u64, ebpf_policy::Ports>,
    190  - pub(crate) denied_map: HashMap<MapData, u64, ebpf_policy::Ports>,
    191  - pub(crate) perf_array: AsyncPerfEventArray<MapData>,
    192  -}
    193  - 
    194  -impl SocketBind {
    195  - pub async fn add_policy(&mut self, policy: policy::SocketBind) -> Result<(), EbpfguardError> {
    196  - let bin_inode = {
    197  - let mut map = INODE_SUBJECT_MAP.lock().await;
    198  - map.resolve_path(policy.subject)?
    199  - };
    200  - 
    201  - let allow: ebpf_policy::Ports = policy.allow.into();
    202  - let deny: ebpf_policy::Ports = policy.deny.into();
    203  - 
    204  - self.allowed_map.insert(bin_inode, allow, 0)?;
    205  - self.denied_map.insert(bin_inode, deny, 0)?;
    206  - 
    207  - Ok(())
    208  - }
    209  - 
    210  - pub async fn list_policies(&self) -> Result<Vec<policy::SocketBind>, EbpfguardError> {
    211  - let mut policies = Vec::new();
    212  - 
    213  - for res in self.allowed_map.iter() {
    214  - let (bin_inode, allow) = res?;
    215  - let deny = self.denied_map.get(&bin_inode, 0)?;
    216  - 
    217  - let subject = {
    218  - let map = INODE_SUBJECT_MAP.lock().await;
    219  - map.resolve_inode(bin_inode)
    220  - };
    221  - 
    222  - policies.push(policy::SocketBind {
    223  - subject,
    224  - allow: allow.into(),
    225  - deny: deny.into(),
    226  - });
    227  - }
    228  - 
    229  - Ok(policies)
    230  - }
    231  - 
    232  - pub async fn alerts(&mut self) -> Result<Receiver<alerts::SocketBind>, EbpfguardError> {
    233  - perf_array_alerts::<ebpf_alerts::SocketBind, alerts::SocketBind>(&mut self.perf_array).await
    234  - }
    235  -}
    236  - 
    237  -pub struct SocketConnect {
    238  - #[allow(dead_code)]
    239  - pub(crate) program_link: Option<LsmLink>,
    240  - pub(crate) allowed_map_v4: HashMap<MapData, u64, ebpf_policy::Ipv4Addrs>,
    241  - pub(crate) denied_map_v4: HashMap<MapData, u64, ebpf_policy::Ipv4Addrs>,
    242  - pub(crate) allowed_map_v6: HashMap<MapData, u64, ebpf_policy::Ipv6Addrs>,
    243  - pub(crate) denied_map_v6: HashMap<MapData, u64, ebpf_policy::Ipv6Addrs>,
    244  - pub(crate) perf_array: AsyncPerfEventArray<MapData>,
    245  -}
    246  - 
    247  -impl SocketConnect {
    248  - pub async fn add_policy(
    249  - &mut self,
    250  - policy: policy::SocketConnect,
    251  - ) -> Result<(), EbpfguardError> {
    252  - let bin_inode = {
    253  - let mut map = INODE_SUBJECT_MAP.lock().await;
    254  - map.resolve_path(policy.subject)?
    255  - };
    256  - 
    257  - let (allow_v4, allow_v6) = policy.allow.into_ebpf();
    258  - let (deny_v4, deny_v6) = policy.deny.into_ebpf();
    259  - 
    260  - self.allowed_map_v4.insert(bin_inode, allow_v4, 0)?;
    261  - self.denied_map_v4.insert(bin_inode, deny_v4, 0)?;
    262  - self.allowed_map_v6.insert(bin_inode, allow_v6, 0)?;
    263  - self.denied_map_v6.insert(bin_inode, deny_v6, 0)?;
    264  - 
    265  - Ok(())
    266  - }
    267  - 
    268  - pub async fn list_policies(&self) -> Result<Vec<policy::SocketConnect>, EbpfguardError> {
    269  - let mut policies = Vec::new();
    270  - 
    271  - for res in self.allowed_map_v4.iter() {
    272  - let (bin_inode, allow_v4) = res?;
    273  - let deny_v4 = self.denied_map_v4.get(&bin_inode, 0)?;
    274  - let allow_v6 = self.allowed_map_v6.get(&bin_inode, 0)?;
    275  - let deny_v6 = self.denied_map_v6.get(&bin_inode, 0)?;
    276  - 
    277  - let subject = {
    278  - let map = INODE_SUBJECT_MAP.lock().await;
    279  - map.resolve_inode(bin_inode)
    280  - };
    281  - 
    282  - let allow = if allow_v4.all() && allow_v6.all() {
    283  - policy::Addresses::All
    284  - } else {
    285  - let mut addrs = Vec::new();
    286  - for addr in allow_v4.addrs.iter() {
    287  - if *addr == 0 {
    288  - break;
    289  - }
    290  - addrs.push(IpAddr::V4(Ipv4Addr::from(addr.to_owned())));
    291  - }
    292  - for addr in allow_v6.addrs.iter() {
    293  - if *addr == [0u8; 16] {
    294  - break;
    295  - }
    296  - addrs.push(IpAddr::V6(Ipv6Addr::from(addr.to_owned())));
    297  - }
    298  - policy::Addresses::Addresses(addrs)
    299  - };
    300  - let deny = if deny_v4.all() && deny_v6.all() {
    301  - policy::Addresses::All
    302  - } else {
    303  - let mut addrs = Vec::new();
    304  - for addr in deny_v4.addrs.iter() {
    305  - if *addr == 0 {
    306  - break;
    307  - }
    308  - addrs.push(IpAddr::V4(Ipv4Addr::from(addr.to_owned())));
    309  - }
    310  - for addr in deny_v6.addrs.iter() {
    311  - if *addr == [0u8; 16] {
    312  - break;
    313  - }
    314  - addrs.push(IpAddr::V6(Ipv6Addr::from(addr.to_owned())));
    315  - }
    316  - policy::Addresses::Addresses(addrs)
    317  - };
    318  - 
    319  - policies.push(policy::SocketConnect {
    320  - subject,
    321  - allow,
    322  - deny,
    323  - });
    324  - }
    325  - 
    326  - Ok(policies)
    327  - }
    328  - 
    329  - pub async fn alerts(&mut self) -> Result<Receiver<alerts::SocketConnect>, EbpfguardError> {
    330  - perf_array_alerts::<ebpf_alerts::SocketConnect, alerts::SocketConnect>(&mut self.perf_array)
    331  - .await
    332  - }
    333  -}
    334  - 
    335  -pub async fn perf_array_alerts<E, U>(
    336  - perf_array: &mut AsyncPerfEventArray<MapData>,
    337  -) -> Result<Receiver<U>, EbpfguardError>
    338  -where
    339  - E: ebpf_alerts::Alert,
    340  - U: alerts::Alert + Debug + Send + From<E> + 'static,
    341  -{
    342  - let (tx, rx) = mpsc::channel(32);
    343  - 
    344  - let cpus = online_cpus()?;
    345  - for cpu_id in cpus {
    346  - let tx = tx.clone();
    347  - let mut buf = perf_array.open(cpu_id, None)?;
    348  - 
    349  - task::spawn(async move {
    350  - let mut buffers = (0..10)
    351  - .map(|_| BytesMut::with_capacity(1024))
    352  - .collect::<Vec<_>>();
    353  - loop {
    354  - let events = buf.read_events(&mut buffers).await.unwrap();
    355  - for buf in buffers.iter_mut().take(events.read) {
    356  - let alert: U = {
    357  - let ptr = buf.as_ptr() as *const E;
    358  - let alert = unsafe { ptr.read_unaligned() };
    359  - alert.into()
    360  - };
    361  - tx.send(alert).await.unwrap();
    362  - }
    363  - }
    364  - });
    365  - }
    366  - 
    367  - Ok(rx)
    368  -}
    369  - 
  • ■ ■ ■ ■ ■ ■
    ebpfguard/src/lib.rs
    skipped 40 lines
    41 41  //!
    42 42  //! * [`bprm_check_security`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L62)
    43 43  //! * [`file_open`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L620)
    44  -//! * [`task_fix_setuid`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L709)
     44 +//! * [`sb_mount`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L128)
     45 +//! * [`sb_remount`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L147)
     46 +//! * [`sb_umount`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L159)
    45 47  //! * [`socket_bind`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L904)
    46 48  //! * [`socket_connect`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L912)
     49 +//! * [`task_fix_setuid`](https://elixir.bootlin.com/linux/v6.2.12/source/include/linux/lsm_hooks.h#L709)
    47 50  //!
    48 51  //! # Examples
    49 52  //!
    skipped 35 lines
    85 88  //! [2023-04-22T20:51:01Z INFO file_open] file_open: pid=3001 subject=980333 path=9632
    86 89  //! [2023-04-22T20:51:03Z INFO file_open] file_open: pid=3010 subject=980298 path=9633
    87 90  //! ```
     91 +//! ### mount
     92 +//!
     93 +//! The [mount](https://github.com/deepfence/ebpfguard/tree/main/examples/file_open)
     94 +//! example shows how to define a policy for `sb_mount`, `sb_remount` and
     95 +//! `sb_umount` LSM hooks as Rust code. It denies the mount operations for all
     96 +//! processes except for the optionally given one.
     97 +//!
     98 +//! To try it out, let's create two directories:
     99 +//!
     100 +//! ```bash
     101 +//! $ mkdir /tmp/test1
     102 +//! $ mkdir /tmp/test2
     103 +//! ```
     104 +//!
     105 +//! Then run our example policy program, first without providing any binary to
     106 +//! allow mount for (so it's denied for all processes):
     107 +//!
     108 +//! ```bash
     109 +//! $ RUST_LOG=info cargo xtask run --example mount
     110 +//! ```
     111 +//!
     112 +//! Let's try to bind mount the first directory to the second one. It should
     113 +//! fail with the following error:
     114 +//!
     115 +//! ```bash
     116 +//! sudo mount --bind /tmp/test1 /tmp/test2
     117 +//! mount: /tmp/test2: permission denied.
     118 +//! dmesg(1) may have more information after failed mount system call.
     119 +//! ```
     120 +//!
     121 +//! And the policy program should show a log like:
     122 +//!
     123 +//! ```bash
     124 +//! [2023-04-23T21:02:58Z INFO mount] sb_mount: pid=17363 subject=678150
     125 +//! ```
     126 +//!
     127 +//! Now let's try to allow mount operations for the mount binary:
     128 +//!
     129 +//! ```bash
     130 +//! $ RUST_LOG=info cargo xtask run --example mount -- --allow /usr/bin/mount
     131 +//! ```
     132 +//!
     133 +//! And try to bind mount the first directory to the second one again. It should
     134 +//! succeed this time:
     135 +//!
     136 +//! ```bash
     137 +//! $ sudo mount --bind /tmp/test1 /tmp/test2
     138 +//! $ mount | grep test
     139 +//! tmpfs on /tmp/test2 type tmpfs (rw,nosuid,nodev,seclabel,nr_inodes=1048576,inode64)
     140 +//! ```
    88 141  //!
    89 142  //! ### `task_fix_setuid`
    90 143  //!
    skipped 73 lines
    164 217  pub mod policy;
    165 218   
    166 219  use error::EbpfguardError;
    167  -use hooks::{All, BprmCheckSecurity, FileOpen, SocketBind, SocketConnect, TaskFixSetuid};
     220 +use hooks::{
     221 + bprm_check_security::BprmCheckSecurity, file_open::FileOpen, sb_mount::SbMount,
     222 + sb_remount::SbRemount, sb_umount::SbUmount, socket_bind::SocketBind,
     223 + socket_connect::SocketConnect, task_fix_setuid::TaskFixSetuid, All,
     224 +};
    168 225  use policy::inode::InodeSubjectMap;
    169 226   
    170 227  pub struct PolicyManager {
    skipped 32 lines
    203 260   pub fn attach_all(&mut self) -> Result<All, EbpfguardError> {
    204 261   let bprm_check_security = self.attach_bprm_check_security()?;
    205 262   let file_open = self.attach_file_open()?;
    206  - let task_fix_setuid = self.attach_task_fix_setuid()?;
     263 + let sb_mount = self.attach_sb_mount()?;
     264 + let sb_remount = self.attach_sb_remount()?;
     265 + let sb_umount = self.attach_sb_umount()?;
    207 266   let socket_bind = self.attach_socket_bind()?;
    208 267   let socket_connect = self.attach_socket_connect()?;
     268 + let task_fix_setuid = self.attach_task_fix_setuid()?;
    209 269   
    210 270   Ok(All {
    211 271   bprm_check_security,
    212 272   file_open,
    213  - task_fix_setuid,
     273 + sb_mount,
     274 + sb_remount,
     275 + sb_umount,
    214 276   socket_bind,
    215 277   socket_connect,
     278 + task_fix_setuid,
    216 279   })
    217 280   }
    218 281   
    219 282   pub fn manage_all(&mut self) -> Result<All, EbpfguardError> {
    220 283   let bprm_check_security = self.manage_bprm_check_security()?;
    221 284   let file_open = self.manage_file_open()?;
    222  - let task_fix_setuid = self.manage_task_fix_setuid()?;
     285 + let sb_mount = self.manage_sb_mount()?;
     286 + let sb_remount = self.manage_sb_remount()?;
     287 + let sb_umount = self.manage_sb_umount()?;
    223 288   let socket_bind = self.manage_socket_bind()?;
    224 289   let socket_connect = self.manage_socket_connect()?;
     290 + let task_fix_setuid = self.manage_task_fix_setuid()?;
    225 291   
    226 292   Ok(All {
    227 293   bprm_check_security,
    228 294   file_open,
    229  - task_fix_setuid,
     295 + sb_mount,
     296 + sb_remount,
     297 + sb_umount,
    230 298   socket_bind,
    231 299   socket_connect,
     300 + task_fix_setuid,
    232 301   })
    233 302   }
    234 303   
    skipped 65 lines
    300 369   .try_into()?;
    301 370   
    302 371   Ok(TaskFixSetuid {
     372 + program_link: None,
     373 + allowed_map,
     374 + denied_map,
     375 + perf_array,
     376 + })
     377 + }
     378 + 
     379 + pub fn attach_sb_mount(&mut self) -> Result<SbMount, EbpfguardError> {
     380 + let mut sb_mount = self.manage_sb_mount()?;
     381 + let program_link = self.attach_program("sb_mount")?;
     382 + sb_mount.program_link = Some(program_link);
     383 + 
     384 + Ok(sb_mount)
     385 + }
     386 + 
     387 + pub fn manage_sb_mount(&mut self) -> Result<SbMount, EbpfguardError> {
     388 + let allowed_map = self.bpf.take_map("ALLOWED_SB_MOUNT").unwrap().try_into()?;
     389 + let denied_map = self.bpf.take_map("DENIED_SB_MOUNT").unwrap().try_into()?;
     390 + let perf_array = self.bpf.take_map("ALERT_SB_MOUNT").unwrap().try_into()?;
     391 + 
     392 + Ok(SbMount {
     393 + program_link: None,
     394 + allowed_map,
     395 + denied_map,
     396 + perf_array,
     397 + })
     398 + }
     399 + 
     400 + pub fn attach_sb_remount(&mut self) -> Result<SbRemount, EbpfguardError> {
     401 + let mut sb_remount = self.manage_sb_remount()?;
     402 + let program_link = self.attach_program("sb_remount")?;
     403 + sb_remount.program_link = Some(program_link);
     404 + 
     405 + Ok(sb_remount)
     406 + }
     407 + 
     408 + pub fn manage_sb_remount(&mut self) -> Result<SbRemount, EbpfguardError> {
     409 + let allowed_map = self
     410 + .bpf
     411 + .take_map("ALLOWED_SB_REMOUNT")
     412 + .unwrap()
     413 + .try_into()?;
     414 + let denied_map = self.bpf.take_map("DENIED_SB_REMOUNT").unwrap().try_into()?;
     415 + let perf_array = self.bpf.take_map("ALERT_SB_REMOUNT").unwrap().try_into()?;
     416 + 
     417 + Ok(SbRemount {
     418 + program_link: None,
     419 + allowed_map,
     420 + denied_map,
     421 + perf_array,
     422 + })
     423 + }
     424 + 
     425 + pub fn attach_sb_umount(&mut self) -> Result<SbUmount, EbpfguardError> {
     426 + let mut sb_umount = self.manage_sb_umount()?;
     427 + let program_link = self.attach_program("sb_umount")?;
     428 + sb_umount.program_link = Some(program_link);
     429 + 
     430 + Ok(sb_umount)
     431 + }
     432 + 
     433 + pub fn manage_sb_umount(&mut self) -> Result<SbUmount, EbpfguardError> {
     434 + let allowed_map = self.bpf.take_map("ALLOWED_SB_UMOUNT").unwrap().try_into()?;
     435 + let denied_map = self.bpf.take_map("DENIED_SB_UMOUNT").unwrap().try_into()?;
     436 + let perf_array = self.bpf.take_map("ALERT_SB_UMOUNT").unwrap().try_into()?;
     437 + 
     438 + Ok(SbUmount {
    303 439   program_link: None,
    304 440   allowed_map,
    305 441   denied_map,
    skipped 89 lines
  • ■ ■ ■ ■ ■
    ebpfguard/src/policy/mod.rs
    skipped 164 lines
    165 165  pub enum Policy {
    166 166   #[serde(rename = "file_open")]
    167 167   FileOpen(FileOpen),
    168  - #[serde(rename = "setuid")]
    169  - TaskFixSetuid(TaskFixSetuid),
     168 + #[serde(rename = "sb_mount")]
     169 + SbMount(SbMount),
     170 + #[serde(rename = "sb_remount")]
     171 + SbRemount(SbRemount),
     172 + #[serde(rename = "sb_umount")]
     173 + SbUmount(SbUmount),
    170 174   #[serde(rename = "socket_bind")]
    171 175   SocketBind(SocketBind),
    172 176   #[serde(rename = "socket_connect")]
    173 177   SocketConnect(SocketConnect),
     178 + #[serde(rename = "task_fix_setuid")]
     179 + TaskFixSetuid(TaskFixSetuid),
    174 180  }
    175 181   
    176 182  #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
    skipped 4 lines
    181 187  }
    182 188   
    183 189  #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
    184  -pub struct TaskFixSetuid {
     190 +pub struct SbMount {
     191 + pub subject: PolicySubject,
     192 + pub allow: bool,
     193 +}
     194 + 
     195 +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
     196 +pub struct SbRemount {
     197 + pub subject: PolicySubject,
     198 + pub allow: bool,
     199 +}
     200 + 
     201 +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
     202 +pub struct SbUmount {
    185 203   pub subject: PolicySubject,
    186 204   pub allow: bool,
    187 205  }
    skipped 10 lines
    198 216   pub subject: PolicySubject,
    199 217   pub allow: Addresses,
    200 218   pub deny: Addresses,
     219 +}
     220 + 
     221 +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
     222 +pub struct TaskFixSetuid {
     223 + pub subject: PolicySubject,
     224 + pub allow: bool,
    201 225  }
    202 226   
    203 227  #[cfg(test)]
    skipped 37 lines
    241 265   }
    242 266   
    243 267   #[test]
    244  - fn test_setuid() {
     268 + fn test_sb_mount() {
    245 269   let yaml = "
    246  -- !setuid
     270 +- !sb_mount
    247 271   subject: all
    248 272   allow: false
    249  -- !setuid
    250  - subject: !binary /usr/bin/sudo
     273 +- !sb_mount
     274 + subject: !binary /usr/bin/mount
    251 275   allow: true
    252 276  ";
    253 277   let policy = serde_yaml::from_str::<Vec<Policy>>(yaml).unwrap();
    254 278   assert_eq!(policy.len(), 2);
    255 279   assert_eq!(
    256 280   policy[0],
    257  - Policy::TaskFixSetuid(TaskFixSetuid {
     281 + Policy::SbMount(SbMount {
    258 282   subject: PolicySubject::All,
    259 283   allow: false
    260 284   })
    261 285   );
    262 286   assert_eq!(
    263 287   policy[1],
    264  - Policy::TaskFixSetuid(TaskFixSetuid {
    265  - subject: PolicySubject::Binary(PathBuf::from("/usr/bin/sudo")),
     288 + Policy::SbMount(SbMount {
     289 + subject: PolicySubject::Binary(PathBuf::from("/usr/bin/mount")),
    266 290   allow: true
    267 291   })
    268 292   );
    skipped 76 lines
    345 369   0x2001, 0x0db8, 0x3333, 0x4444, 0xCCCC, 0xDDDD, 0xEEEE, 0xFFFF
    346 370   )),
    347 371   ]),
     372 + })
     373 + );
     374 + }
     375 + 
     376 + #[test]
     377 + fn test_task_fix_setuid() {
     378 + let yaml = "
     379 +- !task_fix_setuid
     380 + subject: all
     381 + allow: false
     382 +- !task_fix_setuid
     383 + subject: !binary /usr/bin/sudo
     384 + allow: true
     385 +";
     386 + let policy = serde_yaml::from_str::<Vec<Policy>>(yaml).unwrap();
     387 + assert_eq!(policy.len(), 2);
     388 + assert_eq!(
     389 + policy[0],
     390 + Policy::TaskFixSetuid(TaskFixSetuid {
     391 + subject: PolicySubject::All,
     392 + allow: false
     393 + })
     394 + );
     395 + assert_eq!(
     396 + policy[1],
     397 + Policy::TaskFixSetuid(TaskFixSetuid {
     398 + subject: PolicySubject::Binary(PathBuf::from("/usr/bin/sudo")),
     399 + allow: true
    348 400   })
    349 401   );
    350 402   }
    skipped 2 lines
  • ■ ■ ■ ■ ■ ■
    ebpfguard-common/src/alerts.rs
    skipped 78 lines
    79 79   
    80 80  #[repr(C)]
    81 81  #[derive(Copy, Clone)]
     82 +pub struct SbMount {
     83 + pub pid: u32,
     84 + _padding: u32,
     85 + pub binprm_inode: u64,
     86 +}
     87 + 
     88 +impl SbMount {
     89 + pub fn new(pid: u32, binprm_inode: u64) -> Self {
     90 + Self {
     91 + pid,
     92 + _padding: 0,
     93 + binprm_inode,
     94 + }
     95 + }
     96 +}
     97 + 
     98 +impl Alert for SbMount {}
     99 + 
     100 +#[repr(C)]
     101 +#[derive(Copy, Clone)]
     102 +pub struct SbRemount {
     103 + pub pid: u32,
     104 + _padding: u32,
     105 + pub binprm_inode: u64,
     106 +}
     107 + 
     108 +impl SbRemount {
     109 + pub fn new(pid: u32, binprm_inode: u64) -> Self {
     110 + Self {
     111 + pid,
     112 + _padding: 0,
     113 + binprm_inode,
     114 + }
     115 + }
     116 +}
     117 + 
     118 +impl Alert for SbRemount {}
     119 + 
     120 +#[repr(C)]
     121 +#[derive(Copy, Clone)]
     122 +pub struct SbUmount {
     123 + pub pid: u32,
     124 + _padding: u32,
     125 + pub binprm_inode: u64,
     126 +}
     127 + 
     128 +impl SbUmount {
     129 + pub fn new(pid: u32, binprm_inode: u64) -> Self {
     130 + Self {
     131 + pid,
     132 + _padding: 0,
     133 + binprm_inode,
     134 + }
     135 + }
     136 +}
     137 + 
     138 +impl Alert for SbUmount {}
     139 + 
     140 +#[repr(C)]
     141 +#[derive(Copy, Clone)]
    82 142  pub struct SocketBind {
    83 143   pub pid: u32,
    84 144   _padding1: u32,
    skipped 61 lines
    146 206   
    147 207   unsafe impl Pod for BprmCheckSecurity {}
    148 208   unsafe impl Pod for FileOpen {}
    149  - unsafe impl Pod for TaskFixSetuid {}
     209 + unsafe impl Pod for SbMount {}
    150 210   unsafe impl Pod for SocketBind {}
    151 211   unsafe impl Pod for SocketConnect {}
     212 + unsafe impl Pod for TaskFixSetuid {}
    152 213  }
    153 214   
  • ■ ■ ■ ■
    ebpfguard-ebpf/src/lib.rs
    skipped 4 lines
    5 5  pub mod consts;
    6 6  pub mod file_open;
    7 7  pub mod maps;
    8  -pub mod task_fix_setuid;
     8 +pub mod sb_mount;
     9 +pub mod sb_remount;
     10 +pub mod sb_umount;
    9 11  pub mod socket_bind;
    10 12  pub mod socket_connect;
     13 +pub mod task_fix_setuid;
    11 14  #[allow(non_upper_case_globals)]
    12 15  #[allow(non_snake_case)]
    13 16  #[allow(non_camel_case_types)]
    skipped 22 lines
  • ■ ■ ■ ■ ■ ■
    ebpfguard-ebpf/src/main.rs
    skipped 3 lines
    4 4  use aya_bpf::{macros::lsm, programs::LsmContext};
    5 5   
    6 6  use ebpfguard_ebpf::{
    7  - bprm_check_security::bprm_check_security, file_open::file_open, socket_bind::socket_bind,
     7 + bprm_check_security::bprm_check_security, file_open::file_open, sb_mount::sb_mount,
     8 + sb_remount::sb_remount, sb_umount::sb_umount, socket_bind::socket_bind,
    8 9   socket_connect::socket_connect, task_fix_setuid::task_fix_setuid,
    9 10  };
    10 11   
    skipped 16 lines
    27 28   Ok(ret) => ret,
    28 29   Err(_) => 0,
    29 30   }
     31 +}
     32 + 
     33 +#[lsm(name = "sb_mount")]
     34 +pub fn prog_sb_mount(ctx: LsmContext) -> i32 {
     35 + sb_mount(ctx).into()
     36 +}
     37 + 
     38 +#[lsm(name = "sb_remount")]
     39 +pub fn prog_sb_remount(ctx: LsmContext) -> i32 {
     40 + sb_remount(ctx).into()
     41 +}
     42 + 
     43 +#[lsm(name = "sb_umount")]
     44 +pub fn prog_sb_umount(ctx: LsmContext) -> i32 {
     45 + sb_umount(ctx).into()
    30 46  }
    31 47   
    32 48  #[lsm(name = "socket_bind")]
    skipped 17 lines
  • ■ ■ ■ ■ ■ ■
    ebpfguard-ebpf/src/maps.rs
    skipped 32 lines
    33 33  pub static ALERT_TASK_FIX_SETUID: PerfEventArray<alerts::TaskFixSetuid> =
    34 34   PerfEventArray::pinned(1024, 0);
    35 35   
     36 +// Map indicating which binaries are allowed to mount filesystems.
     37 +#[map]
     38 +pub static ALLOWED_SB_MOUNT: HashMap<u64, u8> = HashMap::pinned(1024, 0);
     39 + 
     40 +// Map indicating which binaries are denied to mount filesystems.
     41 +#[map]
     42 +pub static DENIED_SB_MOUNT: HashMap<u64, u8> = HashMap::pinned(1024, 0);
     43 + 
     44 +// Map of alerts for `sb_mount` LSM hook inspection.
     45 +#[map]
     46 +pub static ALERT_SB_MOUNT: PerfEventArray<alerts::SbMount> = PerfEventArray::pinned(1024, 0);
     47 + 
     48 +// Map indicating which binaries are allowed to remount filesystems.
     49 +#[map]
     50 +pub static ALLOWED_SB_REMOUNT: HashMap<u64, u8> = HashMap::pinned(1024, 0);
     51 + 
     52 +// Map indicating which binaries are denied to remount filesystems.
     53 +#[map]
     54 +pub static DENIED_SB_REMOUNT: HashMap<u64, u8> = HashMap::pinned(1024, 0);
     55 + 
     56 +// Map of alerts for `sb_remount` LSM hook inspection.
     57 +#[map]
     58 +pub static ALERT_SB_REMOUNT: PerfEventArray<alerts::SbRemount> = PerfEventArray::pinned(1024, 0);
     59 + 
     60 +// Map indicating which binaries are allowed to unmount filesystems.
     61 +#[map]
     62 +pub static ALLOWED_SB_UMOUNT: HashMap<u64, u8> = HashMap::pinned(1024, 0);
     63 + 
     64 +// Map indicating which binaries are denied to unmount filesystems.
     65 +#[map]
     66 +pub static DENIED_SB_UMOUNT: HashMap<u64, u8> = HashMap::pinned(1024, 0);
     67 + 
     68 +// Map of alerts for `sb_umount` LSM hook inspection.
     69 +#[map]
     70 +pub static ALERT_SB_UMOUNT: PerfEventArray<alerts::SbUmount> = PerfEventArray::pinned(1024, 0);
     71 + 
    36 72  /// Map of allowed socket bind ports for each binary.
    37 73  #[map]
    38 74  pub static ALLOWED_SOCKET_BIND: HashMap<u64, policy::Ports> = HashMap::pinned(1024, 0);
    skipped 30 lines
  • ■ ■ ■ ■ ■ ■
    ebpfguard-ebpf/src/sb_mount.rs
     1 +use aya_bpf::{maps::HashMap, programs::LsmContext, BpfContext};
     2 +use ebpfguard_common::{alerts, consts::INODE_WILDCARD};
     3 + 
     4 +use crate::{
     5 + binprm::current_binprm_inode,
     6 + maps::{ALERT_SB_MOUNT, ALLOWED_SB_MOUNT, DENIED_SB_MOUNT},
     7 + Action, Mode,
     8 +};
     9 + 
     10 +/// Inspects the context of `sb_mount` LSM hook and decides whether to allow or
     11 +/// deny the operation based on the state of the `ALLOWED_SB_MOUNT` and
     12 +/// `DENIED_SB_MOUNT` maps.
     13 +///
     14 +/// If denied, the operation is logged to the `ALERT_SB_MOUNT` map.
     15 +///
     16 +/// # Example
     17 +///
     18 +/// ```rust
     19 +/// use aya_bpf::{macros::lsm, programs::LsmContext};
     20 +///
     21 +/// #[lsm(name = "my_program")]
     22 +/// pub fn my_program(ctx: LsmContext) -> i32 {
     23 +/// sb_mount(ctx).into()
     24 +/// }
     25 +/// ```
     26 +pub fn sb_mount(ctx: LsmContext) -> Action {
     27 + let binprm_inode = current_binprm_inode();
     28 + 
     29 + if unsafe { ALLOWED_SB_MOUNT.get(&INODE_WILDCARD).is_some() } {
     30 + return check_conditions_and_alert(&ctx, &DENIED_SB_MOUNT, binprm_inode, Mode::Denylist);
     31 + }
     32 + 
     33 + if unsafe { DENIED_SB_MOUNT.get(&INODE_WILDCARD).is_some() } {
     34 + return check_conditions_and_alert(&ctx, &ALLOWED_SB_MOUNT, binprm_inode, Mode::Allowlist);
     35 + }
     36 + 
     37 + Action::Allow
     38 +}
     39 + 
     40 +#[inline(always)]
     41 +fn check_conditions_and_alert(
     42 + ctx: &LsmContext,
     43 + map: &HashMap<u64, u8>,
     44 + binprm_inode: u64,
     45 + mode: Mode,
     46 +) -> Action {
     47 + match check_conditions(map, binprm_inode, mode) {
     48 + Action::Deny => {
     49 + ALERT_SB_MOUNT.output(ctx, &alerts::SbMount::new(ctx.pid(), binprm_inode), 0);
     50 + Action::Deny
     51 + }
     52 + action => action,
     53 + }
     54 +}
     55 + 
     56 +#[inline(always)]
     57 +fn check_conditions(map: &HashMap<u64, u8>, binprm_inode: u64, mode: Mode) -> Action {
     58 + if unsafe { map.get(&INODE_WILDCARD).is_some() } {
     59 + return match mode {
     60 + Mode::Allowlist => Action::Allow,
     61 + Mode::Denylist => Action::Deny,
     62 + };
     63 + }
     64 + 
     65 + if unsafe { map.get(&binprm_inode).is_some() } {
     66 + return match mode {
     67 + Mode::Allowlist => Action::Allow,
     68 + Mode::Denylist => Action::Deny,
     69 + };
     70 + }
     71 + 
     72 + match mode {
     73 + Mode::Allowlist => Action::Deny,
     74 + Mode::Denylist => Action::Allow,
     75 + }
     76 +}
     77 + 
  • ■ ■ ■ ■ ■ ■
    ebpfguard-ebpf/src/sb_remount.rs
     1 +use aya_bpf::{maps::HashMap, programs::LsmContext, BpfContext};
     2 +use ebpfguard_common::{alerts, consts::INODE_WILDCARD};
     3 + 
     4 +use crate::{
     5 + binprm::current_binprm_inode,
     6 + maps::{ALERT_SB_REMOUNT, ALLOWED_SB_REMOUNT, DENIED_SB_REMOUNT},
     7 + Action, Mode,
     8 +};
     9 + 
     10 +/// Inspects the context of `sb_remount` LSM hook and decides whether to allow or
     11 +/// deny the operation based on the state of the `ALLOWED_SB_REMOUNT` and
     12 +/// `DENIED_SB_REMOUNT` maps.
     13 +///
     14 +/// If denied, the operation is logged to the `ALERT_SB_REMOUNT` map.
     15 +///
     16 +/// # Example
     17 +///
     18 +/// ```rust
     19 +/// use aya_bpf::{macros::lsm, programs::LsmContext};
     20 +///
     21 +/// #[lsm(name = "my_program")]
     22 +/// pub fn my_program(ctx: LsmContext) -> i32 {
     23 +/// sb_umount(ctx).into()
     24 +/// }
     25 +/// ```
     26 +pub fn sb_remount(ctx: LsmContext) -> Action {
     27 + let binprm_inode = current_binprm_inode();
     28 + 
     29 + if unsafe { ALLOWED_SB_REMOUNT.get(&INODE_WILDCARD).is_some() } {
     30 + return check_conditions_and_alert(&ctx, &DENIED_SB_REMOUNT, binprm_inode, Mode::Denylist);
     31 + }
     32 + 
     33 + if unsafe { DENIED_SB_REMOUNT.get(&INODE_WILDCARD).is_some() } {
     34 + return check_conditions_and_alert(
     35 + &ctx,
     36 + &ALLOWED_SB_REMOUNT,
     37 + binprm_inode,
     38 + Mode::Allowlist,
     39 + );
     40 + }
     41 + 
     42 + Action::Allow
     43 +}
     44 + 
     45 +#[inline(always)]
     46 +fn check_conditions_and_alert(
     47 + ctx: &LsmContext,
     48 + map: &HashMap<u64, u8>,
     49 + binprm_inode: u64,
     50 + mode: Mode,
     51 +) -> Action {
     52 + match check_conditions(map, binprm_inode, mode) {
     53 + Action::Deny => {
     54 + ALERT_SB_REMOUNT.output(ctx, &alerts::SbRemount::new(ctx.pid(), binprm_inode), 0);
     55 + Action::Deny
     56 + }
     57 + action => action,
     58 + }
     59 +}
     60 + 
     61 +#[inline(always)]
     62 +fn check_conditions(map: &HashMap<u64, u8>, binprm_inode: u64, mode: Mode) -> Action {
     63 + if unsafe { map.get(&INODE_WILDCARD).is_some() } {
     64 + return match mode {
     65 + Mode::Allowlist => Action::Allow,
     66 + Mode::Denylist => Action::Deny,
     67 + };
     68 + }
     69 + 
     70 + if unsafe { map.get(&binprm_inode).is_some() } {
     71 + return match mode {
     72 + Mode::Allowlist => Action::Allow,
     73 + Mode::Denylist => Action::Deny,
     74 + };
     75 + }
     76 + 
     77 + match mode {
     78 + Mode::Allowlist => Action::Deny,
     79 + Mode::Denylist => Action::Allow,
     80 + }
     81 +}
     82 + 
  • ■ ■ ■ ■ ■ ■
    ebpfguard-ebpf/src/sb_umount.rs
     1 +use aya_bpf::{maps::HashMap, programs::LsmContext, BpfContext};
     2 +use ebpfguard_common::{alerts, consts::INODE_WILDCARD};
     3 + 
     4 +use crate::{
     5 + binprm::current_binprm_inode,
     6 + maps::{ALERT_SB_UMOUNT, ALLOWED_SB_UMOUNT, DENIED_SB_UMOUNT},
     7 + Action, Mode,
     8 +};
     9 + 
     10 +/// Inspects the context of `sb_umount` LSM hook and decides whether to allow or
     11 +/// deny the operation based on the state of the `ALLOWED_SB_UMOUNT` and
     12 +/// `DENIED_SB_UMOUNT` maps.
     13 +///
     14 +/// If denied, the operation is logged to the `ALERT_SB_UMOUNT` map.
     15 +///
     16 +/// # Example
     17 +///
     18 +/// ```rust
     19 +/// use aya_bpf::{macros::lsm, programs::LsmContext};
     20 +///
     21 +/// #[lsm(name = "my_program")]
     22 +/// pub fn my_program(ctx: LsmContext) -> i32 {
     23 +/// sb_umount(ctx).into()
     24 +/// }
     25 +/// ```
     26 +pub fn sb_umount(ctx: LsmContext) -> Action {
     27 + let binprm_inode = current_binprm_inode();
     28 + 
     29 + if unsafe { ALLOWED_SB_UMOUNT.get(&INODE_WILDCARD).is_some() } {
     30 + return check_conditions_and_alert(&ctx, &DENIED_SB_UMOUNT, binprm_inode, Mode::Denylist);
     31 + }
     32 + 
     33 + if unsafe { DENIED_SB_UMOUNT.get(&INODE_WILDCARD).is_some() } {
     34 + return check_conditions_and_alert(&ctx, &ALLOWED_SB_UMOUNT, binprm_inode, Mode::Allowlist);
     35 + }
     36 + 
     37 + Action::Allow
     38 +}
     39 + 
     40 +#[inline(always)]
     41 +fn check_conditions_and_alert(
     42 + ctx: &LsmContext,
     43 + map: &HashMap<u64, u8>,
     44 + binprm_inode: u64,
     45 + mode: Mode,
     46 +) -> Action {
     47 + match check_conditions(map, binprm_inode, mode) {
     48 + Action::Deny => {
     49 + ALERT_SB_UMOUNT.output(ctx, &alerts::SbUmount::new(ctx.pid(), binprm_inode), 0);
     50 + Action::Deny
     51 + }
     52 + action => action,
     53 + }
     54 +}
     55 + 
     56 +#[inline(always)]
     57 +fn check_conditions(map: &HashMap<u64, u8>, binprm_inode: u64, mode: Mode) -> Action {
     58 + if unsafe { map.get(&INODE_WILDCARD).is_some() } {
     59 + return match mode {
     60 + Mode::Allowlist => Action::Allow,
     61 + Mode::Denylist => Action::Deny,
     62 + };
     63 + }
     64 + 
     65 + if unsafe { map.get(&binprm_inode).is_some() } {
     66 + return match mode {
     67 + Mode::Allowlist => Action::Allow,
     68 + Mode::Denylist => Action::Deny,
     69 + };
     70 + }
     71 + 
     72 + match mode {
     73 + Mode::Allowlist => Action::Deny,
     74 + Mode::Denylist => Action::Allow,
     75 + }
     76 +}
     77 + 
  • ■ ■ ■ ■ ■
    examples/cli/examples/cli/main.rs
    skipped 3 lines
    4 4  use cli_table::{print_stdout, Cell, Style, Table};
    5 5   
    6 6  mod file_open;
     7 +mod sb_mount;
    7 8  mod socket_bind;
    8 9  mod socket_connect;
    9 10  mod task_fix_setuid;
    10 11   
    11 12  use ebpfguard::{policy::reader, PolicyManager};
    12 13  use file_open::list_file_open;
     14 +use sb_mount::list_sb_mount;
    13 15  use socket_bind::list_socket_bind;
    14 16  use socket_connect::list_socket_connect;
    15 17  use task_fix_setuid::list_task_fix_setuid;
    skipped 64 lines
    80 82  async fn list_policies(policy_manager: &mut PolicyManager) -> anyhow::Result<()> {
    81 83   let file_open = list_file_open(policy_manager).await?;
    82 84   let setuid = list_task_fix_setuid(policy_manager).await?;
     85 + let sb_mount = list_sb_mount(policy_manager).await?;
    83 86   let socket_bind = list_socket_bind(policy_manager).await?;
    84 87   let socket_connect = list_socket_connect(policy_manager).await?;
    85 88   
    skipped 2 lines
    88 91   vec![file_open.display()?.cell()],
    89 92   vec!["setuid".cell()],
    90 93   vec![setuid.display()?.cell()],
     94 + vec!["sb_mount".cell()],
     95 + vec![sb_mount.display()?.cell()],
    91 96   vec!["socket_bind".cell()],
    92 97   vec![socket_bind.display()?.cell()],
    93 98   vec!["socket_connect".cell()],
    skipped 10 lines
  • ■ ■ ■ ■ ■ ■
    examples/cli/examples/cli/sb_mount.rs
     1 +use cli_table::{Cell, Style, Table, TableStruct};
     2 +use ebpfguard::PolicyManager;
     3 + 
     4 +pub(crate) async fn list_sb_mount(
     5 + policy_manager: &mut PolicyManager,
     6 +) -> anyhow::Result<TableStruct> {
     7 + let mut table = Vec::new();
     8 + 
     9 + let sb_mount = policy_manager.manage_sb_mount()?;
     10 + 
     11 + for policy in sb_mount.list_policies().await? {
     12 + table.push(vec![policy.subject.to_string(), policy.allow.to_string()]);
     13 + }
     14 + 
     15 + let table = table.table().title(vec![
     16 + "action".cell().bold(true),
     17 + "subject".cell().bold(true),
     18 + ]);
     19 + 
     20 + Ok(table)
     21 +}
     22 + 
  • ■ ■ ■ ■ ■ ■
    examples/cli/policy.yaml
    skipped 3 lines
    4 4  - !setuid
    5 5   subject: !binary /usr/bin/sudo
    6 6   allow: true
    7  -- !file_open
     7 +- !sb_mount
    8 8   subject: all
    9  - allow: all
    10  - deny: !paths
    11  - - /tmp/test
     9 + allow: false
     10 +- !sb_mount
     11 + subject: !binary /usr/bin/mount
     12 + allow: true
    12 13  - !socket_bind
    13 14   subject: all
    14 15   allow: !ports
    skipped 9 lines
  • ■ ■ ■ ■ ■ ■
    examples/mount/Cargo.toml
     1 +[package]
     2 +name = "mount"
     3 +version = "0.1.0"
     4 +edition = "2021"
     5 + 
     6 +[dependencies]
     7 +anyhow = { version = "1", features = ["backtrace"] }
     8 +clap = { version = "4.2", features = ["derive"] }
     9 +env_logger = "0.10"
     10 +ebpfguard = { path = "../../ebpfguard" }
     11 +log = "0.4"
     12 +tokio = { version = "1.25", features = ["macros", "rt", "rt-multi-thread", "net", "signal", "sync"] }
     13 + 
  • ■ ■ ■ ■ ■ ■
    examples/mount/examples/mount.rs
     1 +use std::{
     2 + fs::{create_dir_all, remove_dir_all},
     3 + path::PathBuf,
     4 +};
     5 + 
     6 +use clap::Parser;
     7 +use ebpfguard::{
     8 + policy::{PolicySubject, SbMount, SbRemount, SbUmount},
     9 + PolicyManager,
     10 +};
     11 +use log::info;
     12 + 
     13 +#[derive(Debug, Parser)]
     14 +struct Opt {
     15 + #[clap(long, default_value = "/sys/fs/bpf")]
     16 + bpffs_path: PathBuf,
     17 + #[clap(long, default_value = "example_sb_mount")]
     18 + bpffs_dir: PathBuf,
     19 + /// Binary which should be allowed to mount filesystems.
     20 + #[clap(long)]
     21 + allow: Option<PathBuf>,
     22 +}
     23 + 
     24 +#[tokio::main]
     25 +async fn main() -> anyhow::Result<()> {
     26 + let opt = Opt::parse();
     27 + 
     28 + env_logger::init();
     29 + 
     30 + // Create a directory where ebpfguard policy manager can store its BPF
     31 + // objects (maps).
     32 + let bpf_path = opt.bpffs_path.join(opt.bpffs_dir);
     33 + create_dir_all(&bpf_path)?;
     34 + 
     35 + // Create a policy manager.
     36 + let mut policy_manager = PolicyManager::new(&bpf_path)?;
     37 + 
     38 + // Attach the policy manager to the mount LSM hooks.
     39 + let mut sb_mount = policy_manager.attach_sb_mount()?;
     40 + let mut sb_remount = policy_manager.attach_sb_remount()?;
     41 + let mut sb_umount = policy_manager.attach_sb_umount()?;
     42 + 
     43 + // Get the receiver end of the alerts channel (for the `file_open` LSM
     44 + // hook).
     45 + let mut sb_mount_rx = sb_mount.alerts().await?;
     46 + let mut sb_remount_rx = sb_remount.alerts().await?;
     47 + let mut sb_umount_rx = sb_umount.alerts().await?;
     48 + 
     49 + // Define policies which deny mount operations for all processes (except
     50 + // for the specified subject, if defined).
     51 + sb_mount
     52 + .add_policy(SbMount {
     53 + subject: PolicySubject::All,
     54 + allow: false,
     55 + })
     56 + .await?;
     57 + sb_remount
     58 + .add_policy(SbRemount {
     59 + subject: PolicySubject::All,
     60 + allow: false,
     61 + })
     62 + .await?;
     63 + sb_umount
     64 + .add_policy(SbUmount {
     65 + subject: PolicySubject::All,
     66 + allow: false,
     67 + })
     68 + .await?;
     69 + if let Some(subject) = opt.allow {
     70 + sb_mount
     71 + .add_policy(SbMount {
     72 + subject: PolicySubject::Binary(subject.clone()),
     73 + allow: true,
     74 + })
     75 + .await?;
     76 + sb_remount
     77 + .add_policy(SbRemount {
     78 + subject: PolicySubject::Binary(subject.clone()),
     79 + allow: true,
     80 + })
     81 + .await?;
     82 + sb_umount
     83 + .add_policy(SbUmount {
     84 + subject: PolicySubject::Binary(subject),
     85 + allow: true,
     86 + })
     87 + .await?;
     88 + }
     89 + 
     90 + info!("Waiting for Ctrl-C...");
     91 + 
     92 + // Wait for policy violation alerts (or for CTRL+C).
     93 + loop {
     94 + tokio::select! {
     95 + Some(alert) = sb_mount_rx.recv() => {
     96 + info!(
     97 + "sb_mount: pid={} subject={}",
     98 + alert.pid,
     99 + alert.subject,
     100 + );
     101 + }
     102 + Some(alert) = sb_remount_rx.recv() => {
     103 + info!(
     104 + "sb_remount: pid={} subject={}",
     105 + alert.pid,
     106 + alert.subject,
     107 + );
     108 + }
     109 + Some(alert) = sb_umount_rx.recv() => {
     110 + info!(
     111 + "sb_umount: pid={} subject={}",
     112 + alert.pid,
     113 + alert.subject,
     114 + );
     115 + }
     116 + _ = tokio::signal::ctrl_c() => {
     117 + break;
     118 + }
     119 + }
     120 + }
     121 + 
     122 + info!("Exiting...");
     123 + remove_dir_all(&bpf_path)?;
     124 + 
     125 + Ok(())
     126 +}
     127 + 
Please wait...
Page is in error, reload to recover