Release v1.1.0
This commit is contained in:
1 parent
34227efd2b
commit
edc710cbd2
46 files changed
+5324
-190
No files matched your search
@@ -0,0 +1,274 @@
|
||||
//! 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());
|
||||
}
|
||||
@@ -3,7 +3,9 @@
|
||||
use chrono::Utc;
|
||||
use ipnet::IpNet;
|
||||
use nx9_wg_core::crypto::{generate_keypair, generate_preshared_key};
|
||||
use nx9_wg_core::types::wireguard::{Interface, Peer, PeerProfile, PeerState, PeerType};
|
||||
use nx9_wg_core::types::wireguard::{
|
||||
Interface, InterfaceRole, Peer, PeerProfile, PeerState, PeerType,
|
||||
};
|
||||
use nx9_wireguard::{
|
||||
ClientConfigBuilder, SimulatedWireGuardEngine, WireGuardEngine, generate_qr_ascii,
|
||||
generate_qr_data_url, generate_qr_png_bytes, generate_qr_svg,
|
||||
@@ -23,9 +25,10 @@ async fn test_wireguard_engine_lifecycle_and_telemetry() {
|
||||
let iface = Interface {
|
||||
id: Uuid::new_v4(),
|
||||
name: "wg0".to_string(),
|
||||
role: InterfaceRole::Overlay,
|
||||
private_key: srv_priv,
|
||||
public_key: srv_pub.clone(),
|
||||
listen_port: 51820,
|
||||
listen_port: Some(51820),
|
||||
address_v4: IpNet::from_str("10.0.0.1/24").unwrap(),
|
||||
address_v6: None,
|
||||
mtu: Some(1420),
|
||||
@@ -138,9 +141,10 @@ fn test_client_config_and_qr_codes() {
|
||||
let iface = Interface {
|
||||
id: Uuid::new_v4(),
|
||||
name: "wg0".to_string(),
|
||||
role: InterfaceRole::Overlay,
|
||||
private_key: srv_priv,
|
||||
public_key: srv_pub,
|
||||
listen_port: 51820,
|
||||
listen_port: Some(51820),
|
||||
address_v4: IpNet::from_str("10.0.0.1/24").unwrap(),
|
||||
address_v6: None,
|
||||
mtu: Some(1420),
|
||||
|
||||
Reference in new issue
Block a user