275 lines
8.8 KiB
Rust
275 lines
8.8 KiB
Rust
//! Comprehensive test suite for third-party WireGuard Upstream .conf parsing and validation.
|
|
|
|
use nx9_wg_core::crypto::generate_keypair;
|
|
use nx9_wg_core::types::wireguard::InterfaceRole;
|
|
use nx9_wireguard::UpstreamConfigParser;
|
|
use uuid::Uuid;
|
|
|
|
#[test]
|
|
fn test_valid_proton_style_configuration() {
|
|
let (priv_k, pub_k) = generate_keypair();
|
|
let (_, peer_pub_k) = generate_keypair();
|
|
|
|
let conf = format!(
|
|
r#"
|
|
# ProtonVPN WireGuard Configuration
|
|
[Interface]
|
|
PrivateKey = {}
|
|
Address = 10.2.0.2/32
|
|
DNS = 10.2.0.1
|
|
MTU = 1420
|
|
|
|
[Peer]
|
|
# Server Node
|
|
PublicKey = {}
|
|
AllowedIPs = 0.0.0.0/0, ::/0
|
|
Endpoint = 37.19.199.155:51820
|
|
PersistentKeepalive = 25
|
|
"#,
|
|
priv_k.as_str(),
|
|
peer_pub_k.as_str()
|
|
);
|
|
|
|
let parsed = UpstreamConfigParser::parse(&conf, "proton0").expect("parse proton config");
|
|
assert_eq!(parsed.interface_name, "proton0");
|
|
assert_eq!(parsed.public_key.as_str(), pub_k.as_str());
|
|
assert_eq!(parsed.address_v4.to_string(), "10.2.0.2/32");
|
|
assert_eq!(parsed.address_v6, None);
|
|
assert_eq!(parsed.dns, Some("10.2.0.1".to_string()));
|
|
assert_eq!(parsed.mtu, Some(1420));
|
|
assert_eq!(parsed.listen_port, None);
|
|
|
|
assert_eq!(parsed.peer.name, "proton0-provider");
|
|
assert_eq!(parsed.peer.public_key.as_str(), peer_pub_k.as_str());
|
|
assert_eq!(parsed.peer.endpoint, "37.19.199.155:51820");
|
|
assert_eq!(parsed.peer.allowed_ips, "0.0.0.0/0, ::/0");
|
|
assert_eq!(parsed.peer.persistent_keepalive, Some(25));
|
|
assert_eq!(parsed.peer.preshared_key, None);
|
|
|
|
let iface_id = Uuid::new_v4();
|
|
let peer_id = Uuid::new_v4();
|
|
let (iface, peer) = parsed.into_desired_state(iface_id, peer_id);
|
|
|
|
assert_eq!(iface.id, iface_id);
|
|
assert_eq!(iface.name, "proton0");
|
|
assert_eq!(iface.role, InterfaceRole::Upstream);
|
|
assert_eq!(iface.listen_port, None);
|
|
assert!(iface.enabled);
|
|
|
|
assert_eq!(peer.id, peer_id);
|
|
assert_eq!(peer.interface_id, iface_id);
|
|
assert_eq!(peer.name, "proton0-provider");
|
|
assert_eq!(peer.allowed_ips, "0.0.0.0/0, ::/0");
|
|
assert_eq!(
|
|
peer.server_wireguard_allowed_ips_for_role(InterfaceRole::Upstream),
|
|
"0.0.0.0/0, ::/0"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_omitted_listen_port_remains_unspecified() {
|
|
let (priv_k, _) = generate_keypair();
|
|
let (_, peer_pub_k) = generate_keypair();
|
|
|
|
let conf = format!(
|
|
"[Interface]\nPrivateKey = {}\nAddress = 10.2.0.2/32\n[Peer]\nPublicKey = {}\nAllowedIPs = 0.0.0.0/0, ::/0\nEndpoint = 37.19.199.155:51820\n",
|
|
priv_k.as_str(),
|
|
peer_pub_k.as_str()
|
|
);
|
|
|
|
let parsed = UpstreamConfigParser::parse(&conf, "proton0").expect("parse config");
|
|
assert_eq!(parsed.listen_port, None);
|
|
assert_eq!(parsed.peer.endpoint, "37.19.199.155:51820");
|
|
}
|
|
|
|
#[test]
|
|
fn test_explicit_listen_port_is_preserved() {
|
|
let (priv_k, _) = generate_keypair();
|
|
let (_, peer_pub_k) = generate_keypair();
|
|
|
|
let conf = format!(
|
|
"[Interface]\nPrivateKey = {}\nAddress = 10.2.0.2/32\nListenPort = 45000\n[Peer]\nPublicKey = {}\nAllowedIPs = 0.0.0.0/0, ::/0\nEndpoint = 37.19.199.155:51820\n",
|
|
priv_k.as_str(),
|
|
peer_pub_k.as_str()
|
|
);
|
|
|
|
let parsed = UpstreamConfigParser::parse(&conf, "proton0").expect("parse config");
|
|
assert_eq!(parsed.listen_port, Some(45000));
|
|
}
|
|
|
|
#[test]
|
|
fn test_endpoint_port_is_not_local_listen_port() {
|
|
let (priv_k, _) = generate_keypair();
|
|
let (_, peer_pub_k) = generate_keypair();
|
|
|
|
let conf = format!(
|
|
"[Interface]\nPrivateKey = {}\nAddress = 10.2.0.2/32\n[Peer]\nPublicKey = {}\nAllowedIPs = 0.0.0.0/0, ::/0\nEndpoint = 37.19.199.155:51820\n",
|
|
priv_k.as_str(),
|
|
peer_pub_k.as_str()
|
|
);
|
|
|
|
let parsed = UpstreamConfigParser::parse(&conf, "proton0").expect("parse config");
|
|
assert_eq!(parsed.listen_port, None);
|
|
assert!(parsed.peer.endpoint.ends_with(":51820"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_dual_stack_address_and_preshared_key() {
|
|
let (priv_k, _) = generate_keypair();
|
|
let (_, peer_pub_k) = generate_keypair();
|
|
let (psk, _) = generate_keypair();
|
|
|
|
let conf = format!(
|
|
r#"
|
|
[Interface]
|
|
PrivateKey = {}
|
|
Address = 10.2.0.2/32, fd00::2/64
|
|
DNS = 10.2.0.1, 1.1.1.1
|
|
|
|
[Peer]
|
|
PublicKey = {}
|
|
PresharedKey = {}
|
|
AllowedIPs = 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
|
|
Endpoint = vpn.example.com:51820
|
|
"#,
|
|
priv_k.as_str(),
|
|
peer_pub_k.as_str(),
|
|
psk.as_str()
|
|
);
|
|
|
|
let parsed = UpstreamConfigParser::parse(&conf, "vpn0").expect("parse dual stack");
|
|
assert_eq!(parsed.address_v4.to_string(), "10.2.0.2/32");
|
|
assert_eq!(
|
|
parsed.address_v6.map(|ip| ip.to_string()),
|
|
Some("fd00::2/64".to_string())
|
|
);
|
|
assert_eq!(parsed.dns, Some("10.2.0.1, 1.1.1.1".to_string()));
|
|
assert!(parsed.peer.preshared_key.is_some());
|
|
assert_eq!(parsed.peer.preshared_key.unwrap().as_str(), psk.as_str());
|
|
}
|
|
|
|
#[test]
|
|
fn test_reject_wg0_interface_name() {
|
|
let (priv_k, _) = generate_keypair();
|
|
let (_, peer_pub_k) = generate_keypair();
|
|
let conf = format!(
|
|
"[Interface]\nPrivateKey = {}\nAddress = 10.2.0.2/32\n[Peer]\nPublicKey = {}\nAllowedIPs = 0.0.0.0/0\nEndpoint = 1.2.3.4:51820\n",
|
|
priv_k.as_str(),
|
|
peer_pub_k.as_str()
|
|
);
|
|
|
|
let err = UpstreamConfigParser::parse(&conf, "wg0").unwrap_err();
|
|
assert!(err.to_string().contains("reserved name 'wg0'"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_cardinality_rejections() {
|
|
let (priv_k, _) = generate_keypair();
|
|
let (_, peer_pub_k) = generate_keypair();
|
|
|
|
// 0 peers
|
|
let no_peers = format!(
|
|
"[Interface]\nPrivateKey = {}\nAddress = 10.2.0.2/32\n",
|
|
priv_k.as_str()
|
|
);
|
|
assert!(UpstreamConfigParser::parse(&no_peers, "proton0").is_err());
|
|
|
|
// 2 peers
|
|
let multi_peers = format!(
|
|
r#"
|
|
[Interface]
|
|
PrivateKey = {}
|
|
Address = 10.2.0.2/32
|
|
|
|
[Peer]
|
|
PublicKey = {}
|
|
AllowedIPs = 0.0.0.0/0
|
|
Endpoint = 1.2.3.4:51820
|
|
|
|
[Peer]
|
|
PublicKey = {}
|
|
AllowedIPs = 0.0.0.0/0
|
|
Endpoint = 5.6.7.8:51820
|
|
"#,
|
|
priv_k.as_str(),
|
|
peer_pub_k.as_str(),
|
|
peer_pub_k.as_str()
|
|
);
|
|
let err = UpstreamConfigParser::parse(&multi_peers, "proton0").unwrap_err();
|
|
assert!(err.to_string().contains("exactly one [Peer] section"));
|
|
|
|
// Missing [Interface]
|
|
let no_iface = format!(
|
|
"[Peer]\nPublicKey = {}\nAllowedIPs = 0.0.0.0/0\nEndpoint = 1.2.3.4:51820\n",
|
|
peer_pub_k.as_str()
|
|
);
|
|
assert!(UpstreamConfigParser::parse(&no_iface, "proton0").is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_missing_and_malformed_fields_rejections() {
|
|
let (_, peer_pub_k) = generate_keypair();
|
|
|
|
// Missing PrivateKey
|
|
let no_priv = format!(
|
|
"[Interface]\nAddress = 10.2.0.2/32\n[Peer]\nPublicKey = {}\nAllowedIPs = 0.0.0.0/0\nEndpoint = 1.2.3.4:51820\n",
|
|
peer_pub_k.as_str()
|
|
);
|
|
assert!(UpstreamConfigParser::parse(&no_priv, "proton0").is_err());
|
|
|
|
// Malformed PrivateKey
|
|
let bad_priv = format!(
|
|
"[Interface]\nPrivateKey = not-a-key\nAddress = 10.2.0.2/32\n[Peer]\nPublicKey = {}\nAllowedIPs = 0.0.0.0/0\nEndpoint = 1.2.3.4:51820\n",
|
|
peer_pub_k.as_str()
|
|
);
|
|
assert!(UpstreamConfigParser::parse(&bad_priv, "proton0").is_err());
|
|
|
|
// Missing Address
|
|
let (priv_k, _) = generate_keypair();
|
|
let no_addr = format!(
|
|
"[Interface]\nPrivateKey = {}\n[Peer]\nPublicKey = {}\nAllowedIPs = 0.0.0.0/0\nEndpoint = 1.2.3.4:51820\n",
|
|
priv_k.as_str(),
|
|
peer_pub_k.as_str()
|
|
);
|
|
assert!(UpstreamConfigParser::parse(&no_addr, "proton0").is_err());
|
|
|
|
// Malformed Address
|
|
let bad_addr = format!(
|
|
"[Interface]\nPrivateKey = {}\nAddress = 999.999.999.999/99\n[Peer]\nPublicKey = {}\nAllowedIPs = 0.0.0.0/0\nEndpoint = 1.2.3.4:51820\n",
|
|
priv_k.as_str(),
|
|
peer_pub_k.as_str()
|
|
);
|
|
assert!(UpstreamConfigParser::parse(&bad_addr, "proton0").is_err());
|
|
|
|
// Missing PublicKey
|
|
let no_pub = format!(
|
|
"[Interface]\nPrivateKey = {}\nAddress = 10.2.0.2/32\n[Peer]\nAllowedIPs = 0.0.0.0/0\nEndpoint = 1.2.3.4:51820\n",
|
|
priv_k.as_str()
|
|
);
|
|
assert!(UpstreamConfigParser::parse(&no_pub, "proton0").is_err());
|
|
|
|
// Missing Endpoint
|
|
let no_ep = format!(
|
|
"[Interface]\nPrivateKey = {}\nAddress = 10.2.0.2/32\n[Peer]\nPublicKey = {}\nAllowedIPs = 0.0.0.0/0\n",
|
|
priv_k.as_str(),
|
|
peer_pub_k.as_str()
|
|
);
|
|
assert!(UpstreamConfigParser::parse(&no_ep, "proton0").is_err());
|
|
|
|
// Missing AllowedIPs
|
|
let no_aips = format!(
|
|
"[Interface]\nPrivateKey = {}\nAddress = 10.2.0.2/32\n[Peer]\nPublicKey = {}\nEndpoint = 1.2.3.4:51820\n",
|
|
priv_k.as_str(),
|
|
peer_pub_k.as_str()
|
|
);
|
|
assert!(UpstreamConfigParser::parse(&no_aips, "proton0").is_err());
|
|
|
|
// Unknown section
|
|
let unknown_sec = format!(
|
|
"[Interface]\nPrivateKey = {}\nAddress = 10.2.0.2/32\n[Unknown]\nKey = Val\n[Peer]\nPublicKey = {}\nAllowedIPs = 0.0.0.0/0\nEndpoint = 1.2.3.4:51820\n",
|
|
priv_k.as_str(),
|
|
peer_pub_k.as_str()
|
|
);
|
|
assert!(UpstreamConfigParser::parse(&unknown_sec, "proton0").is_err());
|
|
}
|