panel v0.9.2 — open-source game server manager
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
# Panel production compose — copy to deploy/.env and edit.
|
||||
# NOTE: values containing '#' or '$' must be quoted.
|
||||
|
||||
# REQUIRED — Postgres password for the "panel" DB user.
|
||||
PANEL_DB_PASSWORD=change-me
|
||||
|
||||
# Optional — stable admin password for admin@panel.local on first boot.
|
||||
# Leave empty to get a generated one printed in `docker compose logs controller`.
|
||||
PANEL_ADMIN_PASSWORD=
|
||||
|
||||
# Optional — host ports for the dashboard and agent gRPC.
|
||||
PANEL_HTTP_PORT=8080
|
||||
PANEL_GRPC_PORT=8443
|
||||
|
||||
# Optional — image/version tag baked into the binaries (ldflags).
|
||||
PANEL_VERSION=dev
|
||||
|
||||
# Optional — host directory for agent instance data. MUST be an absolute
|
||||
# path that exists on the host; it is mounted at the SAME path inside the
|
||||
# agent container (game-server bind mounts require host==container path).
|
||||
PANEL_AGENT_DATA=/opt/panel/data
|
||||
@@ -0,0 +1,36 @@
|
||||
# Panel agent — production image.
|
||||
# Build from the repo root: docker build -f deploy/Dockerfile.agent .
|
||||
#
|
||||
# ┌─────────────────────────────── SECURITY ───────────────────────────────┐
|
||||
# │ The agent container mounts the HOST Docker socket (/var/run/docker.sock)│
|
||||
# │ to create/manage game-server containers. Access to the docker socket │
|
||||
# │ is equivalent to root on the host. Only run this on a machine you │
|
||||
# │ fully control, and never expose the socket beyond this container. │
|
||||
# └─────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
FROM golang:1.26 AS build
|
||||
ARG VERSION=dev
|
||||
WORKDIR /src
|
||||
COPY . .
|
||||
ENV CGO_ENABLED=0
|
||||
RUN go build -trimpath \
|
||||
-ldflags "-s -w -X github.com/dbledeez/panel/pkg/version.Version=${VERSION}" \
|
||||
-o /out/agent ./agent/cmd/agent
|
||||
|
||||
# Not distroless: game-module install hooks may shell out (tar, unzip).
|
||||
FROM debian:12-slim
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ca-certificates tar unzip xz-utils \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
WORKDIR /opt/panel
|
||||
COPY --from=build /out/agent /opt/panel/bin/agent
|
||||
COPY --from=build /src/modules /opt/panel/modules
|
||||
# Instance data, sidecar metadata, backups, pairing certs — persist this.
|
||||
VOLUME ["/opt/panel/data"]
|
||||
ENTRYPOINT ["/opt/panel/bin/agent"]
|
||||
CMD ["--controller", "controller:8443", \
|
||||
"--modules-dir", "./modules", \
|
||||
"--data-root", "./data/instances", \
|
||||
"--meta-dir", "./data/instance-meta", \
|
||||
"--backup-dir", "./data/backups", \
|
||||
"--cert-dir", "./data/certs"]
|
||||
@@ -0,0 +1,29 @@
|
||||
# Panel controller — production image.
|
||||
# Build from the repo root: docker build -f deploy/Dockerfile.controller .
|
||||
# (the Makefile "docker" target does exactly this, injecting VERSION)
|
||||
|
||||
FROM golang:1.26 AS build
|
||||
ARG VERSION=dev
|
||||
WORKDIR /src
|
||||
# The repo is a Go workspace (go.work) spanning controller/agent/pkg/proto —
|
||||
# copy the whole tree so workspace resolution works.
|
||||
COPY . .
|
||||
ENV CGO_ENABLED=0
|
||||
RUN go build -trimpath \
|
||||
-ldflags "-s -w -X github.com/dbledeez/panel/pkg/version.Version=${VERSION}" \
|
||||
-o /out/controller ./controller/cmd/controller \
|
||||
&& go build -trimpath \
|
||||
-ldflags "-s -w -X github.com/dbledeez/panel/pkg/version.Version=${VERSION}" \
|
||||
-o /out/panelctl ./controller/cmd/panelctl
|
||||
|
||||
FROM gcr.io/distroless/static-debian12:nonroot
|
||||
WORKDIR /opt/panel
|
||||
COPY --from=build /out/controller /opt/panel/bin/controller
|
||||
COPY --from=build /out/panelctl /opt/panel/bin/panelctl
|
||||
# Module manifests: the controller's port allocator reads ./modules.
|
||||
COPY --from=build /src/modules /opt/panel/modules
|
||||
# CA, admin-token, cluster stores — persist this volume.
|
||||
VOLUME ["/opt/panel/data"]
|
||||
EXPOSE 8080 8443
|
||||
ENTRYPOINT ["/opt/panel/bin/controller"]
|
||||
CMD ["-http", ":8080", "-grpc", ":8443", "-ca-dir", "./data/ca"]
|
||||
@@ -0,0 +1,90 @@
|
||||
# Panel — PRODUCTION single-box compose (postgres + controller + agent).
|
||||
# (Dev Postgres-only compose lives at ../docker-compose.dev.yml.)
|
||||
#
|
||||
# Usage, from the repo root:
|
||||
# cp deploy/.env.example deploy/.env # then edit deploy/.env
|
||||
# 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 create a pair token in the UI
|
||||
# (or via POST /api/admin/pair-tokens) — the agent pairs itself on start
|
||||
# only if you exec the pairing step; see README / install.sh for the flow.
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:17-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: panel
|
||||
POSTGRES_PASSWORD: ${PANEL_DB_PASSWORD:?set PANEL_DB_PASSWORD in deploy/.env}
|
||||
POSTGRES_DB: panel
|
||||
volumes:
|
||||
- panel_pgdata:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U panel -d panel"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 10
|
||||
|
||||
controller:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: deploy/Dockerfile.controller
|
||||
args:
|
||||
VERSION: ${PANEL_VERSION:-dev}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
PANEL_DB_URL: postgres://panel:${PANEL_DB_PASSWORD}@postgres:5432/panel?sslmode=disable
|
||||
# Optional: stable first-boot admin password instead of a generated one.
|
||||
PANEL_ADMIN_PASSWORD: ${PANEL_ADMIN_PASSWORD:-}
|
||||
ports:
|
||||
- "${PANEL_HTTP_PORT:-8080}:8080" # dashboard + REST API
|
||||
- "${PANEL_GRPC_PORT:-8443}:8443" # agent gRPC (mTLS)
|
||||
volumes:
|
||||
- panel_controller_data:/opt/panel/data
|
||||
|
||||
agent:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: deploy/Dockerfile.agent
|
||||
args:
|
||||
VERSION: ${PANEL_VERSION:-dev}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- controller
|
||||
# ┌────────────────────────────── SECURITY ──────────────────────────────┐
|
||||
# │ The agent mounts the HOST Docker socket to create and manage game- │
|
||||
# │ server containers. Docker-socket access is ROOT-EQUIVALENT on the │
|
||||
# │ host: anyone who can reach this container's filesystem or process │
|
||||
# │ can own the machine. Run this stack only on a host you fully trust, │
|
||||
# │ keep the panel dashboard behind auth/TLS, and never re-export the │
|
||||
# │ socket from this container. │
|
||||
# └───────────────────────────────────────────────────────────────────────┘
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
# IMPORTANT: the agent passes instance paths to the HOST docker daemon
|
||||
# as bind mounts, so the host path and in-container path MUST match.
|
||||
# Keep both sides of this mapping identical.
|
||||
- ${PANEL_AGENT_DATA:-/opt/panel/data}:${PANEL_AGENT_DATA:-/opt/panel/data}
|
||||
environment:
|
||||
PANEL_GRPC_PORT: "8443"
|
||||
command:
|
||||
- "--controller"
|
||||
- "controller:8443"
|
||||
- "--modules-dir"
|
||||
- "./modules"
|
||||
- "--data-root"
|
||||
- "${PANEL_AGENT_DATA:-/opt/panel/data}/instances"
|
||||
- "--meta-dir"
|
||||
- "${PANEL_AGENT_DATA:-/opt/panel/data}/instance-meta"
|
||||
- "--backup-dir"
|
||||
- "${PANEL_AGENT_DATA:-/opt/panel/data}/backups"
|
||||
- "--cert-dir"
|
||||
- "${PANEL_AGENT_DATA:-/opt/panel/data}/certs"
|
||||
|
||||
volumes:
|
||||
panel_pgdata:
|
||||
panel_controller_data:
|
||||
@@ -0,0 +1,45 @@
|
||||
# REFERENCE ONLY — install.sh GENERATES the real unit at install time,
|
||||
# substituting the actual PANEL_DIR/ports/user (see write_unit in install.sh).
|
||||
# This static copy assumes the /opt/panel defaults; edit it by hand if you wire
|
||||
# the units up yourself (e.g. via compose) instead of running install.sh.
|
||||
[Unit]
|
||||
Description=Panel agent (Docker runtime for game-server instances)
|
||||
Documentation=https://github.com/dbledeez/panel
|
||||
After=network-online.target docker.service panel-controller.service
|
||||
Wants=network-online.target
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
# The agent user MUST be able to talk to the Docker daemon
|
||||
# (member of the "docker" group, or root). install.sh handles this.
|
||||
User=panel
|
||||
Group=panel
|
||||
SupplementaryGroups=docker
|
||||
WorkingDirectory=/opt/panel
|
||||
# Defaults first; /opt/panel/panel.env overrides (applied later).
|
||||
Environment=PANEL_GRPC_PORT=8443
|
||||
EnvironmentFile=-/opt/panel/panel.env
|
||||
# Pairing (one-time cert issuance) is done by install.sh / the operator:
|
||||
# /opt/panel/bin/agent --pair-url http://localhost:8080 --pair-token <token> --cert-dir ./data/certs
|
||||
ExecStart=/opt/panel/bin/agent \
|
||||
--controller localhost:${PANEL_GRPC_PORT} \
|
||||
--modules-dir ./modules \
|
||||
--data-root ./data/instances \
|
||||
--meta-dir ./data/instance-meta \
|
||||
--backup-dir ./data/backups \
|
||||
--cert-dir ./data/certs
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
|
||||
# Hardening is intentionally lighter than the controller's: the agent
|
||||
# manages Docker containers and bind-mounts instance data, so it needs
|
||||
# the docker socket and its data tree.
|
||||
NoNewPrivileges=true
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/opt/panel
|
||||
PrivateTmp=true
|
||||
LockPersonality=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,45 @@
|
||||
# REFERENCE ONLY — install.sh GENERATES the real unit at install time,
|
||||
# substituting the actual PANEL_DIR/ports/user (see write_unit in install.sh).
|
||||
# This static copy assumes the /opt/panel defaults; edit it by hand if you wire
|
||||
# the units up yourself (e.g. via compose) instead of running install.sh.
|
||||
[Unit]
|
||||
Description=Panel controller (HTTP dashboard :8080 + agent gRPC :8443)
|
||||
Documentation=https://github.com/dbledeez/panel
|
||||
After=network-online.target docker.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
# install.sh creates this user; change to taste. The controller does NOT
|
||||
# need docker access — only the agent does.
|
||||
User=panel
|
||||
Group=panel
|
||||
# The controller resolves ./data (CA, admin-token, cluster stores) and
|
||||
# ./modules relative to its working directory — keep them together here.
|
||||
WorkingDirectory=/opt/panel
|
||||
# Defaults first; /opt/panel/panel.env (PANEL_DB_URL, PANEL_HTTP_PORT,
|
||||
# PANEL_ADMIN_PASSWORD, ...) overrides them because it is applied later.
|
||||
Environment=PANEL_HTTP_PORT=8080
|
||||
Environment=PANEL_GRPC_PORT=8443
|
||||
EnvironmentFile=-/opt/panel/panel.env
|
||||
ExecStart=/opt/panel/bin/controller \
|
||||
-http :${PANEL_HTTP_PORT} \
|
||||
-grpc :${PANEL_GRPC_PORT} \
|
||||
-ca-dir ./data/ca
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
|
||||
# Hardening — the controller only needs its own tree + the network.
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/opt/panel
|
||||
PrivateTmp=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictSUIDSGID=true
|
||||
LockPersonality=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user