Skip to content

WireGuard VPN

Deploy a WireGuard VPN server using the wireguard module.

Overview

This example demonstrates:

  • Using fscm.modules.wireguard for VPN configuration
  • Key generation workflow
  • Multi-peer configuration
  • iptables integration for VPN routing

Architecture

                        Internet
                    ┌───────┴───────┐
                    │  VPN Server   │
                    │ 203.0.113.1   │
                    │  (wg0)        │
                    │ 10.100.0.1/24 │
                    └───────┬───────┘
            ┌───────────────┼───────────────┐
            │               │               │
      ┌─────┴─────┐   ┌─────┴─────┐   ┌─────┴─────┐
      │  Laptop   │   │   Phone   │   │  Tablet   │
      │10.100.0.2 │   │10.100.0.3 │   │10.100.0.4 │
      └───────────┘   └───────────┘   └───────────┘

Files

examples/wireguard_vpn/
├── deploy.py
└── README.md

Configuration

# Server configuration
VPN_INTERFACE = "wg0"
VPN_PORT = 51820
VPN_NETWORK = "10.100.0.0/24"
VPN_SERVER_IP = "10.100.0.1"
PUBLIC_ENDPOINT = "vpn.example.com"  # Or public IP

# Peer definitions
PEERS = [
    {"name": "laptop", "ip": "10.100.0.2"},
    {"name": "phone", "ip": "10.100.0.3"},
    {"name": "tablet", "ip": "10.100.0.4"},
]

Usage

# Preview changes
python examples/wireguard_vpn/deploy.py --dry-run

# Deploy
sudo python examples/wireguard_vpn/deploy.py

# Client configs are saved to /etc/wireguard/clients/

What Gets Deployed

1. WireGuard Installation

def install_wireguard():
    s.pkgs_install("wireguard", "wireguard-tools", sudo=True)

2. Key Generation

def generate_keypair():
    """Generate a WireGuard keypair."""
    private_key = getstdout("wg genkey").strip()
    public_key = getstdout(f"echo '{private_key}' | wg pubkey").strip()
    return private_key, public_key

3. Server Configuration

/etc/wireguard/wg0.conf:

[Interface]
Address = 10.100.0.1/24
ListenPort = 51820
PrivateKey = <server_private_key>

PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
# laptop
PublicKey = <laptop_public_key>
AllowedIPs = 10.100.0.2/32

[Peer]
# phone
PublicKey = <phone_public_key>
AllowedIPs = 10.100.0.3/32

[Peer]
# tablet
PublicKey = <tablet_public_key>
AllowedIPs = 10.100.0.4/32

4. Client Configurations

/etc/wireguard/clients/laptop.conf:

[Interface]
PrivateKey = <laptop_private_key>
Address = 10.100.0.2/32
DNS = 1.1.1.1

[Peer]
PublicKey = <server_public_key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

5. IP Forwarding

def enable_ip_forwarding():
    lineinfile(
        "/etc/sysctl.conf",
        line="net.ipv4.ip_forward=1",
        regexp=r"^#?net\.ipv4\.ip_forward"
    )
    run("sysctl -p", sudo=True)

6. Firewall Rules

def configure_firewall():
    # Allow WireGuard port
    run(f"ufw allow {VPN_PORT}/udp", sudo=True)

    # Or with iptables
    run(f"iptables -A INPUT -p udp --dport {VPN_PORT} -j ACCEPT", sudo=True)

Key Code Sections

Using wireguard Module

from fscm.modules.wireguard import Server, Peer, server

def setup_vpn():
    # Generate server keys
    srv_private, srv_public = generate_keypair()

    # Create server config
    srv = Server(
        address=f"{VPN_SERVER_IP}/24",
        listen_port=VPN_PORT,
        private_key=srv_private,
        public_key=srv_public,
        post_up="iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE",
        post_down="iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE"
    )

    # Create peers
    peers = []
    for peer_def in PEERS:
        peer_private, peer_public = generate_keypair()
        peers.append(Peer(
            name=peer_def["name"],
            address=f"{peer_def['ip']}/32",
            private_key=peer_private,
            public_key=peer_public,
            allowed_ips="0.0.0.0/0"
        ))

    # Generate all configs
    server(srv, peers, endpoint=f"{PUBLIC_ENDPOINT}:{VPN_PORT}")

Enable and Start

def start_wireguard():
    run("systemctl enable wg-quick@wg0", sudo=True)
    run("systemctl start wg-quick@wg0", sudo=True)

Client Setup

Linux

# Copy client config
scp user@vpn.example.com:/etc/wireguard/clients/laptop.conf /etc/wireguard/

# Start VPN
sudo wg-quick up laptop

macOS / iOS / Android

  1. Install WireGuard app
  2. Import configuration file or scan QR code

Generate QR Code

# On server
qrencode -t ansiutf8 < /etc/wireguard/clients/phone.conf

Adding New Peers

def add_peer(name: str, ip: str):
    """Add a new peer to existing VPN."""
    # Generate keys
    private, public = generate_keypair()

    # Add to server config
    peer_section = f"""
[Peer]
# {name}
PublicKey = {public}
AllowedIPs = {ip}/32
"""
    run(f"echo '{peer_section}' >> /etc/wireguard/wg0.conf", sudo=True)

    # Reload without dropping connections
    run("wg syncconf wg0 <(wg-quick strip wg0)", sudo=True)

    # Generate client config
    generate_client_config(name, ip, private)

Troubleshooting

Check Interface Status

sudo wg show

View Connection Logs

journalctl -u wg-quick@wg0 -f

Test Connectivity

# From client
ping 10.100.0.1  # VPN server

# Check external IP
curl ifconfig.me

Common Issues

Issue Solution
Can't connect Check firewall allows UDP 51820
No internet through VPN Check NAT/masquerade rules
Handshake timeout Verify endpoint is reachable
DNS not working Check DNS in client config