use super::{command_mechanism, ActionSpec};
use sysknife_types::RiskLevel;
pub fn specs() -> Vec<ActionSpec> {
vec![
configure_wifi("CafeHotspot", None),
set_dns_servers("wlp1s0", &["1.1.1.1", "8.8.8.8"]),
configure_firewall("public", "ssh", true),
get_firewall_state(),
get_network_status(),
get_listening_ports(),
get_nftables_ruleset(),
get_firewall_backend_state(),
]
}
pub fn configure_wifi(ssid: &str, password: Option<&str>) -> ActionSpec {
let mut args = vec![
"nmcli".to_string(),
"device".to_string(),
"wifi".to_string(),
"connect".to_string(),
ssid.to_string(),
];
if let Some(pw) = password {
args.push("password".to_string());
args.push(pw.to_string());
}
ActionSpec {
action_name: "ConfigureWifi",
mechanism: super::ActionMechanism::Command {
program: "sudo",
args,
},
risk_level: RiskLevel::High,
reboot_required: false,
rollback_available: false,
}
}
pub fn set_dns_servers(interface: &str, servers: &[&str]) -> ActionSpec {
let args = std::iter::once("resolvectl")
.chain(std::iter::once("dns"))
.chain(std::iter::once(interface))
.chain(servers.iter().copied());
ActionSpec {
action_name: "SetDnsServers",
mechanism: command_mechanism("sudo", args),
risk_level: RiskLevel::High,
reboot_required: false,
rollback_available: false,
}
}
pub fn configure_firewall(zone: &str, service: &str, enabled: bool) -> ActionSpec {
let verb = if enabled {
"add-service"
} else {
"remove-service"
};
ActionSpec {
action_name: "ConfigureFirewall",
mechanism: command_mechanism(
"sudo",
[
"/usr/lib/sysknife/action-steps",
"firewall",
zone,
service,
verb,
],
),
risk_level: RiskLevel::High,
reboot_required: false,
rollback_available: false,
}
}
pub fn get_firewall_state() -> ActionSpec {
ActionSpec {
action_name: "GetFirewallState",
mechanism: command_mechanism("firewall-cmd", ["--list-all"]),
risk_level: RiskLevel::Low,
reboot_required: false,
rollback_available: false,
}
}
pub fn get_network_status() -> ActionSpec {
ActionSpec {
action_name: "GetNetworkStatus",
mechanism: command_mechanism("ip", ["-brief", "addr"]),
risk_level: RiskLevel::Low,
reboot_required: false,
rollback_available: false,
}
}
pub fn get_nftables_ruleset() -> ActionSpec {
ActionSpec {
action_name: "GetNftablesRuleset",
mechanism: command_mechanism("sudo", ["nft", "list", "ruleset"]),
risk_level: RiskLevel::Low,
reboot_required: false,
rollback_available: false,
}
}
pub fn get_firewall_backend_state() -> ActionSpec {
ActionSpec {
action_name: "GetFirewallBackendState",
mechanism: command_mechanism("/usr/lib/sysknife/firewall-state", [] as [&str; 0]),
risk_level: RiskLevel::Low,
reboot_required: false,
rollback_available: false,
}
}
pub fn get_listening_ports() -> ActionSpec {
ActionSpec {
action_name: "GetListeningPorts",
mechanism: command_mechanism("ss", ["-tulpnH"]),
risk_level: RiskLevel::Low,
reboot_required: false,
rollback_available: false,
}
}
#[cfg(test)]
mod firewall_tests {
use super::*;
use crate::actions::ActionMechanism;
#[test]
fn firewall_queries_have_fixed_read_only_mechanisms() {
for (spec, program, args) in [
(
get_nftables_ruleset(),
"sudo",
vec!["nft", "list", "ruleset"],
),
(
get_firewall_backend_state(),
"/usr/lib/sysknife/firewall-state",
vec![],
),
] {
assert_eq!(spec.risk_level, RiskLevel::Low);
assert!(!spec.reboot_required && !spec.rollback_available);
assert_eq!(
spec.mechanism,
ActionMechanism::Command {
program,
args: args.into_iter().map(String::from).collect(),
}
);
}
}
}