Skip to main content

libcontainer/
apparmor.rs

1use std::fs;
2use std::io::Write;
3use std::path::{Path, PathBuf};
4
5use pathrs::flags::OpenFlags;
6use pathrs::procfs::{ProcfsBase, ProcfsHandle};
7
8#[derive(Debug, thiserror::Error)]
9pub enum AppArmorError {
10    #[error("failed to apply AppArmor profile")]
11    ActivateProfile {
12        path: PathBuf,
13        profile: String,
14        source: std::io::Error,
15    },
16    #[error(transparent)]
17    Pathrs(#[from] pathrs::error::Error),
18}
19
20type Result<T> = std::result::Result<T, AppArmorError>;
21
22const ENABLED_PARAMETER_PATH: &str = "/sys/module/apparmor/parameters/enabled";
23
24/// Checks if AppArmor has been enabled on the system.
25pub fn is_enabled() -> std::result::Result<bool, std::io::Error> {
26    let aa_enabled = fs::read_to_string(ENABLED_PARAMETER_PATH)?;
27    Ok(aa_enabled.starts_with('Y'))
28}
29
30/// Applies an AppArmor profile to the container.
31pub fn apply_profile(profile: &str) -> Result<()> {
32    if profile.is_empty() {
33        return Ok(());
34    }
35
36    // Try the module specific subdirectory. This is the recommended way to configure
37    // LSMs since Linux 5.1. AppArmor has such a directory since Linux 5.8.
38    activate_profile(Path::new("attr/apparmor/exec"), profile)
39        // try the legacy interface
40        .or_else(|_| activate_profile(Path::new("attr/exec"), profile))
41}
42
43fn activate_profile(subpath: &Path, profile: &str) -> Result<()> {
44    ProcfsHandle::new()?
45        .open(
46            ProcfsBase::ProcSelf,
47            subpath,
48            OpenFlags::O_WRONLY | OpenFlags::O_CLOEXEC,
49        )?
50        .write_all(format!("exec {profile}").as_bytes())
51        .map_err(|err| AppArmorError::ActivateProfile {
52            path: PathBuf::from("/proc/self").join(subpath),
53            profile: profile.to_owned(),
54            source: err,
55        })
56}