#!/usr/bin/env bash
#
# wg-setup.sh — stand up your own WireGuard VPN server, from scratch.
#
#   Run your own VPN. Don't rent trust from a stranger.
#   https://domainless.fun/vpn
#
# What it does, idempotently:
#   1. installs WireGuard (apt / dnf / pacman)
#   2. turns on IP forwarding (a sysctl drop-in, survives reboot)
#   3. generates a server keypair in /etc/wireguard (mode 0600)
#   4. writes /etc/wireguard/<iface>.conf with a NAT masquerade rule
#   5. enables + starts the tunnel on boot
#   6. prints the server's public key + endpoint so you can add clients
#
# Nothing here is proprietary — it's the stock WireGuard + wg-quick setup,
# written out so you can read every line before you trust it. Read it. Then
# run it as root on a fresh VPS or a spare box:
#
#   sudo bash wg-setup.sh
#
# Override any default with an env var, e.g.:
#   sudo WG_PORT=51821 WG_SUBNET=10.9.0 WG_DNS=1.1.1.1 bash wg-setup.sh
#
set -euo pipefail

# ---- knobs (all overridable via the environment) ----------------------------
WG_IFACE="${WG_IFACE:-wg0}"          # interface / config name
WG_PORT="${WG_PORT:-51820}"          # UDP port peers dial in on
WG_SUBNET="${WG_SUBNET:-10.7.0}"     # first three octets of the VPN subnet
WG_ADDR="${WG_ADDR:-${WG_SUBNET}.1}" # the server's address inside the tunnel
WG_DNS="${WG_DNS:-1.1.1.1}"          # DNS handed to clients (used in their .conf)
WG_DIR="/etc/wireguard"
WG_CONF="${WG_DIR}/${WG_IFACE}.conf"

die() { echo "error: $*" >&2; exit 1; }
[ "$(id -u)" = "0" ] || die "run me as root (try: sudo bash $0)"

# ---- 1. install WireGuard ---------------------------------------------------
if ! command -v wg >/dev/null 2>&1; then
  echo "==> installing wireguard…"
  if   command -v apt-get >/dev/null; then apt-get update -qq && apt-get install -y wireguard qrencode
  elif command -v dnf     >/dev/null; then dnf install -y wireguard-tools qrencode
  elif command -v pacman  >/dev/null; then pacman -Sy --noconfirm wireguard-tools qrencode
  else die "no apt/dnf/pacman found — install 'wireguard-tools' by hand, then re-run"
  fi
else
  echo "==> wireguard already installed"
fi

# ---- 2. detect the WAN interface (the one with the default route) -----------
WAN_IFACE="${WAN_IFACE:-$(ip route show default 2>/dev/null | awk '/default/ {print $5; exit}')}"
[ -n "$WAN_IFACE" ] || die "couldn't detect your internet-facing interface — set WAN_IFACE=eth0 (or similar) and re-run"
echo "==> WAN interface: ${WAN_IFACE}"

# ---- 3. enable IP forwarding (persistent) -----------------------------------
echo "==> enabling IPv4/IPv6 forwarding"
cat > /etc/sysctl.d/99-wireguard-forward.conf <<EOF
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
EOF
sysctl -q --system

# ---- 4. server keypair (only generate once) ---------------------------------
umask 077
mkdir -p "$WG_DIR"
if [ ! -s "${WG_DIR}/server.key" ]; then
  echo "==> generating server keypair"
  wg genkey | tee "${WG_DIR}/server.key" | wg pubkey > "${WG_DIR}/server.pub"
else
  echo "==> reusing existing server keypair (${WG_DIR}/server.key)"
fi
SERVER_PRIV="$(cat "${WG_DIR}/server.key")"
SERVER_PUB="$(cat "${WG_DIR}/server.pub")"

# ---- 5. write the interface config (preserving any [Peer] blocks) -----------
# On a re-run we keep whatever clients you've already added and only rewrite
# the [Interface] section.
PEERS=""
if [ -f "$WG_CONF" ]; then
  PEERS="$(awk '/^\[Peer\]/{p=1} p{print}' "$WG_CONF")"
  cp -a "$WG_CONF" "${WG_CONF}.bak.$(date +%s)"
fi

echo "==> writing ${WG_CONF}"
{
  cat <<EOF
# Managed by wg-setup.sh — https://domainless.fun/vpn
# Server address ${WG_ADDR}/24 · listens on udp/${WG_PORT}
[Interface]
Address = ${WG_ADDR}/24
ListenPort = ${WG_PORT}
PrivateKey = ${SERVER_PRIV}
# NAT client traffic out through the WAN interface, and allow forwarding.
PostUp   = iptables -t nat -A POSTROUTING -s ${WG_SUBNET}.0/24 -o ${WAN_IFACE} -j MASQUERADE; iptables -A FORWARD -i ${WG_IFACE} -j ACCEPT; iptables -A FORWARD -o ${WG_IFACE} -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -s ${WG_SUBNET}.0/24 -o ${WAN_IFACE} -j MASQUERADE; iptables -D FORWARD -i ${WG_IFACE} -j ACCEPT; iptables -D FORWARD -o ${WG_IFACE} -j ACCEPT
EOF
  if [ -n "$PEERS" ]; then printf '\n%s\n' "$PEERS"; fi
} > "$WG_CONF"
chmod 600 "$WG_CONF"

# ---- 6. open the firewall port (best-effort) --------------------------------
if command -v ufw >/dev/null 2>&1; then
  ufw allow "${WG_PORT}/udp" >/dev/null 2>&1 || true
fi

# ---- 7. bring it up + enable on boot ----------------------------------------
echo "==> starting the tunnel"
systemctl enable "wg-quick@${WG_IFACE}" >/dev/null 2>&1 || true
# restart so a re-run picks up config changes cleanly
systemctl restart "wg-quick@${WG_IFACE}"

# ---- done -------------------------------------------------------------------
PUBIP="$(curl -fsS4 https://api.ipify.org 2>/dev/null || echo 'YOUR.SERVER.IP')"
cat <<EOF

────────────────────────────────────────────────────────────
  Your WireGuard server is up. 🎉

  Endpoint (give this to clients):  ${PUBIP}:${WG_PORT}
  Server public key:                ${SERVER_PUB}
  VPN subnet:                       ${WG_SUBNET}.0/24
  Clients get DNS:                  ${WG_DNS}

  Add a device:   sudo bash wg-add-client.sh phone
  Check it:       sudo wg show
────────────────────────────────────────────────────────────

  Make sure udp/${WG_PORT} is open to the internet (cloud firewall /
  security group too, not just the box). That's it — you now run a VPN
  that only you control.
EOF
