Skip to main content

sysknife_daemon/actions/
toolbox.rs

1use super::{ActionMechanism, ActionSpec};
2use sysknife_types::RiskLevel;
3
4pub fn specs() -> Vec<ActionSpec> {
5    vec![
6        list_toolboxes("testuser"),
7        create_toolbox("testuser", "sysknife-dev", Some("41"), None),
8        remove_toolbox("testuser", "sysknife-dev"),
9    ]
10}
11
12/// Run fixed Toolbox argv as the target user through the bounded helper.
13///
14/// Toolbox containers are per-user (rootless Podman under the hood). The
15/// daemon's `sysknife` system user has its own empty container store; we must
16/// switch to the correct user so toolbox reads the right storage path and
17/// sub-UID/GID ranges.
18///
19/// The helper sets XDG_RUNTIME_DIR from the resolved UID, drops credentials,
20/// and independently validates the complete Toolbox argument grammar. No
21/// caller-controlled shell string or environment assignment is accepted.
22fn toolbox_as(username: &str, parameters: &[&str]) -> ActionMechanism {
23    let mut args = vec![
24        "/usr/lib/sysknife/action-steps".to_string(),
25        "toolbox".to_string(),
26        username.to_string(),
27    ];
28    args.extend(parameters.iter().map(|value| value.to_string()));
29    ActionMechanism::Command {
30        program: "sudo",
31        args,
32    }
33}
34
35pub fn list_toolboxes(username: &str) -> ActionSpec {
36    // `toolbox list` (without --containers) lists both toolbox containers and
37    // images in a human-readable format and exits 0 even when the list is empty.
38    // --containers was dropped: it causes toolbox to probe the container runtime
39    // directly, which fails when sub-UID/GID ranges are not yet configured.
40    ActionSpec {
41        action_name: "ListToolboxes",
42        mechanism: toolbox_as(username, &["list"]),
43        risk_level: RiskLevel::Low,
44        reboot_required: false,
45        rollback_available: false,
46    }
47}
48
49pub fn create_toolbox(
50    username: &str,
51    name: &str,
52    release: Option<&str>,
53    image: Option<&str>,
54) -> ActionSpec {
55    let mut args = vec!["create", "--container", name];
56    if let Some(release) = release {
57        args.extend(["--release", release]);
58    }
59    if let Some(image) = image {
60        args.extend(["--image", image]);
61    }
62    ActionSpec {
63        action_name: "CreateToolbox",
64        mechanism: toolbox_as(username, &args),
65        risk_level: RiskLevel::Medium,
66        reboot_required: false,
67        rollback_available: false,
68    }
69}
70
71pub fn remove_toolbox(username: &str, name: &str) -> ActionSpec {
72    ActionSpec {
73        action_name: "RemoveToolbox",
74        mechanism: toolbox_as(username, &["rm", name]),
75        risk_level: RiskLevel::Medium,
76        reboot_required: false,
77        rollback_available: false,
78    }
79}