1use std::{
4 process::{Command, Stdio},
5 thread, time,
6};
7
8use anyhow::{anyhow, Result};
9use libtest_mimic::{Arguments, Failed, Trial};
10use protect_tool::get_tests;
11use seabee::{
12 config::SecurityLevel,
13 constants::{self, SEABEECTL_EXE},
14};
15use test_constants::{SHUTDOWN_REQUEST, SHUTDOWN_REQUEST_SIG};
16
17mod daemon_status;
18mod protect_tool;
19mod shared;
20pub mod test_constants;
21mod unverified_policy;
22mod verified_keys;
23mod verified_policy;
24
25const VERIFIED_POLICY_CONFIG: &str = "configs/verified_policy.yaml";
26const VERIFIED_KEYS_CONFIG: &str = "configs/verified_keys.yaml";
27const UNVERIFIED_POLICY_CONFIG: &str = "configs/unverified_policy.yaml";
28
29fn start_daemon() -> Result<()> {
30 let status = Command::new("systemctl")
31 .args(["start", "test_seabee.service"])
32 .status()?;
33 if !status.success() {
34 return Err(anyhow!(
35 "Failed to start SeaBee daemon.\nstatus: {}",
36 status
37 ));
38 }
39
40 let max_wait_seconds = 15;
42 let mut waited = 0;
43 while waited < max_wait_seconds {
44 if daemon_is_ready()? {
45 break;
46 } else {
47 thread::sleep(time::Duration::from_secs(1));
48 waited += 1;
49 }
50 }
51 if waited >= max_wait_seconds {
52 return Err(anyhow!(
53 "Daemon failed to start. Reached max wait time of {} seconds.\nCheck logs with 'journalctl -u test_seabee -f'",
54 max_wait_seconds
55 ));
56 }
57
58 Ok(())
59}
60
61fn daemon_is_ready() -> Result<bool> {
63 let status = Command::new(SEABEECTL_EXE)
64 .arg("list")
65 .stdout(Stdio::null())
66 .stderr(Stdio::null())
67 .status()?;
68 Ok(status.success())
69}
70
71fn stop_daemon() -> Result<()> {
72 let status = Command::new(SEABEECTL_EXE)
74 .args([
75 "shutdown",
76 "-t",
77 SHUTDOWN_REQUEST,
78 "-s",
79 SHUTDOWN_REQUEST_SIG,
80 ])
81 .stdout(Stdio::null())
82 .status()?;
83 if !status.success() {
84 return Err(anyhow!("Failed to shutdown SeaBee.\nstatus: {}", status));
85 }
86
87 Ok(())
88}
89
90fn update_config(path: &str) -> Result<()> {
91 let status = Command::new(SEABEECTL_EXE)
92 .args(["config", "update", path])
93 .stdout(Stdio::null())
94 .status()?;
95 if !status.success() {
96 return Err(anyhow!(
97 "Failed to update SeaBee config.\nstatus: {}",
98 status
99 ));
100 }
101 Ok(())
102}
103
104fn remove_config() -> Result<()> {
105 let status = Command::new(SEABEECTL_EXE)
106 .args(["config", "remove"])
107 .stdout(Stdio::null())
108 .status()?;
109 if !status.success() {
110 return Err(anyhow!(
111 "Failed to remove SeaBee config.\nstatus: {}",
112 status
113 ));
114 }
115 Ok(())
116}
117
118fn seabeectl_clean_keys_policies() -> Result<()> {
119 let status = Command::new(SEABEECTL_EXE)
120 .args(["clean", "policy"])
121 .stdout(Stdio::null())
122 .status()?;
123 if !status.success() {
124 return Err(anyhow!(
125 "Failed to run seabeectl clean policy\nstatus: {}",
126 status
127 ));
128 }
129 let status = Command::new(SEABEECTL_EXE)
130 .args(["clean", "keys"])
131 .stdout(Stdio::null())
132 .status()?;
133 if !status.success() {
134 return Err(anyhow!(
135 "Failed to run seabeectl clean keys\nstatus: {}",
136 status
137 ));
138 }
139 Ok(())
140}
141
142fn check_root_key() -> Result<()> {
143 if !std::path::Path::new(constants::SEABEE_ROOT_KEY_PATH).exists() {
144 return Err(anyhow!(
145 "No root key installed at {}",
146 constants::SEABEE_ROOT_KEY_PATH
147 ));
148 }
149 Ok(())
150}
151
152fn policy_test_setup() -> Result<()> {
153 seabee::utils::verify_requirements()?;
154 check_root_key()?;
155 seabeectl_clean_keys_policies()?;
156 Ok(())
157}
158
159fn policy_test_teardown() -> Result<()> {
160 remove_config()?;
161 Ok(())
162}
163
164fn run_tests_with_config(args: &Arguments, tests: Vec<Trial>, config: &str) -> Result<(), Failed> {
165 update_config(config)?;
166 start_daemon()?;
167 let conclusion = libtest_mimic::run(args, tests);
168 stop_daemon()?;
169
170 if conclusion.has_failed() {
171 return Err("At least one test failed".into());
172 }
173
174 Ok(())
175}
176
177fn run_test_tool_with_config(args: &Arguments, level: SecurityLevel) -> Result<(), Failed> {
178 let child = protect_tool::start_test_tool(level)
179 .map_err(|e| anyhow!("failed to start test tool: {e}"))?;
180 let conclusion = libtest_mimic::run(args, get_tests(level));
181 protect_tool::stop_test_tool(child).map_err(|e| anyhow!("failed to stop test_tool: {e}"))?;
182
183 if conclusion.has_failed() {
184 return Err("At least one test failed".into());
185 }
186 Ok(())
187}
188
189fn run_protect_tool_tests(args: &Arguments) -> Result<(), Failed> {
190 update_config(VERIFIED_POLICY_CONFIG)?;
192 start_daemon()?;
193 if let Err(e) = run_test_tool_with_config(args, SecurityLevel::block) {
194 stop_daemon()?;
195 return Err(e);
196 }
197 let ret = run_test_tool_with_config(args, SecurityLevel::audit);
198 stop_daemon()?;
199 ret
200}
201
202pub fn run_policy_tests(args: &Arguments) -> Result<(), Failed> {
203 policy_test_setup()?;
204
205 println!("Run Verified Policy Tests");
206 run_tests_with_config(args, verified_policy::tests(), VERIFIED_POLICY_CONFIG)?;
207 println!("Run Verified Key Tests");
208 run_tests_with_config(args, verified_keys::tests(), VERIFIED_KEYS_CONFIG)?;
209 println!("Run Unverified Policy Tests");
210 run_tests_with_config(args, unverified_policy::tests(), UNVERIFIED_POLICY_CONFIG)?;
211 println!("Test Using Policy to Secure Another Tool");
212 run_protect_tool_tests(args)?;
213
214 policy_test_teardown()?;
215 Ok(())
216}