Skip to content

Commit 839c969

Browse files
committed
Fixup kernel mods
1 parent 0f0423d commit 839c969

2 files changed

Lines changed: 90 additions & 64 deletions

File tree

src/checkers/kernel.rs

Lines changed: 89 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,40 @@
11
use crate::helpers::{
22
CheckGroup, CheckGroupResult, CheckResult,
3-
CheckResultValue::{Errored, Failed, Passed},
3+
CheckResultValue::{Errored, Failed, Passed}, host_executor::HostNamespaceExecutor,
44
};
55

66
use anyhow::{Result, bail};
77
use async_trait::async_trait;
88
use log::debug;
99
use procfs::{Current, sys::kernel};
10-
use std::fs;
11-
use std::path::PathBuf;
12-
use std::process::Command;
10+
use std::{fs, path::PathBuf, process::Command};
11+
use futures::{future::join_all, FutureExt};
1312

1413
const GROUP_IDENTIFIER: &str = "KernelChecks";
1514
const NAME: &str = "Kernel Checks";
15+
// TODO (bml) assemble actual list
16+
const REQUIRED_MODULES: &[&str] = &[
17+
"nf_tables",
18+
];
1619

17-
pub struct KernelChecks;
20+
pub struct KernelChecks {
21+
host_executor: HostNamespaceExecutor,
22+
}
1823

1924
impl KernelChecks {
20-
pub fn run_all(&self) -> CheckGroupResult {
21-
let results = vec![self.has_modules(), self.version_is_good()];
25+
pub fn new(host_executor: HostNamespaceExecutor) -> Self {
26+
KernelChecks { host_executor }
27+
}
28+
29+
/// Run all the checkers asynchronously, then
30+
/// join and collect the results.
31+
pub async fn run_all(&self) -> CheckGroupResult {
32+
let results = join_all([
33+
self.has_modules().boxed(),
34+
self.version_is_good().boxed(),
35+
])
36+
.await;
37+
2238

2339
let mut group_result = Passed;
2440
for res in results.iter() {
@@ -39,27 +55,15 @@ impl KernelChecks {
3955
}
4056
}
4157

42-
fn current_kernel_version(&self) -> Result<String> {
43-
let output = Command::new("uname")
44-
.arg("-r")
45-
.output()
46-
.expect("Failed to execute command");
47-
48-
if !output.status.success() {
49-
let error_message = String::from_utf8_lossy(&output.stderr);
50-
bail!("{}", error_message.to_string());
51-
}
52-
53-
let kernel_version = String::from_utf8_lossy(&output.stdout).trim().to_string();
54-
Ok(kernel_version)
55-
}
56-
57-
fn version_is_good(&self) -> CheckResult {
58+
async fn version_is_good(&self) -> CheckResult {
5859
let name = String::from("Host Kernel Version Is Good");
5960
let mut result = Passed;
6061

61-
// Get kernel version
62-
let current = kernel::Version::current();
62+
// Get host kernel version
63+
let current = self.host_executor.spawn_in_host_ns(async {
64+
kernel::Version::current()
65+
}).await.expect("error spawning in host");
66+
6367
if let Err(e) = current {
6468
return CheckResult::new(&name, Errored(e.to_string()));
6569
}
@@ -72,68 +76,90 @@ impl KernelChecks {
7276
CheckResult::new(&name, result)
7377
}
7478

75-
fn has_modules(&self) -> CheckResult {
79+
async fn has_modules(&self) -> CheckResult {
7680
let name = String::from("Host Has Necessary Modules");
7781
let mut result = Passed;
7882

79-
let mut required_modules = vec![
80-
"xen_evtchn",
81-
"xen-privcmd",
82-
"xen-netback",
83-
"xen-pciback",
84-
"xen-blkback",
85-
"xen-gntdev",
86-
"xen-gntalloc",
87-
];
88-
89-
// Get kernel version
90-
let kernel_version = self.current_kernel_version();
91-
if let Err(e) = kernel_version {
92-
return CheckResult::new(&name, Errored(e.to_string()));
93-
}
94-
let kernel_version = kernel_version.unwrap();
83+
let required_modules: Vec<String> = REQUIRED_MODULES.iter().map(|s| s.to_string()).collect();
9584

9685
// Search builtin modules
97-
let path = PathBuf::from(format!("/lib/modules/{kernel_version}/modules.builtin"));
98-
let builtins = fs::read_to_string(path);
99-
if let Err(e) = builtins {
100-
return CheckResult::new(&name, Errored(e.to_string()));
86+
let remaining = match self.find_builtins(&required_modules).await {
87+
Ok(r) => r,
88+
Err(e) => return CheckResult::new(&name, Errored(format!("getting kernel builtins {e}"))),
89+
};
90+
91+
// Search loaded modules
92+
let remaining = match self.find_loaded(&remaining).await {
93+
Ok(r) => r,
94+
Err(e) => return CheckResult::new(&name, Errored(format!("getting kernel modules {e}"))),
95+
};
96+
if !remaining.is_empty() {
97+
result = Failed(format!("missing {:?}", remaining))
10198
}
102-
let builtins = builtins.unwrap();
99+
100+
CheckResult::new(&name, result)
101+
}
102+
103+
/// Looks at builtins for kernel_version and compares that to the list of
104+
/// required modules.
105+
/// Returns a vec of everything from required_modules that WAS NOT found in builtins.
106+
async fn find_builtins(&self, required_modules: &Vec<String>) -> Result<Vec<String>> {
107+
let mut modules_to_find: Vec<String> = required_modules.clone();
108+
109+
// read host builtins
110+
let builtins = self.host_executor.spawn_in_host_ns(async move {
111+
// Get kernel version
112+
let output = Command::new("uname")
113+
.arg("-r")
114+
.output()?;
115+
116+
if !output.status.success() {
117+
let error_message = String::from_utf8_lossy(&output.stderr);
118+
bail!("{}", error_message.to_string());
119+
}
120+
let kernel_version = String::from_utf8_lossy(&output.stdout).trim().to_string();
121+
let path = PathBuf::from(format!("/lib/modules/{kernel_version}/modules.builtin"));
122+
fs::read_to_string(path).map_err(|e| anyhow::anyhow!(e))
123+
}).await??;
103124

104125
for builtin in builtins.lines() {
105-
let found = required_modules
126+
let found = modules_to_find
106127
.iter()
107128
.position(|required| builtin.contains(required));
108129

109130
if let Some(index) = found {
110-
debug!("builtin {}", required_modules[index]);
111-
required_modules.remove(index);
131+
debug!("builtin {}", modules_to_find[index]);
132+
modules_to_find.remove(index);
112133
}
113134
}
114135

115-
// Search loaded modules
116-
let modules = procfs::KernelModules::current();
117-
if let Err(e) = modules {
118-
return CheckResult::new(&name, Errored(format!("getting kernel modules {e}")));
119-
}
136+
Ok(modules_to_find)
137+
}
138+
139+
/// Looks at loaded modules for the current host kernel and compares that to the list of
140+
/// required modules.
141+
/// Returns a vec of everything from required_modules that WAS NOT loaded.
142+
async fn find_loaded(&self, required_modules: &Vec<String>) -> Result<Vec<String>> {
143+
let mut modules_to_find: Vec<String> = required_modules.clone();
144+
145+
let modules = self.host_executor.spawn_in_host_ns(async move {
146+
procfs::KernelModules::current()
147+
}).await?;
148+
120149
let modules = modules.unwrap();
121150

122151
for (name, _) in modules.0.iter() {
123-
let found = required_modules
152+
let found = modules_to_find
124153
.iter()
125154
.position(|required| required == name);
126155

127156
if let Some(index) = found {
128-
debug!("module {}", required_modules[index]);
129-
required_modules.remove(index);
157+
debug!("module {}", modules_to_find[index]);
158+
modules_to_find.remove(index);
130159
}
131160
}
132-
if !required_modules.is_empty() {
133-
result = Failed(format!("missing {:?}", required_modules))
134-
}
135161

136-
CheckResult::new(&name, result)
162+
Ok(modules_to_find)
137163
}
138164
}
139165

@@ -152,6 +178,6 @@ impl CheckGroup for KernelChecks {
152178
}
153179

154180
async fn run(&self) -> CheckGroupResult {
155-
self.run_all()
181+
self.run_all().await
156182
}
157183
}

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ async fn main() -> Result<()> {
110110
let groups: Vec<Box<dyn CheckGroup>> = vec![
111111
Box::new(SystemChecks),
112112
Box::new(ScriptChecks),
113-
Box::new(KernelChecks),
113+
Box::new(KernelChecks::new(host_executor.clone())),
114114
Box::new(SystemRecorder::new(host_executor.clone())),
115115
];
116116

0 commit comments

Comments
 (0)