1use super::{command_mechanism, ActionSpec};
2use sysknife_types::RiskLevel;
3
4pub fn specs() -> Vec<ActionSpec> {
5 vec![
6 configure_wifi("CafeHotspot", None),
7 set_dns_servers("wlp1s0", &["1.1.1.1", "8.8.8.8"]),
8 configure_firewall("public", "ssh", true),
9 get_firewall_state(),
10 get_network_status(),
11 get_listening_ports(),
12 get_nftables_ruleset(),
13 get_firewall_backend_state(),
14 ]
15}
16
17pub fn configure_wifi(ssid: &str, password: Option<&str>) -> ActionSpec {
18 let mut args = vec![
21 "nmcli".to_string(),
22 "device".to_string(),
23 "wifi".to_string(),
24 "connect".to_string(),
25 ssid.to_string(),
26 ];
27 if let Some(pw) = password {
28 args.push("password".to_string());
29 args.push(pw.to_string());
30 }
31 ActionSpec {
32 action_name: "ConfigureWifi",
33 mechanism: super::ActionMechanism::Command {
34 program: "sudo",
35 args,
36 },
37 risk_level: RiskLevel::High,
38 reboot_required: false,
39 rollback_available: false,
40 }
41}
42
43pub fn set_dns_servers(interface: &str, servers: &[&str]) -> ActionSpec {
44 let args = std::iter::once("resolvectl")
45 .chain(std::iter::once("dns"))
46 .chain(std::iter::once(interface))
47 .chain(servers.iter().copied());
48
49 ActionSpec {
50 action_name: "SetDnsServers",
51 mechanism: command_mechanism("sudo", args),
52 risk_level: RiskLevel::High,
53 reboot_required: false,
54 rollback_available: false,
55 }
56}
57
58pub fn configure_firewall(zone: &str, service: &str, enabled: bool) -> ActionSpec {
64 let verb = if enabled {
65 "add-service"
66 } else {
67 "remove-service"
68 };
69 ActionSpec {
70 action_name: "ConfigureFirewall",
71 mechanism: command_mechanism(
72 "sudo",
73 [
74 "/usr/lib/sysknife/action-steps",
75 "firewall",
76 zone,
77 service,
78 verb,
79 ],
80 ),
81 risk_level: RiskLevel::High,
82 reboot_required: false,
83 rollback_available: false,
84 }
85}
86
87pub fn get_firewall_state() -> ActionSpec {
88 ActionSpec {
92 action_name: "GetFirewallState",
93 mechanism: command_mechanism("firewall-cmd", ["--list-all"]),
94 risk_level: RiskLevel::Low,
95 reboot_required: false,
96 rollback_available: false,
97 }
98}
99
100pub fn get_network_status() -> ActionSpec {
101 ActionSpec {
102 action_name: "GetNetworkStatus",
103 mechanism: command_mechanism("ip", ["-brief", "addr"]),
104 risk_level: RiskLevel::Low,
105 reboot_required: false,
106 rollback_available: false,
107 }
108}
109
110pub fn get_nftables_ruleset() -> ActionSpec {
112 ActionSpec {
113 action_name: "GetNftablesRuleset",
114 mechanism: command_mechanism("sudo", ["nft", "list", "ruleset"]),
115 risk_level: RiskLevel::Low,
116 reboot_required: false,
117 rollback_available: false,
118 }
119}
120
121pub fn get_firewall_backend_state() -> ActionSpec {
123 ActionSpec {
124 action_name: "GetFirewallBackendState",
125 mechanism: command_mechanism("/usr/lib/sysknife/firewall-state", [] as [&str; 0]),
126 risk_level: RiskLevel::Low,
127 reboot_required: false,
128 rollback_available: false,
129 }
130}
131
132pub fn get_listening_ports() -> ActionSpec {
137 ActionSpec {
138 action_name: "GetListeningPorts",
139 mechanism: command_mechanism("ss", ["-tulpnH"]),
142 risk_level: RiskLevel::Low,
143 reboot_required: false,
144 rollback_available: false,
145 }
146}
147
148#[cfg(test)]
149mod firewall_tests {
150 use super::*;
151 use crate::actions::ActionMechanism;
152
153 #[test]
154 fn firewall_queries_have_fixed_read_only_mechanisms() {
155 for (spec, program, args) in [
156 (
157 get_nftables_ruleset(),
158 "sudo",
159 vec!["nft", "list", "ruleset"],
160 ),
161 (
162 get_firewall_backend_state(),
163 "/usr/lib/sysknife/firewall-state",
164 vec![],
165 ),
166 ] {
167 assert_eq!(spec.risk_level, RiskLevel::Low);
168 assert!(!spec.reboot_required && !spec.rollback_available);
169 assert_eq!(
170 spec.mechanism,
171 ActionMechanism::Command {
172 program,
173 args: args.into_iter().map(String::from).collect(),
174 }
175 );
176 }
177 }
178}