5232609719
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
289 lines
11 KiB
Markdown
289 lines
11 KiB
Markdown
# Installing panel
|
|
|
|
Four supported paths, in order of least effort:
|
|
|
|
1. [Linux one-liner](#linux-one-liner) — `install.sh` (recommended)
|
|
2. [Windows one-liner](#windows-one-liner) — `install.ps1`
|
|
3. [docker-compose](#docker-compose) — controller + agent + Postgres as containers
|
|
4. [From source](#from-source) — full manual control
|
|
|
|
Then: [Postgres provisioning](#postgres-provisioning),
|
|
[flags reference](#controller-flags), [systemd](#systemd),
|
|
[reverse proxy + TLS](#reverse-proxy--real-tls), [firewall](#firewall-basics).
|
|
|
|
---
|
|
|
|
## Linux one-liner
|
|
|
|
Debian, Ubuntu, Fedora, CentOS/RHEL, Raspbian. Run as root:
|
|
|
|
```bash
|
|
curl -fsSL https://raw.githubusercontent.com/dbledeez/panel/main/install.sh | sudo bash
|
|
```
|
|
|
|
What it does (idempotent — safe to re-run; existing containers, units,
|
|
CA, DB and passwords are kept, binaries and modules refreshed):
|
|
|
|
1. Installs Docker via `get.docker.com` if missing.
|
|
2. Starts a managed `panel-postgres` container (unless you point it at
|
|
an existing DB with `PANEL_DB_URL`).
|
|
3. Builds (or downloads, with `PANEL_PREBUILT_URL`) `controller`,
|
|
`agent`, and `panelctl` into `/opt/panel/bin/`.
|
|
4. Boots the controller once to bootstrap the internal CA and the admin
|
|
account, then pairs the local agent over mTLS.
|
|
5. Installs and enables `panel-controller.service` and
|
|
`panel-agent.service`.
|
|
|
|
Tunables (env vars, all optional — from `install.sh`'s header):
|
|
|
|
| Env var | Meaning | Default |
|
|
|---|---|---|
|
|
| `PANEL_REPO_URL` | git/https base of the panel source | github repo |
|
|
| `PANEL_PREBUILT_URL` | tarball with prebuilt `bin/{controller,agent,panelctl}` + `modules/` (skips the Go build) | — |
|
|
| `PANEL_VERSION` | version string baked into the binaries | `git describe \|\| dev` |
|
|
| `PANEL_DIR` | install prefix | `/opt/panel` |
|
|
| `PANEL_DATA_DIR` | data directory | `$PANEL_DIR/data` |
|
|
| `PANEL_HTTP_PORT` | dashboard port | `8080` |
|
|
| `PANEL_GRPC_PORT` | agent gRPC port | `8443` |
|
|
| `PANEL_ADMIN_PASSWORD` | stable first-boot admin password | generated by the controller |
|
|
| `PANEL_DB_URL` | use an EXISTING Postgres instead of the managed container | — |
|
|
| `PANEL_DB_PASSWORD` | password for the managed `panel-postgres` container | generated |
|
|
| `PANEL_SKIP_AGENT=1` | controller only (add agents on other boxes later — see [PAIRING.md](PAIRING.md)) | `0` |
|
|
|
|
Example — controller-only box on a custom port with a pinned admin password:
|
|
|
|
```bash
|
|
PANEL_SKIP_AGENT=1 PANEL_HTTP_PORT=9090 PANEL_ADMIN_PASSWORD='s3cret' \
|
|
bash install.sh
|
|
```
|
|
|
|
## Windows one-liner
|
|
|
|
PowerShell 5+. Docker Desktop must be installed and **running** first
|
|
(the script checks and refuses otherwise).
|
|
|
|
```powershell
|
|
powershell -ExecutionPolicy Bypass -File install.ps1 -AdminPassword secret123
|
|
```
|
|
|
|
Parameters (mirror the Linux env vars): `-RepoUrl`, `-PrebuiltUrl`,
|
|
`-Version`, `-InstallDir` (default `C:\Panel`), `-HttpPort` (8080),
|
|
`-GrpcPort` (8443), `-AdminPassword`, `-DbUrl`, `-DbPassword`,
|
|
`-SkipAgent`. The same `PANEL_*` env vars are honored as defaults.
|
|
|
|
The controller and agent are registered as **logon-time Scheduled
|
|
Tasks**. For a hardened always-on Windows service, wrap the same command
|
|
lines with [nssm](https://nssm.cc):
|
|
`nssm install panel-controller C:\Panel\bin\controller.exe ...`.
|
|
|
|
## docker-compose
|
|
|
|
A production single-box stack (Postgres + controller + agent) lives at
|
|
`deploy/docker-compose.yml`. From the repo root:
|
|
|
|
```bash
|
|
cp deploy/.env.example deploy/.env # then edit deploy/.env (PANEL_DB_PASSWORD is required)
|
|
docker compose -f deploy/docker-compose.yml --env-file deploy/.env up -d --build
|
|
```
|
|
|
|
First run: watch `docker compose logs controller` for the
|
|
`ADMIN BOOTSTRAP` banner (generated admin password), then mint a pair
|
|
token in the UI and pair the agent — see [PAIRING.md](PAIRING.md).
|
|
|
|
> **Security note (from the compose file itself):** the agent container
|
|
> mounts the **host** Docker socket to manage game-server containers.
|
|
> Docker-socket access is root-equivalent on the host. Run this stack
|
|
> only on a host you fully trust and keep the dashboard behind auth/TLS.
|
|
|
|
## From source
|
|
|
|
Prereqs: Go 1.26+, Docker, [`buf`](https://buf.build/docs/installation/)
|
|
on PATH for proto stubs.
|
|
|
|
```bash
|
|
buf generate # proto stubs (once per clone / .proto change)
|
|
go build -o bin/controller ./controller/cmd/controller
|
|
go build -o bin/agent ./agent/cmd/agent
|
|
go build -o bin/panelctl ./controller/cmd/panelctl
|
|
|
|
# 1) Postgres (dev container) — or see "Postgres provisioning" below
|
|
docker compose -f docker-compose.dev.yml up -d
|
|
|
|
# 2) Controller — first run prints the ADMIN BOOTSTRAP banner
|
|
./bin/controller
|
|
|
|
# 3) Agent (same box, dev): plaintext is fine locally
|
|
./bin/agent --insecure --modules-dir=./modules \
|
|
--data-root=./data/instances --meta-dir=./data/instance-meta \
|
|
--backup-dir=./data/backups
|
|
```
|
|
|
|
For production, pair the agent instead of `--insecure`
|
|
([PAIRING.md](PAIRING.md)) and run both under systemd (below).
|
|
|
|
There is also a root `Makefile` (`make build`, `make test`, `make vet`,
|
|
`make install-local`, ...).
|
|
|
|
---
|
|
|
|
## Postgres provisioning
|
|
|
|
The controller needs Postgres (17 is what the managed container and
|
|
compose stack use; the schema is plain SQL with embedded migrations, run
|
|
automatically at controller start).
|
|
|
|
Bring your own instance:
|
|
|
|
```sql
|
|
CREATE ROLE panel LOGIN PASSWORD 'choose-a-password';
|
|
CREATE DATABASE panel OWNER panel;
|
|
```
|
|
|
|
Point the controller at it via either:
|
|
|
|
- `--db-url 'postgres://panel:...@dbhost:5432/panel?sslmode=disable'`
|
|
- `PANEL_DB_URL` env var (honored when `--db-url` is left at its default)
|
|
|
|
The default (dev) DSN is
|
|
`postgres://panel:panel_dev@127.0.0.1:5432/panel?sslmode=disable`.
|
|
|
|
Or let `install.sh` run the `panel-postgres` container for you.
|
|
|
|
---
|
|
|
|
## Controller flags
|
|
|
|
From `controller/cmd/controller/main.go`:
|
|
|
|
| Flag | Default | Meaning |
|
|
|---|---|---|
|
|
| `-grpc` | `:8443` | gRPC listen address (both Agent + Panel services) |
|
|
| `-http` | `:8080` | HTTP dashboard + REST API listen address |
|
|
| `-ca-dir` | `./data/ca` | directory holding internal CA + server cert (auto-generated on first run) |
|
|
| `-tls` | `auto` | `auto` \| `on` \| `off` — `auto` enables mTLS if CA + server cert present |
|
|
| `-tls-hostnames` | — | comma-separated SAN DNS names to add to the server cert (e.g. `panel.example.com`) |
|
|
| `-log-level` | `info` | `debug`, `info`, `warn`, `error` |
|
|
| `-db-url` | dev DSN | Postgres connection string (env `PANEL_DB_URL` also honored) |
|
|
| `-public-url` | — | public base URL of the dashboard (needed for Steam OpenID sign-in — see [ADMIN.md](ADMIN.md)) |
|
|
| `-initial-admin-steam-id` | — | 17-digit SteamID64 linked to the first admin, enabling Sign in with Steam ([ADMIN.md](ADMIN.md)) |
|
|
|
|
Env vars: `PANEL_DB_URL`, `PANEL_ADMIN_PASSWORD` (first-boot admin
|
|
password preseed).
|
|
|
|
## Agent flags
|
|
|
|
From `agent/cmd/agent/main.go`:
|
|
|
|
| Flag | Default | Meaning |
|
|
|---|---|---|
|
|
| `--controller` | `localhost:8443` | controller gRPC address `host:port` |
|
|
| `--agent-id` | mTLS cert CN, or hostname when `--insecure` | agent identifier |
|
|
| `--modules-dir` | `./modules` | path to modules directory |
|
|
| `--data-root` | `./data/instances` | root directory for instance data |
|
|
| `--meta-dir` | `./data/instance-meta` | per-instance sidecar metadata (survives agent restart) |
|
|
| `--backup-dir` | `./data/backups` | where instance backup archives are written |
|
|
| `--cert-dir` | `./data/certs` | holds `agent.crt` + `agent.key` + `ca.crt` after pairing |
|
|
| `--heartbeat` | `10s` | heartbeat interval |
|
|
| `--insecure` | `false` | force plaintext (no TLS) — dev/local only |
|
|
| `--pair-token` | — | pair mode: fetch CA + signed cert from `--pair-url` and exit ([PAIRING.md](PAIRING.md)) |
|
|
| `--pair-url` | `http://localhost:8080` | controller HTTP URL for the pair endpoints |
|
|
| `--log-level` | `info` | `debug`, `info`, `warn`, `error` |
|
|
|
|
## systemd
|
|
|
|
Reference units live in `deploy/systemd/` (`panel-controller.service`,
|
|
`panel-agent.service`) — `install.sh` installs them for you. Highlights:
|
|
|
|
- Both run as the `panel` user from `WorkingDirectory=/opt/panel`; the
|
|
agent additionally gets `SupplementaryGroups=docker`.
|
|
- `/opt/panel/panel.env` (optional `EnvironmentFile`) overrides
|
|
`PANEL_HTTP_PORT`, `PANEL_GRPC_PORT`, `PANEL_DB_URL`,
|
|
`PANEL_ADMIN_PASSWORD`, ...
|
|
- The controller unit is hardened (`ProtectSystem=strict`,
|
|
`ReadWritePaths=/opt/panel`); the agent's hardening is intentionally
|
|
lighter because it drives the Docker socket.
|
|
|
|
```bash
|
|
sudo cp deploy/systemd/*.service /etc/systemd/system/
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable --now panel-controller panel-agent
|
|
journalctl -u panel-controller -f # ADMIN BOOTSTRAP banner lives here
|
|
```
|
|
|
|
## Reverse proxy + real TLS
|
|
|
|
The dashboard speaks plain HTTP on `:8080`; put a reverse proxy with a
|
|
real certificate in front of it for anything internet-facing. **Only
|
|
proxy the HTTP port** — agents talk to gRPC `:8443` directly (mTLS,
|
|
LAN/VPN is fine; see [PAIRING.md](PAIRING.md)).
|
|
|
|
The dashboard uses SSE (`/api/events`) — disable proxy buffering on
|
|
that path or the live console will lag.
|
|
|
|
### Caddy
|
|
|
|
```
|
|
panel.example.com {
|
|
reverse_proxy 127.0.0.1:8080 {
|
|
flush_interval -1 # SSE: stream immediately
|
|
}
|
|
}
|
|
```
|
|
|
|
That's the whole file — Caddy handles Let's Encrypt automatically.
|
|
|
|
### nginx
|
|
|
|
```nginx
|
|
server {
|
|
listen 443 ssl;
|
|
server_name panel.example.com;
|
|
ssl_certificate /etc/letsencrypt/live/panel.example.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/panel.example.com/privkey.pem;
|
|
|
|
client_max_body_size 512m; # scenario/mod zip uploads
|
|
|
|
location / {
|
|
proxy_pass http://127.0.0.1:8080;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
}
|
|
|
|
location /api/events { # SSE — no buffering
|
|
proxy_pass http://127.0.0.1:8080;
|
|
proxy_set_header Host $host;
|
|
proxy_buffering off;
|
|
proxy_cache off;
|
|
proxy_read_timeout 1h;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Connection "";
|
|
}
|
|
}
|
|
```
|
|
|
|
If the proxy terminates TLS with a public hostname and agents pair
|
|
through it, add that hostname to the controller cert SANs:
|
|
`-tls-hostnames panel.example.com`.
|
|
|
|
## Firewall basics
|
|
|
|
On the **controller** box:
|
|
|
|
| Port | Proto | Who needs it | Expose to internet? |
|
|
|---|---|---|---|
|
|
| 8080 (HTTP) | tcp | browsers, `panelctl admin`, pairing agents | only via the reverse proxy above |
|
|
| 8443 (gRPC) | tcp | agents + `panelctl` | no — LAN/VPN preferred; mTLS protects it if you must |
|
|
| 5432 (Postgres) | tcp | controller only | never |
|
|
|
|
On each **agent** box: no inbound panel ports at all — the agent dials
|
|
out to the controller. You only open the **game ports** of the instances
|
|
it hosts; see [NETWORKING.md](NETWORKING.md) for the per-game table.
|
|
|
|
Example (ufw, controller on a LAN):
|
|
|
|
```bash
|
|
sudo ufw allow from 192.168.1.0/24 to any port 8080 proto tcp
|
|
sudo ufw allow from 192.168.1.0/24 to any port 8443 proto tcp
|
|
```
|