Skip to content

Commit 02b394c

Browse files
committed
feat(apparmor): add support for optionally specifying a loaded apparmor profile to use for a process
1 parent d5911ce commit 02b394c

4 files changed

Lines changed: 40 additions & 0 deletions

File tree

src/apparmor.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//! AppArmor profile transition for the workload process.
2+
3+
use std::io::Write;
4+
5+
/// Stage an AppArmor profile transition that takes effect on the next `execve`
6+
/// (the kernel's `aa_change_onexec` interface).
7+
///
8+
/// The named profile must already be loaded in the kernel. Writing an un-loaded/unknown
9+
/// profile name here will cause the next `execve` to fail with `-ENOENT`.
10+
/// Must be called after `PR_SET_NO_NEW_PRIVS` and before `execvpe()`.
11+
///
12+
/// The command must reach the kernel in a single `write(2)`, so it is formatted
13+
/// into one buffer. Writes to the per-LSM attr node `/proc/self/attr/apparmor/exec`
14+
/// (present on Linux 5.1+), and falls back to the pre-5.1 global node `/proc/self/attr/exec`.
15+
pub fn change_onexec(profile: &str) -> std::io::Result<()> {
16+
let cmd = format!("exec {profile}");
17+
let mut file = match std::fs::OpenOptions::new()
18+
.write(true)
19+
.open("/proc/self/attr/apparmor/exec")
20+
{
21+
Ok(file) => file,
22+
Err(e) if e.kind() == std::io::ErrorKind::NotFound => std::fs::OpenOptions::new()
23+
.write(true)
24+
.open("/proc/self/attr/exec")?,
25+
Err(e) => return Err(e),
26+
};
27+
file.write_all(cmd.as_bytes())
28+
}

src/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ pub struct ExecutableSpec {
8686
#[serde(default)]
8787
pub seccomp: Option<SeccompFilter>,
8888

89+
/// An optional AppArmor profile name to transition to on `execve`. The named
90+
/// profile must already be loaded in the kernel. Staged after
91+
/// `PR_SET_NO_NEW_PRIVS`, before `execvpe()`.
92+
#[serde(default)]
93+
pub apparmor: Option<String>,
94+
8995
/// An optional out-of-memory score adjustment value.
9096
pub oom_score_adj: Option<i32>,
9197
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod apparmor;
12
pub mod caps;
23
pub mod cgroup;
34
pub mod config;

src/wrap.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,11 @@ impl ExecutableSpec {
725725
unsafe { filter.install()? };
726726
}
727727

728+
if let Some(profile) = &self.apparmor {
729+
crate::apparmor::change_onexec(profile)
730+
.map_err(|e| anyhow!("failed to set AppArmor profile {profile:?}: {e}"))?;
731+
}
732+
728733
// The Rust runtime ignores SIGPIPE (SIG_IGN) process-wide, and that
729734
// disposition is inherited across execve. Restore SIG_DFL so the
730735
// workload sees the standard broken-pipe behaviour, matching runc/crun.

0 commit comments

Comments
 (0)