Skip to content

Commit 32b9983

Browse files
committed
collect kernel config
1 parent 839c969 commit 32b9983

2 files changed

Lines changed: 109 additions & 48 deletions

File tree

src/checkers/kernel.rs

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
use crate::helpers::{
22
CheckGroup, CheckGroupResult, CheckResult,
3-
CheckResultValue::{Errored, Failed, Passed}, host_executor::HostNamespaceExecutor,
3+
CheckResultValue::{Errored, Failed, Passed},
4+
host_executor::HostNamespaceExecutor,
45
};
56

67
use anyhow::{Result, bail};
78
use async_trait::async_trait;
9+
use futures::{FutureExt, future::join_all};
810
use log::debug;
911
use procfs::{Current, sys::kernel};
1012
use std::{fs, path::PathBuf, process::Command};
11-
use futures::{future::join_all, FutureExt};
1213

1314
const GROUP_IDENTIFIER: &str = "KernelChecks";
1415
const NAME: &str = "Kernel Checks";
1516
// TODO (bml) assemble actual list
16-
const REQUIRED_MODULES: &[&str] = &[
17-
"nf_tables",
18-
];
17+
const REQUIRED_MODULES: &[&str] = &["nf_tables"];
1918

2019
pub struct KernelChecks {
2120
host_executor: HostNamespaceExecutor,
@@ -29,12 +28,7 @@ impl KernelChecks {
2928
/// Run all the checkers asynchronously, then
3029
/// join and collect the results.
3130
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-
31+
let results = join_all([self.has_modules().boxed(), self.version_is_good().boxed()]).await;
3832

3933
let mut group_result = Passed;
4034
for res in results.iter() {
@@ -60,9 +54,11 @@ impl KernelChecks {
6054
let mut result = Passed;
6155

6256
// 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");
57+
let current = self
58+
.host_executor
59+
.spawn_in_host_ns(async { kernel::Version::current() })
60+
.await
61+
.expect("error spawning in host");
6662

6763
if let Err(e) = current {
6864
return CheckResult::new(&name, Errored(e.to_string()));
@@ -80,18 +76,23 @@ impl KernelChecks {
8076
let name = String::from("Host Has Necessary Modules");
8177
let mut result = Passed;
8278

83-
let required_modules: Vec<String> = REQUIRED_MODULES.iter().map(|s| s.to_string()).collect();
79+
let required_modules: Vec<String> =
80+
REQUIRED_MODULES.iter().map(|s| s.to_string()).collect();
8481

8582
// Search builtin modules
8683
let remaining = match self.find_builtins(&required_modules).await {
8784
Ok(r) => r,
88-
Err(e) => return CheckResult::new(&name, Errored(format!("getting kernel builtins {e}"))),
85+
Err(e) => {
86+
return CheckResult::new(&name, Errored(format!("getting kernel builtins {e}")));
87+
}
8988
};
9089

9190
// Search loaded modules
9291
let remaining = match self.find_loaded(&remaining).await {
9392
Ok(r) => r,
94-
Err(e) => return CheckResult::new(&name, Errored(format!("getting kernel modules {e}"))),
93+
Err(e) => {
94+
return CheckResult::new(&name, Errored(format!("getting kernel modules {e}")));
95+
}
9596
};
9697
if !remaining.is_empty() {
9798
result = Failed(format!("missing {:?}", remaining))
@@ -107,20 +108,21 @@ impl KernelChecks {
107108
let mut modules_to_find: Vec<String> = required_modules.clone();
108109

109110
// 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??;
111+
let builtins = self
112+
.host_executor
113+
.spawn_in_host_ns(async move {
114+
// Get kernel version
115+
let output = Command::new("uname").arg("-r").output()?;
116+
117+
if !output.status.success() {
118+
let error_message = String::from_utf8_lossy(&output.stderr);
119+
bail!("{}", error_message);
120+
}
121+
let kernel_version = String::from_utf8_lossy(&output.stdout).trim().to_string();
122+
let path = PathBuf::from(format!("/lib/modules/{kernel_version}/modules.builtin"));
123+
fs::read_to_string(path).map_err(|e| anyhow::anyhow!(e))
124+
})
125+
.await??;
124126

125127
for builtin in builtins.lines() {
126128
let found = modules_to_find
@@ -142,16 +144,15 @@ impl KernelChecks {
142144
async fn find_loaded(&self, required_modules: &Vec<String>) -> Result<Vec<String>> {
143145
let mut modules_to_find: Vec<String> = required_modules.clone();
144146

145-
let modules = self.host_executor.spawn_in_host_ns(async move {
146-
procfs::KernelModules::current()
147-
}).await?;
147+
let modules = self
148+
.host_executor
149+
.spawn_in_host_ns(async move { procfs::KernelModules::current() })
150+
.await?;
148151

149152
let modules = modules.unwrap();
150153

151154
for (name, _) in modules.0.iter() {
152-
let found = modules_to_find
153-
.iter()
154-
.position(|required| required == name);
155+
let found = modules_to_find.iter().position(|required| required == name);
155156

156157
if let Some(index) = found {
157158
debug!("module {}", modules_to_find[index]);

src/recorders/system.rs

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
use anyhow::{Result, bail};
12
use async_trait::async_trait;
2-
use futures::FutureExt;
3-
use futures::future::join_all;
4-
use std::path::{Path, PathBuf};
5-
use std::process::Command;
3+
use flate2::read::GzDecoder;
4+
use futures::{FutureExt, future::join_all};
5+
use std::{
6+
io::Read,
7+
path::{Path, PathBuf},
8+
process::Command,
9+
};
610

711
use crate::helpers::{
812
CheckGroup, CheckGroupResult, CheckResult,
@@ -31,6 +35,7 @@ impl SystemRecorder {
3135
self.record_cpuinfo().boxed(),
3236
self.record_cmdline().boxed(),
3337
self.record_grub_cfg().boxed(),
38+
self.record_kernel_cfg().boxed(),
3439
])
3540
.await;
3641

@@ -92,15 +97,34 @@ impl SystemRecorder {
9297
return None;
9398
}
9499
let name = format!("Record {}", local_file.display());
95-
let output = tokio::fs::read_to_string(&local_file).await;
96-
if let Err(e) = output {
97-
return Some(CheckResult::new(
100+
101+
let bytes = match tokio::fs::read(&local_file).await {
102+
Ok(b) => b,
103+
Err(e) => {
104+
return Some(CheckResult::new(
105+
&name,
106+
Errored(format!("failed to read {}: {e}", local_file.display())),
107+
));
108+
}
109+
};
110+
111+
let content = if bytes.starts_with(&[0x1f, 0x8b]) {
112+
// Gzip magic bytes detected
113+
let mut decoder = GzDecoder::new(&bytes[..]);
114+
let mut s = String::new();
115+
decoder.read_to_string(&mut s).map(|_| s)
116+
} else {
117+
String::from_utf8(bytes)
118+
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
119+
};
120+
121+
match content {
122+
Ok(c) => Some(CheckResult::new_with_output(&name, Passed, Some(c))),
123+
Err(e) => Some(CheckResult::new(
98124
&name,
99-
Errored(format!("failed to read {}: {e}", local_file.display())),
100-
));
125+
Errored(format!("failed to decode {}: {e}", local_file.display())),
126+
)),
101127
}
102-
let output = output.unwrap();
103-
Some(CheckResult::new_with_output(&name, Passed, Some(output)))
104128
})
105129
.await
106130
.unwrap_or_else(|_| panic!("could not record {}", file.display()))
@@ -141,6 +165,42 @@ impl SystemRecorder {
141165
Errored(format!("failed to find any {:?}", files)),
142166
)
143167
}
168+
169+
async fn record_kernel_cfg(&self) -> CheckResult {
170+
let name = "Record kernel config";
171+
// Get kernel version
172+
//
173+
let Ok(kver) = self.current_kernel_version().await else {
174+
return CheckResult::new(name, Errored("failed to find kernel version".to_string()));
175+
};
176+
177+
let files = ["/proc/config.gz", &format!("boot/config-{kver}")];
178+
179+
for file in files {
180+
if let Some(result) = self.record_file(&PathBuf::from(file)).await {
181+
return result;
182+
}
183+
}
184+
CheckResult::new(name, Errored(format!("failed to find any {:?}", files)))
185+
}
186+
187+
async fn current_kernel_version(&self) -> Result<String> {
188+
self.host_executor
189+
.spawn_in_host_ns(async {
190+
let output = Command::new("uname")
191+
.arg("-r")
192+
.output()
193+
.expect("Failed to execute command");
194+
195+
if !output.status.success() {
196+
let error_message = String::from_utf8_lossy(&output.stderr);
197+
bail!("{}", error_message);
198+
}
199+
200+
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
201+
})
202+
.await?
203+
}
144204
}
145205

146206
#[async_trait]

0 commit comments

Comments
 (0)