The roles had drifted far from production: Forgejo was pinned at 12 while 15.0.11 runs, its config lacked the mailer, packages and the real database path, and several services existed only on the hosts. Running a role would have rolled production back. Take the servers as the truth and make the repository match them. site.yaml now holds the desired state of both hosts: - base: unattended upgrades, fail2ban, Docker daemon config and the weekly image prune, as found on the hosts. New: a 500M journal cap, and the Docker systemd override is kept absent. That override once opened dockerd on tcp/2375 without authentication. - traefik: templates as deployed, version in a variable (v3.7.18). The Coraza WAF files are removed; no router ever used them. - forgejo: 15.0.11, app.ini generated from the live file with the secrets taken from the vault. - crawler_alert, sillytavern, minecraft, minecraft_fossils: added from the hosts. Forgejo's canonical name becomes git.asxp.io; git.czsk.it stays as an alias on the same router. docker_service restarts a stack when its config changes, no longer forces ownership on existing data directories, and hides template diffs because they carry secrets. Containers listed in service_graceful_stop are saved and stopped with a long timeout before compose recreates them: Docker's 10 s default is too short for a Minecraft world. Both game servers get stop_grace_period for the same reason. Removed: the full-deployment and runner playbooks with their prompts, the Forgejo backup and restore roles for the old bare-metal host, and inventory entries for services that were never deployed. SSH hardening stays a manual playbook, since a mistake there locks everyone out. New secrets (SMTP, RCON, CurseForge key) went into the vault. Assisted-by: Claude:fable-5-2
87 lines
2.7 KiB
YAML
87 lines
2.7 KiB
YAML
services:
|
|
minecraft-fossils:
|
|
image: itzg/minecraft-server:java21
|
|
container_name: minecraft-fossils
|
|
restart: unless-stopped
|
|
stop_grace_period: 2m
|
|
stdin_open: true
|
|
tty: true
|
|
ports:
|
|
- "25566:25565"
|
|
env_file:
|
|
- secrets.env
|
|
environment:
|
|
EULA: "TRUE"
|
|
TYPE: FABRIC
|
|
VERSION: "1.20.1"
|
|
FABRIC_LOADER_VERSION: "0.18.4"
|
|
MEMORY: "8G"
|
|
ONLINE_MODE: "FALSE"
|
|
ENFORCE_WHITELIST: "TRUE"
|
|
ENFORCE_SECURE_PROFILE: "FALSE"
|
|
WHITELIST_FILE: /data/whitelist.json
|
|
OPS_FILE: /data/ops.json
|
|
ENABLE_RCON: "TRUE"
|
|
MOTD: "[mc.czsk.dev] Fossils Archaeology Revival: Genesis"
|
|
USE_AIKAR_FLAGS: "TRUE"
|
|
volumes:
|
|
- /opt/minecraft-fossils/data:/data
|
|
networks:
|
|
- fossils
|
|
|
|
whitelist-sync:
|
|
image: alpine:3
|
|
container_name: minecraft-fossils-wlsync
|
|
restart: unless-stopped
|
|
depends_on:
|
|
- minecraft-fossils
|
|
volumes:
|
|
- /opt/minecraft:/src:ro
|
|
- /opt/minecraft-fossils/data:/dst
|
|
env_file:
|
|
- secrets.env
|
|
environment:
|
|
RCON_HOST: minecraft-fossils
|
|
INTERVAL: "15"
|
|
command:
|
|
- sh
|
|
- -c
|
|
- |
|
|
apk add --no-cache python3 >/dev/null
|
|
cat > /rcon.py <<'PY'
|
|
import socket, struct, sys, os
|
|
host, port, pw, cmd = os.environ["RCON_HOST"], 25575, os.environ["RCON_PASSWORD"], sys.argv[1]
|
|
def pkt(i, t, b): p = struct.pack("<ii", i, t) + b.encode() + b"\x00\x00"; return struct.pack("<i", len(p)) + p
|
|
def recv(s):
|
|
ln = struct.unpack("<i", s.recv(4))[0]; d = b""
|
|
while len(d) < ln: d += s.recv(ln - len(d))
|
|
return struct.unpack("<ii", d[:8])[0], d[8:-2].decode(errors="replace")
|
|
s = socket.create_connection((host, port), timeout=5)
|
|
s.send(pkt(1, 3, pw))
|
|
rid, _ = recv(s)
|
|
if rid == -1: print("auth failed"); sys.exit(1)
|
|
s.send(pkt(2, 2, cmd))
|
|
_, out = recv(s); print(out)
|
|
PY
|
|
echo "[wlsync] started, interval=$${INTERVAL}s"
|
|
while true; do
|
|
for f in whitelist.json ops.json banned-players.json banned-ips.json; do
|
|
if [ -f /src/$$f ] && ! cmp -s /src/$$f /dst/$$f 2>/dev/null; then
|
|
cp /src/$$f /dst/$$f
|
|
echo "[wlsync] copied $$f"
|
|
case $$f in
|
|
whitelist.json) CMD="whitelist reload" ;;
|
|
ops.json) CMD="reload" ;;
|
|
banned-players.json|banned-ips.json) CMD="kick @a Ban list updated" ;;
|
|
esac
|
|
python3 /rcon.py "$$CMD" 2>&1 | sed 's/^/[wlsync] rcon: /' || echo "[wlsync] rcon failed"
|
|
fi
|
|
done
|
|
sleep $$INTERVAL
|
|
done
|
|
networks:
|
|
- fossils
|
|
|
|
networks:
|
|
fossils:
|
|
driver: bridge
|