tests/policy/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use 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    // wait for seabee daemon to be ready
41    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
61/// Uses "seabeectl list" to determine if seabee is ready
62fn 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    // shutdown
73    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    // start
191    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}