diff --git a/.env.example b/.env.example index d376fd3..88b9ad1 100644 --- a/.env.example +++ b/.env.example @@ -27,3 +27,14 @@ WG_UI_PORT=51821 # DNS inviati ai client WG_DEFAULT_DNS=1.1.1.1,8.8.8.8 + +# --- OPZIONALI — Limiti risorse container (SBC) --- +# Decommenta e adatta alla RAM disponibile: +# 512 MB RAM → WG_MEM_LIMIT=128m +# 1 GB RAM → WG_MEM_LIMIT=256m (default) +# 2 GB+ RAM → WG_MEM_LIMIT=384m +# Tenere WG_MEMSWAP_LIMIT = WG_MEM_LIMIT per disabilitare lo swap del container. +# Su single-core (Pi Zero / Pi 1) impostare WG_CPUS=0.75. +# WG_MEM_LIMIT=256m +# WG_MEMSWAP_LIMIT=256m +# WG_CPUS=1.0 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..88f472b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,47 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this is + +A Docker Compose setup for a self-hosted WireGuard VPN server using [wg-easy](https://github.com/wg-easy/wg-easy), designed to run on a Raspberry Pi or any Linux server. The web UI (wg-easy) handles client management, QR code generation, and key lifecycle. + +## Common commands + +```bash +# First-time setup +cp .env.example .env +# Edit .env with WG_HOST, PASSWORD_HASH, TZ + +# Generate bcrypt password hash (no extra dependencies) +docker run --rm -it ghcr.io/wg-easy/wg-easy wgpw 'YourPassword' + +# Start the VPN +docker compose up -d + +# Update wg-easy to the latest image +docker compose pull && docker compose up -d + +# View logs +docker compose logs -f wg-easy +``` + +## Configuration + +All runtime configuration lives in `.env` (not committed). Required variables: + +| Variable | Description | +|---|---| +| `WG_HOST` | Public IP or DDNS hostname clients connect to | +| `PASSWORD_HASH` | bcrypt hash of the web UI password (prefix `$2a$`, `$2b$`, or `$2y$`) | +| `TZ` | IANA timezone (e.g. `Europe/Rome`) | + +Optional: `WG_PORT` (default 51820/udp), `WG_UI_PORT` (default 51821/tcp), `WG_DEFAULT_DNS` (default `1.1.1.1,8.8.8.8`). + +## Important constraints + +- `wg-data/` is auto-generated by the container on first start and holds live WireGuard keys (`wg0.conf`, `wg0.json`). Never commit it. +- `.env` is gitignored — it holds the password hash and hostname. +- The container requires `NET_ADMIN` and `SYS_MODULE` capabilities plus `net.ipv4.ip_forward=1` sysctl — these are already set in `docker-compose.yml`. +- The router must forward UDP port 51820 (or `WG_PORT`) to the server's local IP. +- `openssl passwd` does **not** support bcrypt — use the Docker method, `htpasswd`, or Python's `bcrypt` library to generate `PASSWORD_HASH`. diff --git a/README.md b/README.md index 96c0cf8..4e3ce2c 100644 --- a/README.md +++ b/README.md @@ -31,43 +31,36 @@ Apri `.env` con un editor e compila i valori richiesti: | Variabile | Descrizione | Obbligatoria | |---|---|---| -| `WG_HOST` | IP pubblico o hostname DDNS del server | Si | -| `PASSWORD_HASH` | Hash bcrypt della password per la UI web | Si | -| `TZ` | Fuso orario (formato IANA, es. `Europe/Rome`) | Si | +| `WG_HOST` | IP pubblico o hostname DDNS del server | Sì | +| `PASSWORD_HASH` | Hash della password per la UI web (vedi sotto) | Sì | +| `TZ` | Fuso orario (formato IANA, es. `Europe/Rome`) | Sì | | `WG_PORT` | Porta UDP WireGuard (default: `51820`) | No | | `WG_UI_PORT` | Porta TCP interfaccia web (default: `51821`) | No | | `WG_DEFAULT_DNS` | DNS inviati ai client (default: `1.1.1.1,8.8.8.8`) | No | -### 3. Genera l'hash della password +### 3. Genera e imposta la password -Scegli uno dei seguenti metodi: +La password non si scrive in chiaro nel file `.env`: va convertita in un **hash** (una stringa cifrata che wg-easy usa per verificare il login senza conservare la password originale). -**Metodo A — Docker (consigliato, zero dipendenze aggiuntive):** +**Passo 1** — Scegli la password che vuoi usare per accedere all'interfaccia web, poi esegui questo comando sostituendo `LatuaPassword` con quella scelta: ```bash docker run --rm -it ghcr.io/wg-easy/wg-easy wgpw 'LatuaPassword' ``` -Output esempio: `PASSWORD_HASH='$2a$12$...'` -Copia il valore senza le virgolette singole. +**Passo 2** — Il comando stampa una riga simile a questa: -**Metodo B — `htpasswd` (pacchetto `apache2-utils`):** - -```bash -sudo apt install apache2-utils -htpasswd -bnBC 12 "" 'LatuaPassword' | tr -d ':\n' +``` +PASSWORD_HASH='$2a$12$cDMCiMmFTuMOlT1E4BvxEO4CJfzMKSanRZSMqiE1234abcXYZ' ``` -**Metodo C — Python:** +**Passo 3** — Copia solo la parte dopo `PASSWORD_HASH=`, **senza** le virgolette singole. Nel file `.env` deve apparire così: -```bash -pip3 install bcrypt -python3 -c "import bcrypt; print(bcrypt.hashpw(b'LatuaPassword', bcrypt.gensalt(12)).decode())" +``` +PASSWORD_HASH=$2a$12$cDMCiMmFTuMOlT1E4BvxEO4CJfzMKSanRZSMqiE1234abcXYZ ``` -> **Nota:** `openssl passwd` non supporta bcrypt. I metodi sopra producono hash con prefisso `$2a$`, `$2b$` o `$2y$` — tutti validi e accettati da wg-easy. - -> **Attenzione:** incolla il valore esatto in `.env`, senza aggiungere `$$` o altri caratteri di escape. +> **Attenzione:** non aggiungere virgolette, spazi o altri caratteri intorno al valore. Copia e incolla esattamente quello che hai ottenuto. ### 4. Apri la porta sul router @@ -94,7 +87,7 @@ Da qui puoi aggiungere client, scaricare i file di configurazione e generare QR ## Struttura del progetto ``` -vpn-wg/ +vpn/ ├── docker-compose.yml # Definizione del servizio ├── .env.example # Template variabili (committato) ├── .env # Variabili locali con segreti (NON committato) diff --git a/docker-compose.yml b/docker-compose.yml index 4ea0a04..7dbf2d3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,9 +1,31 @@ services: wg-easy: - image: ghcr.io/wg-easy/wg-easy:latest + image: ghcr.io/wg-easy/wg-easy:15.2.2 # Pinnato — aggiornare deliberatamente con compose pull container_name: wg-easy restart: unless-stopped + # --- Limiti risorse (SBC con 1 GB RAM) --- + # mem_limit = memswap_limit disabilita lo swap per il container: + # se supera 256 MB Docker lo riavvia pulito invece di mandare in OOM l'host. + mem_limit: "${WG_MEM_LIMIT:-256m}" + memswap_limit: "${WG_MEMSWAP_LIMIT:-256m}" + cpus: "${WG_CPUS:-1.0}" + + # --- Rotazione log: evita che i log riempiano la SD card --- + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" + + # --- Health check: riavvio automatico se Node.js si blocca --- + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:51821/"] + interval: 60s + timeout: 10s + retries: 3 + start_period: 30s + environment: PASSWORD_HASH: "${PASSWORD_HASH}" TZ: "${TZ}" @@ -14,8 +36,12 @@ services: volumes: - ./wg-data:/etc/wireguard + # File temporanei in RAM invece che sulla SD card + tmpfs: + - /tmp:size=32m,mode=1777 + ports: - - "${WG_PORT:-51820}:51820/udp" # Traffico VPN WireGuard + - "${WG_PORT:-51820}:51820/udp" # Traffico VPN WireGuard - "${WG_UI_PORT:-51821}:51821/tcp" # Interfaccia web di gestione cap_add: