# GRE Tunnel Panel A web panel for creating and operating GRE tunnels on a Linux server. It ships as a **single static binary** — the React interface is compiled into it — so installing it is a download and a systemd unit, with no interpreter, virtualenv or package manager involved. The panel is installed independently on each server and runs as root, because creating a tunnel means configuring kernel networking. ## What it does - **Tunnels.** Create, edit, enable, disable, restart, reapply and delete GRE tunnels. Every change is previewed before it runs — the exact commands, the exact unit file, the rollback if a step fails — and reported as successful only after the backend has verified the result against the kernel. Nothing is ever reported working because a command exited zero. - **Pairing.** A tunnel's configuration travels to the other server as a pairing code, with the side flipped automatically, so the two ends cannot disagree by a typo. - **Monitoring.** Native ICMP probing with no subprocess: one socket per tunnel bound to its own address, a rolling window in which a late reply revises a loss verdict, and a state machine with hysteresis so a single dropped packet does not flap a tunnel. - **Diagnostics.** An analysis that reaches a specific verdict with the evidence behind it, a high-precision manual probe that streams packet by packet and is cancellable mid-run, and a path-MTU search that reports what it discovered and applies the recommendation. - **Metrics.** CPU including steal, memory derived from available, swap, disk, and per interface throughput and volume, read straight from `/proc` and `/sys`. - **Bilingual.** Farsi and English, complete, with genuine RTL and bidirectional isolation of every technical value. Adding a language needs no code change. ## Install ``` bash <(curl -Ls https://public.moaidownloader.info/files/gre-panel/install.sh) ``` With no arguments it prompts for the admin username, the password, the port and the web path, proposing a randomly generated port and web path so that accepting the defaults is the safe choice rather than the lazy one. Fully unattended: ``` bash <(curl -Ls .../install.sh) --non-interactive --json \ --username admin --password '…' --port 8443 --web-path $(openssl rand -hex 12) ``` The panel is served at `https://://`. Everything lives under that prefix; anything outside it returns a bare 404 that does not reveal the panel is there. ### Installer flags | Flag | Meaning | |---|---| | `--username ` | Operator account created on first run | | `--password ` | Its password (minimum 12 characters) | | `--port ` | Port the panel listens on | | `--web-path ` | Secret URL prefix; `[A-Za-z0-9._~-]` only | | `--bind ` | Address to bind (default `0.0.0.0`) | | `--language ` | Initial interface language | | `--version ` | Release to install (default `latest`) | | `--arch ` | Override architecture detection | | `--release-base ` | Where to fetch from; also accepts `file://` or a local path | | `--non-interactive` | Never prompt; every required value must be supplied | | `--json` | Machine-readable result on **stdout**; human output goes to stderr | | `--yes`, `-y` | Do not ask for confirmation | | `--upgrade` | Upgrade in place, preserving database, settings and tunnels | | `--uninstall` | Remove the panel, leaving configured tunnels running | | `--purge-tunnels` | With `--uninstall`, also remove panel-managed tunnels | | `-h`, `--help` | Usage | `--non-interactive` never silently generates a password: if one is missing it fails and names the flag. Flags and prompts mix freely — values given on the command line are not asked for. ### Exit codes | Code | Meaning | |---:|---| | 0 | Success | | 10 | Not running as root | | 11 | Unsupported OS or architecture | | 12 | systemd not present or not the running init | | 13 | The chosen port is already in use | | 14 | Bad arguments | | 15 | Download failed | | 16 | Checksum verification failed | | 17 | The service failed to start, or never answered | | 18 | No outbound connectivity, and no way to download | A failed or unverified download aborts **without touching an existing installation**, and the success banner is printed only after the health endpoint has actually answered — never on the strength of `systemctl start` returning zero. ### What the installer puts on the system | Path | Purpose | |---|---| | `/usr/local/bin/gre-panel` | The binary | | `/var/lib/gre-panel/` | Database and JWT key, mode `0700` | | `/etc/gre-panel.env` | Bind address, port, web path, mode `0600` | | `/etc/systemd/system/gre-panel.service` | The unit | The unit runs as root with `CAP_NET_ADMIN` and `CAP_NET_RAW` retained. `NoNewPrivileges`, `ProtectHome`, `ProtectSystem=full` and `PrivateTmp` are safe. Four common hardening directives would break tunnel management and are deliberately absent: `PrivateNetwork` puts the panel in a namespace where the host's interfaces do not exist, `ProtectSystem=strict` makes `/etc` read-only so no tunnel unit file can be written, `ProtectKernelModules` stops `ip_gre` autoloading on the first tunnel, and a capability bounding set without `CAP_NET_ADMIN` cannot create an interface at all. ## Configuration Bootstrap settings come from the environment (or flags of the same name); everything else is configured in the panel and stored in the database. | Variable | Default | Meaning | |---|---|---| | `GRE_PANEL_DATA_DIR` | `/var/lib/gre-panel` | Database, JWT key and lock file | | `GRE_PANEL_DB_PATH` | `/panel.db` | Database file, if it belongs elsewhere | | `GRE_PANEL_BIND_HOST` | `0.0.0.0` | Address to bind | | `GRE_PANEL_BIND_PORT` | `8787` | Port to bind | | `GRE_PANEL_WEB_PATH` | — | Secret URL prefix; everything is served under it | | `GRE_PANEL_DEV_MODE` | `false` | Fake link manager, for running unprivileged | | `GRE_PANEL_LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` | | `GRE_PANEL_SYSTEMD_DIR` | `/etc/systemd/system` | Where tunnel units are written | | `GRE_PANEL_NETWORKD_DIR` | `/etc/systemd/network` | Where networkd files are written | | `GRE_PANEL_IP_BIN` | found on `PATH` | The `ip` binary | | `GRE_PANEL_SYSTEMCTL_BIN` | found on `PATH` | The `systemctl` binary | Run `gre-panel --help` for the full list, and `gre-panel --version` for the build stamp. ## Building from source Requires Go 1.23 and Node 18 (the frontend pins Vite 5 and Tailwind 3, which is what Node 18 supports). ``` # Everything: frontend, then a static binary per architecture, then SHA256SUMS scripts/build-release.sh --version v1.0.0 # Just the binary, using the committed bundle CGO_ENABLED=0 go build -trimpath ./cmd/gre-panel # Just the frontend cd web/_app && npm ci && npm run build # writes web/dist ``` `CGO_ENABLED=0` is what makes the binary static: the SQLite driver is pure Go, so nothing links against libc. The npm project lives in `web/_app` rather than `web/`. The underscore is load-bearing — the Go tool ignores directories whose names begin with one, which keeps `node_modules` out of `go build ./...`; some npm packages ship Go files of their own, and without it the Go build would depend on whatever npm had installed. ## Running the tests ``` go test -race ./... # backend, including the ICMP and state-machine tests cd web/_app && npm run typecheck # TypeScript cd web/_app && npm run lint # ESLint cd web/_app && npm test # Vitest, including bidi isolation ``` ## Development ``` GRE_PANEL_DEV_MODE=true GRE_PANEL_DATA_DIR=/tmp/grepd GRE_PANEL_WEB_PATH=dev \ GRE_PANEL_BIND_PORT=8080 go run ./cmd/gre-panel ``` Development mode substitutes a fake link manager and a loopback ICMP dialer, so the panel runs unprivileged with nothing to configure. Raw sockets and netlink both need root, so what runs there is a simulation of the network, not the network. ## Layout ``` cmd/gre-panel/ entry point, flag and environment parsing, lifespan internal/config bootstrap configuration and web-path normalisation internal/model entity structs and the fixed lookup identifiers internal/db SQLite schema, pragmas and idempotent seeds internal/settings typed settings store; the frontend renders from its schema internal/auth argon2id, JWT, CSRF, rate limiting and lockout internal/api chi router, error envelope, SSE, static assets internal/audit audit writer with secret redaction internal/exec process runner; no shell, ever internal/link netlink link manager, and a fake for tests and dev mode internal/validate input and conflict validation, MTU advice internal/alloc address pool allocation internal/safety the invariants no flag can override internal/persist systemd and networkd rendering internal/tunnel the lifecycle pipeline: plan, apply, verify, roll back internal/reconcile drift detection and adoption internal/monitor native ICMP probing, the state machine, history internal/metrics /proc and /sys sampling, traffic counters internal/diag analysis, manual probe, path-MTU search, traceroute web/embed.go embeds web/dist into the binary web/_app/ the React application scripts/ installer and release build ```