cli: avoid data-dir initialization for version; create db parent dirs; redact generated passwords in CLI output
- Prevent 'nx9-wg version' from creating data directories by avoiding database initialization. - Create parent directories when an explicit --database path is provided. - Redact printed generated administrator passwords; announce file path or redact instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
commit
2ac6c81dfe
140 files changed
+31342
No files matched your search
@@ -0,0 +1,288 @@
|
||||
//! Tests for Network, Route, and Firewall Rule repositories.
|
||||
|
||||
use chrono::Utc;
|
||||
use ipnet::IpNet;
|
||||
use nx9_wg_core::crypto::generate_keypair;
|
||||
use nx9_wg_core::types::firewall::{
|
||||
FirewallAction, FirewallDirection, FirewallProtocol, FirewallRule,
|
||||
};
|
||||
use nx9_wg_core::types::network::{Network, Route};
|
||||
use nx9_wg_core::types::wireguard::Interface;
|
||||
use nx9_wg_db::Store;
|
||||
use std::net::IpAddr;
|
||||
use std::str::FromStr;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_network_and_route_crud() {
|
||||
let store = Store::connect_in_memory().await.expect("connect");
|
||||
store.migrate().await.expect("migrate");
|
||||
|
||||
let now = Utc::now().naive_utc();
|
||||
let net_id = Uuid::new_v4();
|
||||
|
||||
let net = Network {
|
||||
id: net_id,
|
||||
name: "Home Lab".to_string(),
|
||||
cidr: IpNet::from_str("192.168.10.0/24").expect("cidr"),
|
||||
enabled: true,
|
||||
description: Some("Internal lab subnet".to_string()),
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
};
|
||||
|
||||
store.create_network(&net).await.expect("create_network");
|
||||
|
||||
let fetched_net = store
|
||||
.get_network(net_id)
|
||||
.await
|
||||
.expect("get")
|
||||
.expect("found");
|
||||
assert_eq!(fetched_net.name, "Home Lab");
|
||||
assert_eq!(fetched_net.cidr.to_string(), "192.168.10.0/24");
|
||||
assert!(fetched_net.enabled);
|
||||
|
||||
// Test routes
|
||||
let route_id = Uuid::new_v4();
|
||||
let route = Route {
|
||||
id: route_id,
|
||||
network_id: Some(net_id),
|
||||
interface_id: None,
|
||||
destination: IpNet::from_str("192.168.10.0/24").expect("dest cidr"),
|
||||
gateway: Some(IpAddr::from_str("10.0.0.1").expect("gateway")),
|
||||
interface_name: None,
|
||||
metric: Some(100),
|
||||
enabled: true,
|
||||
description: Some("Lab route via wg gateway".to_string()),
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
};
|
||||
|
||||
store.create_route(&route).await.expect("create_route");
|
||||
|
||||
let fetched_route = store
|
||||
.get_route(route_id)
|
||||
.await
|
||||
.expect("get")
|
||||
.expect("route found");
|
||||
assert_eq!(fetched_route.network_id, Some(net_id));
|
||||
assert_eq!(
|
||||
fetched_route.gateway,
|
||||
Some(IpAddr::from_str("10.0.0.1").unwrap())
|
||||
);
|
||||
assert_eq!(fetched_route.metric, Some(100));
|
||||
|
||||
// Enable/disable route
|
||||
store
|
||||
.set_route_enabled(route_id, false)
|
||||
.await
|
||||
.expect("disable");
|
||||
let disabled_route = store
|
||||
.get_route(route_id)
|
||||
.await
|
||||
.expect("get")
|
||||
.expect("route");
|
||||
assert!(!disabled_route.enabled);
|
||||
|
||||
// List routes for network
|
||||
let net_routes = store
|
||||
.list_routes_for_network(net_id)
|
||||
.await
|
||||
.expect("list net routes");
|
||||
assert_eq!(net_routes.len(), 1);
|
||||
|
||||
// Deleting network sets route's network_id to NULL (ON DELETE SET NULL)
|
||||
store.delete_network(net_id).await.expect("delete network");
|
||||
let route_after_net_delete = store
|
||||
.get_route(route_id)
|
||||
.await
|
||||
.expect("get")
|
||||
.expect("route");
|
||||
assert!(
|
||||
route_after_net_delete.network_id.is_none(),
|
||||
"network_id must be SET NULL when network is deleted"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_firewall_rule_crud_and_priority_ordering() {
|
||||
let store = Store::connect_in_memory().await.expect("connect");
|
||||
store.migrate().await.expect("migrate");
|
||||
|
||||
let now = Utc::now().naive_utc();
|
||||
let iface_id = Uuid::new_v4();
|
||||
let (priv_k, pub_k) = generate_keypair();
|
||||
|
||||
let iface = Interface {
|
||||
id: iface_id,
|
||||
name: "wg0".to_string(),
|
||||
private_key: priv_k,
|
||||
public_key: pub_k,
|
||||
listen_port: 51820,
|
||||
address_v4: IpNet::from_str("10.0.0.1/24").unwrap(),
|
||||
address_v6: None,
|
||||
mtu: None,
|
||||
dns: None,
|
||||
enabled: true,
|
||||
pre_up: None,
|
||||
post_up: None,
|
||||
pre_down: None,
|
||||
post_down: None,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
};
|
||||
store
|
||||
.create_interface(&iface)
|
||||
.await
|
||||
.expect("create interface");
|
||||
|
||||
let rule1_id = Uuid::new_v4();
|
||||
let rule1 = FirewallRule {
|
||||
id: rule1_id,
|
||||
name: "Allow SSH".to_string(),
|
||||
interface_id: Some(iface_id),
|
||||
peer_id: None,
|
||||
direction: FirewallDirection::In,
|
||||
action: FirewallAction::Accept,
|
||||
protocol: FirewallProtocol::Tcp,
|
||||
source: None,
|
||||
destination: None,
|
||||
source_port: None,
|
||||
destination_port: Some(22),
|
||||
port_range: None,
|
||||
priority: 50,
|
||||
enabled: true,
|
||||
description: Some("SSH access".to_string()),
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
};
|
||||
|
||||
let rule2_id = Uuid::new_v4();
|
||||
let rule2 = FirewallRule {
|
||||
id: rule2_id,
|
||||
name: "Drop All Other".to_string(),
|
||||
interface_id: Some(iface_id),
|
||||
peer_id: None,
|
||||
direction: FirewallDirection::In,
|
||||
action: FirewallAction::Drop,
|
||||
protocol: FirewallProtocol::Any,
|
||||
source: None,
|
||||
destination: None,
|
||||
source_port: None,
|
||||
destination_port: None,
|
||||
port_range: None,
|
||||
priority: 100,
|
||||
enabled: true,
|
||||
description: Some("Default drop".to_string()),
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
};
|
||||
|
||||
store
|
||||
.create_firewall_rule(&rule2)
|
||||
.await
|
||||
.expect("create rule2");
|
||||
store
|
||||
.create_firewall_rule(&rule1)
|
||||
.await
|
||||
.expect("create rule1");
|
||||
|
||||
// List rules should order by priority ASC (rule1 priority 50 comes before rule2 priority 100)
|
||||
let rules = store.list_firewall_rules().await.expect("list rules");
|
||||
assert_eq!(rules.len(), 2);
|
||||
assert_eq!(rules[0].id, rule1_id);
|
||||
assert_eq!(rules[0].priority, 50);
|
||||
assert_eq!(rules[1].id, rule2_id);
|
||||
assert_eq!(rules[1].priority, 100);
|
||||
|
||||
// List rules for interface
|
||||
let iface_rules = store
|
||||
.list_firewall_rules_for_interface(iface_id)
|
||||
.await
|
||||
.expect("list iface rules");
|
||||
assert_eq!(iface_rules.len(), 2);
|
||||
|
||||
// Enable/disable rule
|
||||
store
|
||||
.set_firewall_rule_enabled(rule1_id, false)
|
||||
.await
|
||||
.expect("disable");
|
||||
let disabled = store
|
||||
.get_firewall_rule(rule1_id)
|
||||
.await
|
||||
.expect("get")
|
||||
.expect("rule");
|
||||
assert!(!disabled.enabled);
|
||||
|
||||
// Delete rule
|
||||
store.delete_firewall_rule(rule1_id).await.expect("delete");
|
||||
assert!(
|
||||
store
|
||||
.get_firewall_rule(rule1_id)
|
||||
.await
|
||||
.expect("get")
|
||||
.is_none()
|
||||
);
|
||||
|
||||
// Peer-specific rule with port range
|
||||
let peer_id = Uuid::new_v4();
|
||||
let peer = nx9_wg_core::types::wireguard::Peer {
|
||||
id: peer_id,
|
||||
interface_id: iface_id,
|
||||
name: "test-peer-fw".to_string(),
|
||||
peer_type: nx9_wg_core::types::wireguard::PeerType::RoadWarrior,
|
||||
state: nx9_wg_core::types::wireguard::PeerState::Active,
|
||||
public_key: nx9_wg_core::types::wireguard::WireGuardPublicKey::new(
|
||||
"testpubkey12345678901234567890123456789012=".to_string(),
|
||||
),
|
||||
private_key: None,
|
||||
preshared_key: None,
|
||||
endpoint: None,
|
||||
allowed_ips: "10.0.0.2/32".to_string(),
|
||||
server_allowed_ips: None,
|
||||
address_v4: Some("10.0.0.2/32".parse().unwrap()),
|
||||
address_v6: None,
|
||||
dns: None,
|
||||
mtu: None,
|
||||
persistent_keepalive: None,
|
||||
profile: nx9_wg_core::types::wireguard::PeerProfile::FullTunnel,
|
||||
expires_at: None,
|
||||
last_handshake_at: None,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
};
|
||||
store.create_peer(&peer).await.expect("create peer");
|
||||
|
||||
let peer_rule_id = Uuid::new_v4();
|
||||
let peer_rule = FirewallRule {
|
||||
id: peer_rule_id,
|
||||
name: "Peer Port Range Rule".to_string(),
|
||||
interface_id: Some(iface_id),
|
||||
peer_id: Some(peer_id),
|
||||
direction: FirewallDirection::In,
|
||||
action: FirewallAction::Accept,
|
||||
protocol: FirewallProtocol::TcpUdp,
|
||||
source: None,
|
||||
destination: None,
|
||||
source_port: None,
|
||||
destination_port: None,
|
||||
port_range: Some("8000-8100".to_string()),
|
||||
priority: 25,
|
||||
enabled: true,
|
||||
description: Some("Custom peer range".to_string()),
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
};
|
||||
store
|
||||
.create_firewall_rule(&peer_rule)
|
||||
.await
|
||||
.expect("create peer rule");
|
||||
|
||||
let peer_rules = store
|
||||
.list_firewall_rules_for_peer(peer_id)
|
||||
.await
|
||||
.expect("list peer rules");
|
||||
assert_eq!(peer_rules.len(), 1);
|
||||
assert_eq!(peer_rules[0].port_range.as_deref(), Some("8000-8100"));
|
||||
assert_eq!(peer_rules[0].protocol, FirewallProtocol::TcpUdp);
|
||||
}
|
||||
Reference in new issue
Block a user