WireGuard Setup

WireGuard is a modern, high-performance VPN protocol. nmrs provides full WireGuard support through NetworkManager's native WireGuard integration — no additional plugins required.

Prerequisites

  • NetworkManager 1.16+ (WireGuard support was added in 1.16)
  • The wireguard kernel module must be loaded (built into Linux 5.6+, available as a module on older kernels)
  • A WireGuard configuration from your VPN provider or server administrator

Quick Start

use nmrs::{NetworkManager, VpnCredentials, VpnType, WireGuardPeer};

#[tokio::main]
async fn main() -> nmrs::Result<()> {
    let nm = NetworkManager::new().await?;

    let peer = WireGuardPeer::new(
        "SERVER_PUBLIC_KEY_BASE64",
        "vpn.example.com:51820",
        vec!["0.0.0.0/0".into()],
    ).with_persistent_keepalive(25);

    let creds = VpnCredentials::new(
        VpnType::WireGuard,
        "MyVPN",
        "vpn.example.com:51820",
        "CLIENT_PRIVATE_KEY_BASE64",
        "10.0.0.2/24",
        vec![peer],
    ).with_dns(vec!["1.1.1.1".into()]);

    nm.connect_vpn(creds).await?;

    println!("VPN connected!");
    Ok(())
}

Understanding WireGuard Concepts

ConceptDescription
Private KeyYour client's secret key (base64, 44 chars). Never share this.
Public KeyThe server's public key (base64, 44 chars). Provided by server admin.
EndpointServer address in host:port format (e.g., vpn.example.com:51820)
AddressYour client's IP within the VPN tunnel (e.g., 10.0.0.2/24)
Allowed IPsIP ranges to route through the tunnel. 0.0.0.0/0 routes everything.
DNSDNS servers to use while the VPN is active
Persistent KeepaliveSeconds between keepalive packets (helps with NAT traversal)

VpnCredentials Fields

FieldRequiredDescription
vpn_typeYesMust be VpnType::WireGuard
nameYesConnection profile name
gatewayYesServer endpoint (host:port)
private_keyYesClient private key (base64)
addressYesClient IP with CIDR (10.0.0.2/24)
peersYesAt least one WireGuardPeer
dnsNoDNS servers for the VPN
mtuNoMTU size (typical: 1420)
uuidNoCustom UUID (auto-generated if omitted)

Building Credentials

Direct Constructor

#![allow(unused)]
fn main() {
use nmrs::{VpnCredentials, VpnType, WireGuardPeer};

let peer = WireGuardPeer::new(
    "HIgo9xNzJMWLKAShlKl6/bUT1VI9Q0SDBXGtLXkPFXc=",
    "vpn.example.com:51820",
    vec!["0.0.0.0/0".into(), "::/0".into()],
).with_persistent_keepalive(25)
 .with_preshared_key("OPTIONAL_PSK_BASE64");

let creds = VpnCredentials::new(
    VpnType::WireGuard,
    "HomeVPN",
    "vpn.example.com:51820",
    "YBk6X3pP8KjKz7+HFWzVHNqL3qTZq8hX9VxFQJ4zVmM=",
    "10.0.0.2/24",
    vec![peer],
).with_dns(vec!["1.1.1.1".into(), "8.8.8.8".into()])
 .with_mtu(1420);
}

Builder Pattern

The builder pattern avoids positional parameter confusion:

#![allow(unused)]
fn main() {
use nmrs::{VpnCredentials, WireGuardPeer};

let peer = WireGuardPeer::new(
    "HIgo9xNzJMWLKAShlKl6/bUT1VI9Q0SDBXGtLXkPFXc=",
    "vpn.example.com:51820",
    vec!["0.0.0.0/0".into()],
).with_persistent_keepalive(25);

let creds = VpnCredentials::builder()
    .name("HomeVPN")
    .wireguard()
    .gateway("vpn.example.com:51820")
    .private_key("YBk6X3pP8KjKz7+HFWzVHNqL3qTZq8hX9VxFQJ4zVmM=")
    .address("10.0.0.2/24")
    .add_peer(peer)
    .with_dns(vec!["1.1.1.1".into()])
    .with_mtu(1420)
    .build();
}

WireGuardPeer Configuration

FieldRequiredDescription
public_keyYesPeer's WireGuard public key (base64)
gatewayYesPeer endpoint (host:port)
allowed_ipsYesIP ranges to route through this peer
preshared_keyNoAdditional shared secret for post-quantum security
persistent_keepaliveNoKeepalive interval in seconds

Multiple Peers

WireGuard supports multiple peers with different routing rules:

#![allow(unused)]
fn main() {
use nmrs::WireGuardPeer;

let full_tunnel = WireGuardPeer::new(
    "peer1_public_key",
    "vpn.example.com:51820",
    vec!["0.0.0.0/0".into()],
).with_persistent_keepalive(25);

let split_tunnel = WireGuardPeer::new(
    "peer2_public_key",
    "office.example.com:51820",
    vec!["10.0.0.0/8".into(), "192.168.0.0/16".into()],
);
}

Routing with Allowed IPs

ConfigurationEffect
["0.0.0.0/0"]Full tunnel — all traffic goes through VPN
["0.0.0.0/0", "::/0"]Full tunnel with IPv6
["10.0.0.0/8"]Split tunnel — only 10.x.x.x traffic
["192.168.1.0/24"]Split tunnel — only one subnet

Validation

nmrs validates all WireGuard parameters before sending them to NetworkManager:

  • Private/public keys: Must be valid base64, approximately 44 characters
  • Address: Must include CIDR notation (e.g., 10.0.0.2/24)
  • Gateway: Must be in host:port format with a valid port
  • Peers: At least one peer is required, each with a valid public key and non-empty allowed IPs

Invalid parameters produce specific error variants:

ErrorCause
InvalidPrivateKeyKey missing, wrong length, or invalid base64
InvalidPublicKeyPeer key invalid
InvalidAddressMissing CIDR prefix or invalid IP
InvalidGatewayMissing port or invalid format
InvalidPeersNo peers, or peer has no allowed IPs

Security Best Practices

  • Never hardcode private keys — use environment variables or a secrets manager
  • Use preshared keys when available for additional post-quantum security
  • Set persistent keepalive to 25 seconds if behind NAT
  • Use split tunneling when you only need to reach specific networks

Next Steps