> For the complete documentation index, see [llms.txt](https://martian1337.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://martian1337.gitbook.io/notes/digital-privacy/self-hosting/universal-wireguard-rdp-framework.md).

# Universal WireGuard RDP Framework

> Why Replace SSH / Tailscale for RDP?
>
> Layering TCP traffic (RDP on port 3389) over SSH or user-space overlay networks like Tailscale introduces severe TCP-over-TCP retransmission overhead. When packet loss occurs, both TCP stacks attempt retransmissions simultaneously, causing frozen sessions and input lag.
>
> This framework establishes a kernel-space WireGuard UDP tunnel, delivering direct near-LAN performance without exposing RDP to the public internet.

### Architecture & Security Features

<table data-header-hidden><thead><tr><th width="172"></th><th width="205"></th><th width="397"></th></tr></thead><tbody><tr><td><strong>Feature</strong></td><td><strong>Implementation</strong></td><td><strong>Benefit</strong></td></tr><tr><td>Post-Quantum Encryption</td><td>Per-peer symmetric Pre-Shared Keys (<code>wg genpsk</code>)</td><td>Protects session key exchanges against potential future quantum decryption threats.</td></tr><tr><td>Distro Agnostic</td><td>Auto-detects <code>apt</code>, <code>dnf</code>, <code>pacman</code>, <code>zypper</code>, <code>apk</code>, <code>xbps</code>, <code>emerge</code></td><td>Runs on any Linux distribution without modification.</td></tr><tr><td>Init System Agnostic</td><td>Auto-detects <code>systemd</code>, <code>OpenRC</code>, <code>runit</code>, and <code>SysVinit</code></td><td>Compatible with desktop, minimal, or containerized Linux OSes.</td></tr><tr><td>Zero-Downtime Hot-Reload</td><td>Live config syncing via <code>wg syncconf</code></td><td>Adding new peers never drops active RDP connections.</td></tr><tr><td>xRDP Binding Lockdown</td><td>Binds xRDP exclusively to <code>10.10.0.X:3389</code></td><td>Port 3389 is completely invisible to public internet and local LAN scanners.</td></tr><tr><td>Kernel Hardening</td><td><code>rp_filter = 1</code> and disabled ICMP redirects</td><td>Prevents IP spoofing, packet injection, and routing loop attacks.</td></tr></tbody></table>

> ⚠️ Zero-Trust File Permissions:
>
> All cryptographic keys and configuration files are explicitly locked down to `0600` or `0700` permissions owned exclusively by `root`.

### 1. Server Setup Script (`setup-wg-server.sh`)

Run this script as `root` on your Public IP Linux Server to set up the gateway interface (`wg0` on `10.10.0.1/24`).

```bash
#!/bin/bash
# Universal & Security-Hardened WireGuard Server Setup

set -e

if [ "$EUID" -ne 0 ]; then
  echo "[-] Error: Please run as root (sudo)."
  exit 1
fi

WG_IF="wg0"
WG_PORT=51820
WG_NET="10.10.0"
SERVER_IP="${WG_NET}.1/24"

# 1. Detect Default Network Interface
DEFAULT_NIC=$(ip route get 1.1.1.1 2>/dev/null | grep -oP 'dev \K\S+' | head -n1)
if [ -z "$DEFAULT_NIC" ]; then
    DEFAULT_NIC=$(ip route show default | awk '/default/ {print $5}' | head -n1)
fi

echo "--> Detected WAN Interface: ${DEFAULT_NIC}"

# 2. Universal Package Installation
install_packages() {
    echo "--> Installing WireGuard & Security Tooling..."
    if command -v apt-get &>/dev/null; then
        apt-get update -qq && apt-get install -y wireguard wireguard-tools iptables curl iproute2 cron
    elif command -v dnf &>/dev/null; then
        dnf install -y wireguard-tools iptables curl iproute cronie
    elif command -v pacman &>/dev/null; then
        pacman -Sy --noconfirm wireguard-tools iptables curl iproute2 cronie
    elif command -v zypper &>/dev/null; then
        zypper install -y wireguard-tools iptables curl iproute2 cronie
    elif command -v apk &>/dev/null; then
        apk add wireguard-tools iptables curl iproute2 cron
    else
        echo "[-] Package manager not explicitly matched. Ensure wireguard-tools and iptables are installed."
    fi
}
install_packages

# 3. Kernel Network Hardening & Forwarding
echo "--> Applying Sysctl Kernel Hardening..."
sysctl -w net.ipv4.ip_forward=1 >/dev/null
sysctl -w net.ipv4.conf.all.rp_filter=1 >/dev/null
sysctl -w net.ipv4.conf.default.rp_filter=1 >/dev/null
sysctl -w net.ipv4.conf.all.accept_redirects=0 >/dev/null
sysctl -w net.ipv4.conf.all.send_redirects=0 >/dev/null

cat <<EOF > /etc/sysctl.d/99-wireguard-hardened.conf
net.ipv4.ip_forward = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
EOF

# 4. Key Generation (Strict 0700 / 0600 permissions)
mkdir -p /etc/wireguard
chmod 700 /etc/wireguard
if [ ! -f /etc/wireguard/server_private.key ]; then
    echo "--> Generating Server Cryptographic Keys..."
    wg genkey | tee /etc/wireguard/server_private.key | wg pubkey > /etc/wireguard/server_public.key
    chmod 600 /etc/wireguard/server_private.key
fi

SERVER_PRIVKEY=$(cat /etc/wireguard/server_private.key)
SERVER_PUBKEY=$(cat /etc/wireguard/server_public.key)

# 5. Write Server Config with Stateful Firewall Rules
echo "--> Writing Server Configuration (/etc/wireguard/${WG_IF}.conf)..."
cat <<EOF > /etc/wireguard/${WG_IF}.conf
[Interface]
Address = ${SERVER_IP}
ListenPort = ${WG_PORT}
PrivateKey = ${SERVER_PRIVKEY}

# Firewall Hardening: Forward stateful connections & drop untracked packets
PostUp = iptables -A FORWARD -i %i -m state --state ESTABLISHED,RELATED -j ACCEPT; iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o ${DEFAULT_NIC} -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -m state --state ESTABLISHED,RELATED -j ACCEPT; iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o ${DEFAULT_NIC} -j MASQUERADE

EOF

chmod 600 /etc/wireguard/${WG_IF}.conf

# 6. Automatic Firewall Allowance
if command -v ufw &>/dev/null && ufw status | grep -q "active"; then
    ufw allow ${WG_PORT}/udp
elif command -v firewall-cmd &>/dev/null && systemctl is-active --quiet firewalld 2>/dev/null; then
    firewall-cmd --add-port=${WG_PORT}/udp --permanent
    firewall-cmd --reload
fi

# 7. Start Interface
if command -v systemctl &>/dev/null && systemctl is-system-running &>/dev/null; then
    systemctl enable wg-quick@${WG_IF}
    systemctl restart wg-quick@${WG_IF}
else
    wg-quick down ${WG_IF} 2>/dev/null || true
    wg-quick up ${WG_IF}
fi

PUBLIC_IP=$(curl -s --max-time 5 https://api.ipify.org || curl -s --max-time 5 https://ifconfig.me)

echo "=================================================================="
echo " Server Setup Complete!"
echo " Public IP  : ${PUBLIC_IP}"
echo " Public Key : ${SERVER_PUBKEY}"
echo " WireGuard  : ${WG_IF} (${SERVER_IP})"
echo "=================================================================="
```

### 2. Client Generator Script (`add-wg-client.sh`)

Run this script as `root` on the server to generate peer configurations with unique asymmetric keys and Pre-Shared Keys (PSK).

```bash
#!/bin/bash
# Hardened Client Generator with PSK & Hot-Reloading

set -e

if [ "$EUID" -ne 0 ]; then
  echo "[-] Error: Please run as root (sudo)."
  exit 1
fi

if [ -z "$1" ] || [ -z "$2" ]; then
    echo "Usage: sudo ./add-wg-client.sh <client_name> <last_octet_ip>"
    echo "Example: sudo ./add-wg-client.sh target-desktop 2"
    echo "Example: sudo ./add-wg-client.sh laptop-client 3"
    exit 1
fi

CLIENT_NAME="$1"
OCTET="$2"
WG_IF="wg0"
WG_PORT=51820
WG_NET="10.10.0"
CLIENT_IP="${WG_NET}.${OCTET}"

PUBLIC_IP=$(curl -s --max-time 5 https://api.ipify.org || curl -s --max-time 5 https://ifconfig.me)
SERVER_PUBKEY=$(cat /etc/wireguard/server_public.key)

# Generate Client Private Key and Pre-Shared Key (PSK)
CLIENT_PRIVKEY=$(wg genkey)
CLIENT_PUBKEY=$(echo "$CLIENT_PRIVKEY" | wg pubkey)
CLIENT_PSK=$(wg genpsk)

# Append Peer to Server Config
cat <<EOF >> /etc/wireguard/${WG_IF}.conf

# Peer: ${CLIENT_NAME}
[Peer]
PublicKey = ${CLIENT_PUBKEY}
PresharedKey = ${CLIENT_PSK}
AllowedIPs = ${CLIENT_IP}/32
EOF

# Sync configuration live without tearing down interface
if command -v wg-quick &>/dev/null && wg show "${WG_IF}" &>/dev/null; then
    wg syncconf "${WG_IF}" <(wg-quick strip "${WG_IF}")
fi

# Output Client Configuration File
CLIENT_FILE="${CLIENT_NAME}.conf"
cat <<EOF > "${CLIENT_FILE}"
[Interface]
PrivateKey = ${CLIENT_PRIVKEY}
Address = ${CLIENT_IP}/32

[Peer]
PublicKey = ${SERVER_PUBKEY}
PresharedKey = ${CLIENT_PSK}
Endpoint = ${PUBLIC_IP}:${WG_PORT}
# Split-Tunneling: Only route VPN Subnet
AllowedIPs = ${WG_NET}.0/24
PersistentKeepalive = 25
EOF

chmod 600 "${CLIENT_FILE}"

echo "=================================================================="
echo " Hardened Client Profile Created!"
echo " Name        : ${CLIENT_NAME}"
echo " VPN IP      : ${CLIENT_IP}"
echo " Config File : $(pwd)/${CLIENT_FILE}"
echo "=================================================================="
```

### 3. Client Installer Script (`setup-wg-client.sh`)

Transfer a generated `.conf` profile to any client or target machine and run this script to configure WireGuard, enable boot persistence, and deploy the auto-healing ping watchdog.

```bash
#!/bin/bash
# Universal Client Deployment, Remote Access Provisioning & Auto-Healing Watchdog Installer

set -e

if [ "$EUID" -ne 0 ]; then
  echo "[-] Error: Please run as root (sudo)."
  exit 1
fi

CONF_FILE="$1"

if [ -z "$CONF_FILE" ] || [ ! -f "$CONF_FILE" ]; then
    echo "Usage: sudo ./setup-wg-client.sh <path_to_client_config.conf>"
    echo "Example: sudo ./setup-wg-client.sh ubuntu.conf"
    exit 1
fi

# 1. Dynamically parse client VPN IP address from the config file (e.g., 10.10.0.2)
CLIENT_VPN_IP=$(grep -i 'Address' "$CONF_FILE" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -n1)

if [ -z "$CLIENT_VPN_IP" ]; then
    echo "[-] Warning: Could not auto-detect VPN IP from $CONF_FILE. Defaulting xRDP to bind to all interfaces."
else
    echo "--> Detected Client VPN IP: ${CLIENT_VPN_IP}"
fi

# 2. Universal Dependency Installation (WireGuard, SSH, xRDP, Cron)
echo "--> Installing Client Dependencies (WireGuard, OpenSSH, xRDP, Cron)..."
if command -v apt-get &>/dev/null; then
    apt-get update -qq && apt-get install -y wireguard wireguard-tools iproute2 cron openssh-server xrdp ufw
    SSH_SVC="ssh"
    XRDP_SVC="xrdp"
elif command -v dnf &>/dev/null; then
    dnf install -y wireguard-tools iproute cronie openssh-server xrdp
    SSH_SVC="sshd"
    XRDP_SVC="xrdp"
elif command -v pacman &>/dev/null; then
    pacman -Sy --noconfirm wireguard-tools iproute2 cronie openssh xrdp
    SSH_SVC="sshd"
    XRDP_SVC="xrdp"
elif command -v zypper &>/dev/null; then
    zypper install -y wireguard-tools iproute2 cronie openssh xrdp
    SSH_SVC="sshd"
    XRDP_SVC="xrdp"
elif command -v apk &>/dev/null; then
    apk add wireguard-tools iproute2 cron openssh xrdp
    SSH_SVC="sshd"
    XRDP_SVC="xrdp"
else
    echo "[-] Package manager not explicitly matched. Ensure wireguard, openssh-server, and xrdp are installed."
    SSH_SVC="ssh"
    XRDP_SVC="xrdp"
fi

# 3. Deploy WireGuard Configuration File
echo "--> Deploying WireGuard Interface (/etc/wireguard/wg0.conf)..."
mkdir -p /etc/wireguard
cp "$CONF_FILE" /etc/wireguard/wg0.conf
chmod 600 /etc/wireguard/wg0.conf

# 4. Configure & Bind xRDP Strictly to WireGuard Interface
if [ -f /etc/xrdp/xrdp.ini ] && [ -n "$CLIENT_VPN_IP" ]; then
    echo "--> Binding xRDP strictly to WireGuard IP (${CLIENT_VPN_IP}:3389)..."
    sed -i "s/^address=.*/address=${CLIENT_VPN_IP}/" /etc/xrdp/xrdp.ini
fi

# 5. Configure Local Firewalls (Allow SSH & xRDP strictly on wg0)
if command -v ufw &>/dev/null && ufw status | grep -q "active"; then
    echo "--> Configuring UFW rules for wg0 interface..."
    ufw allow in on wg0 to any port 22 proto tcp >/dev/null 2>&1 || true
    ufw allow in on wg0 to any port 3389 proto tcp >/dev/null 2>&1 || true
elif command -v firewall-cmd &>/dev/null && systemctl is-active --quiet firewalld 2>/dev/null; then
    echo "--> Configuring Firewalld rules for wg0 interface..."
    firewall-cmd --zone=trusted --add-interface=wg0 --permanent >/dev/null 2>&1 || true
    firewall-cmd --reload >/dev/null 2>&1 || true
fi

# 6. Enable and Start Remote Access Services
echo "--> Starting and Enabling SSH and xRDP Services..."
if command -v systemctl &>/dev/null && systemctl is-system-running &>/dev/null; then
    systemctl enable --now "$SSH_SVC" 2>/dev/null || systemctl restart "$SSH_SVC" 2>/dev/null || true
    systemctl enable --now "$XRDP_SVC" 2>/dev/null || systemctl restart "$XRDP_SVC" 2>/dev/null || true
fi

# 7. Create Watchdog Healing Script
cat <<'EOF' > /usr/local/bin/wg-watchdog.sh
#!/bin/bash
# Auto-healing Watchdog Script
GATEWAY="10.10.0.1"
WG_IF="wg0"
LOG_FILE="/var/log/wg-watchdog.log"

if ! ip link show "$WG_IF" >/dev/null 2>&1; then
    echo "$(date): Interface $WG_IF missing. Rebuilding..." >> "$LOG_FILE"
    wg-quick up "$WG_IF" >/dev/null 2>&1
    exit 0
fi

if ! ping -c 2 -W 3 "$GATEWAY" >/dev/null 2>&1; then
    echo "$(date): Gateway $GATEWAY unreachable. Resetting $WG_IF tunnel..." >> "$LOG_FILE"
    if command -v systemctl &>/dev/null && systemctl is-system-running &>/dev/null; then
        systemctl restart "wg-quick@${WG_IF}"
    else
        wg-quick down "$WG_IF" >/dev/null 2>&1 || true
        wg-quick up "$WG_IF" >/dev/null 2>&1
    fi
fi
EOF

chmod +x /usr/local/bin/wg-watchdog.sh

# 8. Configure Cron Jobs (Boot Persistence + Watchdog execution every 2 mins)
echo "--> Setting up Persistence Cron Jobs..."
crontab -l 2>/dev/null | grep -v "wg-watchdog" | grep -v "wg-quick" > /tmp/cron_bak || true
echo "@reboot /usr/bin/wg-quick up wg0 >/dev/null 2>&1" >> /tmp/cron_bak
echo "*/2 * * * * /usr/local/bin/wg-watchdog.sh" >> /tmp/cron_bak
crontab /tmp/cron_bak
rm -f /tmp/cron_bak

# 9. Enable and Launch WireGuard System Service
if command -v systemctl &>/dev/null && systemctl is-system-running &>/dev/null; then
    systemctl enable wg-quick@wg0
    systemctl restart wg-quick@wg0
else
    wg-quick down wg0 2>/dev/null || true
    wg-quick up wg0
fi

echo "=================================================================="
echo " Client Deployment & Remote Access Services Complete!"
echo " WireGuard  : Active (${CLIENT_VPN_IP:-N/A})"
echo " SSH Status : Enabled (Port 22)"
echo " xRDP Status: Bound & Enabled (${CLIENT_VPN_IP:-All}:3389)"
echo " Firewalls  : Rules applied on wg0 interface"
echo " Watchdog   : Running (/var/log/wg-watchdog.log)"
echo "=================================================================="
```

### 4. Hardening xRDP on Target Machine

To prevent `xrdp` on your target Linux machine from listening publicly or over local networks, lock its binding address down exclusively to its assigned WireGuard IP address.

Run this on the Remote Target Machine (e.g., assigned IP `10.10.0.2`):

```bash
# Bind xRDP strictly to its WireGuard VPN IP address
sudo sed -i 's/^address=.*/address=10.10.0.2/' /etc/xrdp/xrdp.ini

# Restart the service
sudo systemctl restart xrdp
```

### 5. End-to-End Deployment Workflow

1. Initialize Server Gateway:

   On your public IP server, run:

```bash
sudo chmod +x setup-wg-server.sh
sudo ./setup-wg-server.sh
```

1. Generate Client Profiles:

   On the server, provision configurations for your machines:

   ```bash
   sudo chmod +x add-wg-client.sh

   # Profile for target machine (gets 10.10.0.2)
   sudo ./add-wg-client.sh target-pc 2

   # Profile for viewing laptop (gets 10.10.0.3)
   sudo ./add-wg-client.sh viewing-laptop 3
   ```
2. Deploy Configurations to Clients:

   Transfer `target-pc.conf` and `viewing-laptop.conf` to their respective systems and execute:

   ```bash
   sudo chmod +x setup-wg-client.sh

   # On Target PC:
   sudo ./setup-wg-client.sh target-pc.conf

   # On Viewing Laptop:
   sudo ./setup-wg-client.sh viewing-laptop.conf
   ```

### 6. RDP Client Configuration

#### Remmina Setup (GUI)

1. Open Remmina and click `+` to add a new connection profile.
2. Set Protocol to `RDP - Remote Desktop Protocol`.
3. Set Server to `10.10.0.2` *(Target's WireGuard IP)*.
4. Set Color Depth to `RemoteFX (32 bpp)` or `True color (32 bpp)`.
5. Under the Advanced tab, set Resolution to `Use client resolution`.
6. Click Save and Connect.

#### FreeRDP Setup (CLI)

Launch FreeRDP directly over the active WireGuard tunnel:

Bash

```
xfreerdp /v:10.10.0.2 /u:your_username /size:1920x1080 /gdi:hw +dynamic-resolution
```

### 7. Diagnostics & Troubleshooting Commands

* Inspect Active Handshakes & Stats:

  Bash

  ```
  sudo wg show
  ```

  *(Verify that `latest handshake` is within the last 2 minutes and transfer byte counters are increasing.)*
* Ping Private Gateway:

  Bash

  ```
  ping 10.10.0.1
  ```
* View Auto-Healing Watchdog Logs:

  Bash

  ```
  tail -f /var/log/wg-watchdog.log
  ```
