Skip to content

Commit

Permalink
feat(health): include fuse check
Browse files Browse the repository at this point in the history
  • Loading branch information
QaidVoid committed Oct 25, 2024
1 parent 293960f commit ee9d3b7
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 28 deletions.
34 changes: 34 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ serde_json = "1.0.128"
strip-ansi-escapes = "0.2.0"
termion = "4.0.3"
tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] }
which = "6.0.3"
xattr = { version = "1.3.1", default-features = false }
130 changes: 102 additions & 28 deletions src/core/health.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{cmp::Ordering, path::Path};
use std::{cmp::Ordering, future::Future, os::unix::fs::PermissionsExt, path::Path, pin::Pin};

use futures::future::join_all;
use libc::{fork, unshare, waitpid, CLONE_NEWUSER, PR_CAPBSET_READ};
use tokio::fs;

Expand Down Expand Up @@ -40,35 +41,22 @@ pub async fn check_health() {
errors.push("Your kernel does not support user namespaces");
}

if let Ok(content) = fs::read_to_string("/proc/sys/kernel/unprivileged_userns_clone").await {
if content.trim() == "0" {
errors.push("You must enable unprivileged_userns_clone");
}
}
if let Ok(content) = fs::read_to_string("/proc/sys/user/max_user_namespaces").await {
if content.trim() == "0" {
errors.push("You must enable max_user_namespaces");
}
}
if let Ok(content) = fs::read_to_string("/proc/sys/kernel/userns_restrict").await {
if content.trim() == "1" {
errors.push("You must disable userns_restrict");
}
}
if let Ok(content) =
fs::read_to_string("/proc/sys/kernel/apparmor_restrict_unprivileged_userns").await
{
if content.trim() == "1" {
errors.push("You must disable apparmor_restrict_unprivileged_userns");
}
}
let checks: Vec<Pin<Box<dyn Future<Output = Option<&'static str>>>>> = vec![
Box::pin(check_unprivileged_userns_clone()),
Box::pin(check_max_user_namespaces()),
Box::pin(check_userns_restrict()),
Box::pin(check_apparmor_restrict()),
Box::pin(check_capabilities()),
Box::pin(check_fusermount()),
];

if !check_capability(CAP_SYS_ADMIN) {
errors.push("Capability 'CAP_SYS_ADMIN' is not available.");
if !check_capability(CAP_MKNOD) {
errors.push("Capability 'CAP_MKNOD' is not available.");
let results = join_all(checks).await;

results.into_iter().for_each(|result| {
if let Some(error) = result {
errors.push(error);
}
}
});

for error in &errors {
warn!("{}", error);
Expand All @@ -83,3 +71,89 @@ pub async fn check_health() {
success!("Everything is in order.")
}
}

async fn check_unprivileged_userns_clone() -> Option<&'static str> {
let content = fs::read_to_string("/proc/sys/kernel/unprivileged_userns_clone")
.await
.ok()?;
if content.trim() == "0" {
Some("You must enable unprivileged_userns_clone")
} else {
None
}
}

async fn check_max_user_namespaces() -> Option<&'static str> {
let content = fs::read_to_string("/proc/sys/user/max_user_namespaces")
.await
.ok()?;
if content.trim() == "0" {
Some("You must enable max_user_namespaces")
} else {
None
}
}

async fn check_userns_restrict() -> Option<&'static str> {
let content = fs::read_to_string("/proc/sys/kernel/userns_restrict")
.await
.ok()?;
if content.trim() == "1" {
Some("You must disable userns_restrict")
} else {
None
}
}

async fn check_apparmor_restrict() -> Option<&'static str> {
let content = fs::read_to_string("/proc/sys/kernel/apparmor_restrict_unprivileged_userns")
.await
.ok()?;
if content.trim() == "1" {
Some("You must disable apparmor_restrict_unprivileged_userns")
} else {
None
}
}

async fn check_capabilities() -> Option<&'static str> {
if !check_capability(CAP_SYS_ADMIN) {
if !check_capability(CAP_MKNOD) {
return Some("Capability 'CAP_MKNOD' is not available.");
}
return Some("Capability 'CAP_SYS_ADMIN' is not available.");
}
None
}

async fn check_fusermount() -> Option<&'static str> {
match which::which("fusermount") {
Ok(path) => match path.metadata() {
Ok(meta) => {
let permissions = meta.permissions().mode();
if permissions != 0o104755 {
return Some(Box::leak(
format!(
"Invalid {} file mode bits. Set 4755 for {}",
"fusermount".color(Color::Blue),
path.to_string_lossy().color(Color::Green)
)
.into_boxed_str(),
) as &'static str);
}
}
Err(_) => return Some("Unable to read fusermount"),
},
Err(_) => {
return Some(Box::leak(
format!(
"{} not found. Please install {}",
"fusermount".color(Color::Blue),
"fuse".color(Color::Blue)
)
.into_boxed_str(),
))
}
}
None
}

0 comments on commit ee9d3b7

Please sign in to comment.