1use sysknife_types::RiskLevel;
2
3pub mod apparmor;
4pub mod apt;
5pub mod apt_preferences;
6pub mod auditd;
7pub mod certbot;
8pub mod cloudinit;
9pub mod containers;
10pub mod deployment;
11pub mod distrobox;
12pub mod fail2ban;
13pub mod filesystem;
14pub mod flatpak;
15pub mod grub;
16pub mod identity;
17pub mod journald;
18pub mod layering;
19pub mod livepatch;
20pub mod logging;
21pub mod lvm;
22pub mod mounts;
23pub mod multipass;
24pub mod netplan;
25pub mod network;
26pub mod package_repos;
27pub mod pam;
28pub mod ppa;
29pub mod processes;
30pub mod reboot;
31pub mod release_upgrade;
32pub mod resolvectl;
33pub mod services;
34pub mod snap;
35pub mod ssh;
36pub mod sudoers;
37pub mod sysctl;
38pub mod system_info;
39pub mod toolbox;
40pub mod ubuntu_pro;
41pub mod ufw;
42pub mod users;
43pub(crate) mod validate;
44
45#[derive(Clone, Debug, PartialEq, Eq)]
46pub enum ActionMechanism {
47 Command {
48 program: &'static str,
49 args: Vec<String>,
50 },
51 FileScan {
52 path: String,
53 },
54 FileWrite {
55 path: String,
56 content: String,
57 },
58 FilePatch {
59 path: String,
60 search: String,
61 replace: String,
62 },
63 FileDelete {
64 path: String,
65 },
66}
67
68#[derive(Clone, Debug, PartialEq, Eq)]
69pub struct ActionSpec {
70 pub action_name: &'static str,
71 pub mechanism: ActionMechanism,
72 pub risk_level: RiskLevel,
73 pub reboot_required: bool,
74 pub rollback_available: bool,
75}
76
77pub fn catalogue() -> Vec<(&'static str, Vec<ActionSpec>)> {
86 vec![
87 ("Deployment (atomic host)", deployment::specs()),
88 ("Package layering (rpm-ostree)", layering::specs()),
89 ("Filesystem", filesystem::specs()),
90 ("Flatpak", flatpak::specs()),
91 ("Toolbox", toolbox::specs()),
92 ("Services", services::specs()),
93 ("Processes", processes::specs()),
94 ("Journald", journald::specs()),
95 ("Storage — LVM", lvm::specs()),
96 ("Kernel parameters — sysctl", sysctl::specs()),
97 ("Mounts & swap", mounts::specs()),
98 ("Log management", logging::specs()),
99 ("PAM password policy", pam::specs()),
100 ("auditd", auditd::specs()),
101 ("certbot / ACME", certbot::specs()),
102 ("Scoped sudoers.d", sudoers::specs()),
103 ("Network", network::specs()),
104 ("resolvectl", resolvectl::specs()),
105 ("Identity / time / locale", identity::specs()),
106 ("Users & groups", users::specs()),
107 ("SSH keys", ssh::specs()),
108 ("Package repositories", package_repos::specs()),
109 ("System info", system_info::specs()),
110 ("Containers", containers::specs()),
111 ("Reboot", reboot::specs()),
112 ("AppArmor", apparmor::specs()),
113 ("cloud-init", cloudinit::specs()),
114 ("fail2ban", fail2ban::specs()),
115 ("apt", apt::specs()),
116 ("apt preferences / pinning", apt_preferences::specs()),
117 ("PPA", ppa::specs()),
118 ("snap", snap::specs()),
119 ("ufw", ufw::specs()),
120 ("netplan", netplan::specs()),
121 ("distrobox", distrobox::specs()),
122 ("GRUB", grub::specs()),
123 ("Ubuntu release upgrade", release_upgrade::specs()),
124 ("Ubuntu Pro", ubuntu_pro::specs()),
125 ("Livepatch", livepatch::specs()),
126 ("Multipass", multipass::specs()),
127 ]
128}
129
130pub fn all_specs() -> Vec<ActionSpec> {
132 catalogue()
133 .into_iter()
134 .flat_map(|(_, specs)| specs)
135 .collect()
136}
137
138#[derive(Clone, Copy, Debug, PartialEq, Eq)]
142pub struct SpecMeta {
143 pub risk_level: RiskLevel,
144 pub reboot_required: bool,
145 pub rollback_available: bool,
146}
147
148static SPEC_META: std::sync::LazyLock<std::collections::HashMap<&'static str, SpecMeta>> =
149 std::sync::LazyLock::new(|| {
150 all_specs()
151 .into_iter()
152 .map(|s| {
153 (
154 s.action_name,
155 SpecMeta {
156 risk_level: s.risk_level,
157 reboot_required: s.reboot_required,
158 rollback_available: s.rollback_available,
159 },
160 )
161 })
162 .collect()
163 });
164
165pub fn spec_meta(action_name: &str) -> Option<SpecMeta> {
168 SPEC_META.get(action_name).copied()
169}
170
171pub(crate) fn command_mechanism(
172 program: &'static str,
173 args: impl IntoIterator<Item = impl Into<String>>,
174) -> ActionMechanism {
175 ActionMechanism::Command {
176 program,
177 args: args.into_iter().map(Into::into).collect(),
178 }
179}
180
181pub(crate) fn file_write_mechanism(
182 path: impl Into<String>,
183 content: impl Into<String>,
184) -> ActionMechanism {
185 ActionMechanism::FileWrite {
186 path: path.into(),
187 content: content.into(),
188 }
189}
190
191pub(crate) fn file_patch_mechanism(
192 path: impl Into<String>,
193 search: impl Into<String>,
194 replace: impl Into<String>,
195) -> ActionMechanism {
196 ActionMechanism::FilePatch {
197 path: path.into(),
198 search: search.into(),
199 replace: replace.into(),
200 }
201}
202
203pub(crate) fn file_scan_mechanism(path: impl Into<String>) -> ActionMechanism {
204 ActionMechanism::FileScan { path: path.into() }
205}
206
207pub(crate) fn file_delete_mechanism(path: impl Into<String>) -> ActionMechanism {
208 ActionMechanism::FileDelete { path: path.into() }
209}