From 5172316919b9da776d739abd49ee1dde203d1f99 Mon Sep 17 00:00:00 2001 From: Oier Bravo Urtasun Date: Sun, 7 Jun 2026 18:48:54 +0200 Subject: [PATCH] feat(auth): Blessing Skin variant as Drasl alternative Override stack that swaps Drasl for Blessing Skin Server + MariaDB, talking to Minecraft via the yggdrasil-api plugin (authlib endpoint /api/yggdrasil, ONLINE_MODE=TRUE). Caddy auth.* repointed to blessing-skin:80; Drasl left idle (compose merges depends_on). Run: docker compose -f docker-compose.yml -f docker-compose.blessingskin.yml up -d Co-Authored-By: Claude Opus 4.8 (1M context) --- .env.example | 10 +++ caddy/conf.d/00-core-blessingskin.caddy | 14 ++++ docker-compose.blessingskin.yml | 103 ++++++++++++++++++++++++ plan/03b-blessing-skin.md | 80 ++++++++++++++++++ 4 files changed, 207 insertions(+) create mode 100644 caddy/conf.d/00-core-blessingskin.caddy create mode 100644 docker-compose.blessingskin.yml create mode 100644 plan/03b-blessing-skin.md diff --git a/.env.example b/.env.example index 71d3531..06b8112 100644 --- a/.env.example +++ b/.env.example @@ -17,3 +17,13 @@ ENABLE_MDNS=false RCON_PASSWORD=change-me-to-something-random # Only needed if using CurseForge-exclusive mods: # CF_API_KEY=your-curseforge-api-key + +# ── Blessing Skin auth variant (docker-compose.blessingskin.yml) ────── +# Only needed when running the Blessing Skin override instead of Drasl. +BS_DB_NAME=blessingskin +BS_DB_USER=blessingskin +BS_DB_PASSWORD=change-me-bs-db +BS_DB_ROOT_PASSWORD=change-me-bs-root +# Generate once and paste here so it survives container recreates: +# docker run --rm azusamikan/blessing-skin-server-docker:latest php artisan key:generate --show +BS_APP_KEY=base64:replace-with-generated-key diff --git a/caddy/conf.d/00-core-blessingskin.caddy b/caddy/conf.d/00-core-blessingskin.caddy new file mode 100644 index 0000000..9b363bd --- /dev/null +++ b/caddy/conf.d/00-core-blessingskin.caddy @@ -0,0 +1,14 @@ +# Core ingress — Blessing Skin variant. Replaces 00-core.caddy when the +# docker-compose.blessingskin.yml override is in play (mounted at the same +# target path). auth.* now points at Blessing Skin instead of Drasl. +# +# Blessing Skin serves BOTH its web UI (/) and the Yggdrasil API (/api/yggdrasil) +# on the same vhost, so a single reverse_proxy covers everything. +http://auth.{$BASE_DOMAIN} { + reverse_proxy blessing-skin:80 +} + +http://pack.{$BASE_DOMAIN} { + root * /srv/pack + file_server browse +} diff --git a/docker-compose.blessingskin.yml b/docker-compose.blessingskin.yml new file mode 100644 index 0000000..4b52c4b --- /dev/null +++ b/docker-compose.blessingskin.yml @@ -0,0 +1,103 @@ +# ────────────────────────────────────────────────────────────────── +# Blessing Skin auth variant — use INSTEAD of Drasl. +# ────────────────────────────────────────────────────────────────── +# Layer this over the base file: +# +# docker compose -f docker-compose.yml -f docker-compose.blessingskin.yml up -d +# +# What it does vs the base stack: +# - drasl → left running but UNUSED + unreachable (Caddy no longer +# routes to it; it has no host port). Compose merges +# depends_on, so it can't be removed from an override — +# idling it is the clean option. `docker compose ... stop +# drasl` after up if you want it down. +# - caddy → auth.* now reverse-proxies blessing-skin:80 +# - mariadb → new; Blessing Skin's datastore (no SQLite support) +# - blessing-skin → new; PHP skin server + yggdrasil-api plugin +# - minecraft → authlib-injector now points at /api/yggdrasil, +# ONLINE_MODE=TRUE (real session validation against BSS) +# +# First-run is a WEB WIZARD — see plan/03b-blessing-skin.md. The yggdrasil-api +# plugin must be installed + enabled from the BSS admin panel before the +# /api/yggdrasil endpoint (and thus Minecraft login) works. +# +# DB creds + APP_KEY come from .env (see .env.example: BS_* vars). +# ────────────────────────────────────────────────────────────────── + +services: + caddy: + # Swap the core ingress conf for the Blessing Skin one (same mount target, + # so it shadows the base 00-core.caddy). volumes is a full replacement here. + volumes: + - ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro + - ./caddy/conf.d/00-core-blessingskin.caddy:/etc/caddy/conf.d/00-core.caddy:ro + - ./pack:/srv/pack:ro + depends_on: + - blessing-skin + + mariadb: + image: mariadb:11 + container_name: mariadb + restart: unless-stopped + environment: + MARIADB_DATABASE: ${BS_DB_NAME} + MARIADB_USER: ${BS_DB_USER} + MARIADB_PASSWORD: ${BS_DB_PASSWORD} + MARIADB_ROOT_PASSWORD: ${BS_DB_ROOT_PASSWORD} + TZ: "Europe/Madrid" + volumes: + - bs_db:/var/lib/mysql + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 10s + timeout: 5s + retries: 10 + networks: + - mcnet + + blessing-skin: + image: azusamikan/blessing-skin-server-docker:latest + container_name: blessing-skin + restart: unless-stopped + # Internal-only — reached via Caddy at auth.${BASE_DOMAIN}. + environment: + APP_URL: "http://auth.${BASE_DOMAIN}" + # Persist APP_KEY across recreates (else sessions/encryption break). + # Generate once: docker run --rm azusamikan/blessing-skin-server-docker:latest php artisan key:generate --show + APP_KEY: ${BS_APP_KEY} + DB_DRIVER: "mysql" + DB_HOST: "mariadb" + DB_PORT: "3306" + DB_DATABASE: ${BS_DB_NAME} + DB_USERNAME: ${BS_DB_USER} + DB_PASSWORD: ${BS_DB_PASSWORD} + TZ: "Europe/Madrid" + volumes: + - bs_storage:/app/storage # uploaded skins/capes + textures + - bs_plugins:/app/plugins # yggdrasil-api plugin lives here + depends_on: + mariadb: + condition: service_healthy + networks: + - mcnet + + minecraft: + environment: + # authlib-injector API root for Blessing Skin's yggdrasil-api plugin + # is /api/yggdrasil (NOT /authlib-injector like Drasl). + JVM_OPTS: "-javaagent:/extras/authlib-injector.jar=http://auth.${BASE_DOMAIN}/api/yggdrasil" + # authlib-injector needs real auth: online-mode TRUE so the server + # validates sessions against Blessing Skin. (Drasl variant used FALSE.) + ONLINE_MODE: "TRUE" + # Keep FALSE for safety on 1.21+. The yggdrasil-api plugin DOES sign + # profile keys, so you MAY flip this to TRUE if signed chat is wanted. + ENFORCE_SECURE_PROFILE: "FALSE" + # Merged with the base depends_on (drasl, caddy); just adds blessing-skin so + # Minecraft starts after the skin server is up. + depends_on: + - blessing-skin + +volumes: + bs_db: + bs_storage: + bs_plugins: diff --git a/plan/03b-blessing-skin.md b/plan/03b-blessing-skin.md new file mode 100644 index 0000000..4942e91 --- /dev/null +++ b/plan/03b-blessing-skin.md @@ -0,0 +1,80 @@ +# Blessing Skin — auth + skin server (Drasl alternative) + +## Summary + +Drop-in alternative to Drasl (`plan/03-drasl.md`). Blessing Skin Server (BSS) is +a PHP skin station; the **yggdrasil-api plugin** gives it a Yggdrasil API that +authlib-injector talks to — same end result as Drasl, different internals. + +Run it as an override **instead of** Drasl: + +``` +docker compose -f docker-compose.yml -f docker-compose.blessingskin.yml up -d +``` + +The override (`docker-compose.blessingskin.yml`) repoints Caddy's `auth.*` vhost +at `blessing-skin:80`, adds `mariadb` + `blessing-skin`, and switches the +Minecraft authlib endpoint to `/api/yggdrasil`. Drasl stays defined but idle and +unreachable (compose merges `depends_on`, so an override can't remove it); +`docker compose … stop drasl` after up if you want it down. + +## Drasl vs Blessing Skin — what changes + +| | Drasl | Blessing Skin | +|---|---|---| +| Backend | single Go binary | PHP app + **MariaDB** (no SQLite) | +| Config | rendered `config.toml` | **web install wizard** + DB | +| Yggdrasil API | built in | **yggdrasil-api plugin** (install + enable) | +| authlib endpoint | `…/authlib-injector` | `…/api/yggdrasil` | +| OIDC/Keycloak | supported (future) | not native (OAuth plugins exist) | +| Server online-mode | repo used FALSE | **TRUE** (real session validation) | + +## Setup (first run) + +1. `cp .env.example .env`, fill the `BS_*` vars. Generate `BS_APP_KEY`: + ``` + docker run --rm azusamikan/blessing-skin-server-docker:latest \ + php artisan key:generate --show + ``` +2. Bring the stack up with the override (command above). +3. Open `http://auth.${BASE_DOMAIN}` → BSS install wizard. DB host = `mariadb`, + port `3306`, name/user/pass = the `BS_*` values. Create the admin account. +4. Admin panel → **Plugins → Plugin Market** → install **yggdrasil-api** → + enable it. (Needs internet; for air-gap, pre-place the plugin jar/zip into + the `bs_plugins` volume.) After enabling, the API root is live at + `http://auth.${BASE_DOMAIN}/api/yggdrasil`. +5. Each player: register on the BSS site, add a character whose name == their + in-game player name, upload a skin. +6. Restart minecraft if it came up before the plugin was enabled. + +## Client (Prism / authlib-injector) + +- authlib-injector URL: `http://auth.${BASE_DOMAIN}/api/yggdrasil` +- Login = BSS **email + password** (BSS uses email as the account id). +- Must match the server's `-javaagent` arg exactly (same host + path). + +## Gotchas + +- **`/api/yggdrasil`, not `/authlib-injector`.** Wrong path → silent "Invalid + session". Server `JVM_OPTS` and every client must agree. +- **ONLINE_MODE=TRUE.** authlib-injector only authenticates when online-mode is + on; the server then validates the join against BSS. (The Drasl variant in this + repo set FALSE — do not copy that here.) +- **Secure profile (1.21+).** Override ships `ENFORCE_SECURE_PROFILE=FALSE` for + safety. yggdrasil-api *does* sign profile keys, so TRUE may work — flip and + test if signed chat matters; revert on "Invalid signature". +- **APP_KEY must persist.** Set `BS_APP_KEY` in `.env`. Losing it invalidates + all sessions and breaks encrypted data. +- **DB is the source of truth.** `bs_db` volume holds accounts; `bs_storage` + holds skins; `bs_plugins` holds the yggdrasil-api plugin. Back these up. +- **Air-gap:** the plugin market needs internet. Install + enable yggdrasil-api + while online (it persists in `bs_plugins`), or stage the plugin manually. + +## Image note + +`azusamikan/blessing-skin-server-docker:latest` is a community image and the +env-var names (`DB_*`, `APP_KEY`) may differ slightly by tag — if auto-config +doesn't take, the web wizard is the reliable path. Official source: +https://github.com/bs-community/blessing-skin-server , +plugin: https://github.com/bs-community/yggdrasil-api , authlib-injector setup: +https://github.com/bs-community/blessing-skin-manual/blob/master/man/yggdrasil-api/authlib-injector.md