diff --git a/.env.example b/.env.example index 2e9ff10..cde8111 100644 --- a/.env.example +++ b/.env.example @@ -37,7 +37,7 @@ DISTRIBUTION_WEB_ROOT=/home/oier/projects/mc-mods/ulicraft-group/ulicraft-distri # Only needed to issue real TLS certs. DNS-01 needs no inbound ports. LE_EMAIL=you@example.com # space-separated subdomains; apex ${BASE_DOMAIN} is always included -LE_SUBDOMAINS=auth pack distribution www +LE_SUBDOMAINS=auth pack distribution www avatar # OVH API creds (DNS zone write). Create at https://eu.api.ovh.com/createToken/ # OVH_CK can be blank on first run — acme.sh prints an auth URL, then paste it. OVH_END_POINT=ovh-eu diff --git a/.gitignore b/.gitignore index 621410a..599fa66 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ runtime/authlib-injector.jar # rendered configs (from tooling/render-config.sh) drasl/config/config.toml +nmsr/config.toml dnsmasq/dnsmasq.conf # packwiz pack files rendered from pack/**/*.tmpl by tooling/render-pack.sh # (domain-dependent build output); source of truth is the .tmpl + custom/ jars. diff --git a/caddy/conf.d/40-avatar.caddy b/caddy/conf.d/40-avatar.caddy new file mode 100644 index 0000000..078ee29 --- /dev/null +++ b/caddy/conf.d/40-avatar.caddy @@ -0,0 +1,5 @@ +# Avatar toggle — NMSR skin/avatar renderer (docker-compose.nmsr.yml). +# Mounted only when the nmsr override is present. +http://avatar.{$BASE_DOMAIN} { + reverse_proxy nmsr:8080 +} diff --git a/docker-compose.nmsr.yml b/docker-compose.nmsr.yml new file mode 100644 index 0000000..2f7bdb4 --- /dev/null +++ b/docker-compose.nmsr.yml @@ -0,0 +1,32 @@ +# ────────────────────────────────────────────────────────────────── +# NMSR-aas — avatar/skin renderer at avatar.${BASE_DOMAIN}. +# ────────────────────────────────────────────────────────────────── +# docker compose -f docker-compose.yml -f docker-compose.caddy.yml \ +# -f docker-compose.nmsr.yml up -d --build +# +# Built from source (no upstream image) — see docker/nmsr/Dockerfile. +# Renders YOUR players' skins by pulling profiles from Drasl over the internal +# mcnet network (config: nmsr/config.toml, rendered from .tmpl). +# +# Caddy reverse-proxies avatar.${BASE_DOMAIN} -> nmsr:8080 internally. The host +# port is published on 127.0.0.1:9898 ONLY (local debugging / not reachable from +# outside) — public access goes through nginx -> caddy. +services: + # Mount the avatar vhost snippet into caddy (the caddy override owns the base + # config; this adds one more conf.d file like the distribution override does). + caddy: + volumes: + - ./caddy/conf.d/40-avatar.caddy:/etc/caddy/conf.d/40-avatar.caddy:ro + + nmsr: + build: + context: ./docker/nmsr + image: ulicraft/nmsr:local + container_name: nmsr + restart: unless-stopped + ports: + - "127.0.0.1:9898:8080" + volumes: + - ./nmsr/config.toml:/nmsr/config.toml:ro + networks: + - mcnet diff --git a/docker/nmsr/Dockerfile b/docker/nmsr/Dockerfile new file mode 100644 index 0000000..6c9f63b --- /dev/null +++ b/docker/nmsr/Dockerfile @@ -0,0 +1,33 @@ +# NMSR-as-a-Service (https://github.com/NickAcPT/nmsr-rs) — built from source. +# Upstream ships no published image, so we vendor a pinned build. Adapted from +# the upstream Dockerfile but: pinned to a commit (reproducible), and the config +# is bind-mounted at runtime (not COPYed) so it can change without a rebuild. +# +# Pin — bump deliberately: +ARG NMSR_REF=948ba4bc027b + +FROM rust:slim-bookworm AS builder +ARG NMSR_REF +WORKDIR /tmp +RUN apt-get update -y \ + && apt-get --no-install-recommends install -y git libssl-dev pkg-config ca-certificates +RUN git clone https://github.com/NickAcPT/nmsr-rs/ +WORKDIR /tmp/nmsr-rs +RUN git checkout "${NMSR_REF}" +# target-cpu=native: this image is built on the host that runs it (self-host). +RUN RUSTFLAGS="-Ctarget-cpu=native" \ + cargo build --release --bin nmsr-aas --features ears --package nmsr-aas + +FROM rust:slim-bookworm +# mesa-vulkan-drivers provides lavapipe (software Vulkan) for headless rendering. +RUN apt-get update -y \ + && apt-get --no-install-recommends install -y mesa-vulkan-drivers ca-certificates +WORKDIR /nmsr +COPY --from=builder /tmp/nmsr-rs/target/release/nmsr-aas /nmsr/nmsr-aas +ENV NMSR_USE_SMAA=1 \ + NMSR_SAMPLE_COUNT=1 \ + WGPU_BACKEND=vulkan \ + RUST_BACKTRACE=1 +EXPOSE 8080 +# config.toml is bind-mounted by docker-compose.nmsr.yml. +CMD ["/nmsr/nmsr-aas", "-c", "/nmsr/config.toml"] diff --git a/nginx/ulicraft-caddy.conf.tmpl b/nginx/ulicraft-caddy.conf.tmpl index 8360242..9b81950 100644 --- a/nginx/ulicraft-caddy.conf.tmpl +++ b/nginx/ulicraft-caddy.conf.tmpl @@ -32,7 +32,7 @@ upstream ulicraft_caddy { server { listen 80; listen [::]:80; - server_name ${BASE_DOMAIN} www.${BASE_DOMAIN} auth.${BASE_DOMAIN} pack.${BASE_DOMAIN} distribution.${BASE_DOMAIN}; + server_name ${BASE_DOMAIN} www.${BASE_DOMAIN} auth.${BASE_DOMAIN} pack.${BASE_DOMAIN} distribution.${BASE_DOMAIN} avatar.${BASE_DOMAIN}; return 301 https://$host$request_uri; } @@ -127,3 +127,21 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } } + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name avatar.${BASE_DOMAIN}; + + ssl_certificate ${APP_DIR}/certs/avatar.${BASE_DOMAIN}/cert.pem; + ssl_certificate_key ${APP_DIR}/certs/avatar.${BASE_DOMAIN}/key.pem; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + location / { + proxy_pass http://ulicraft_caddy; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} diff --git a/nmsr/config.toml.tmpl b/nmsr/config.toml.tmpl new file mode 100644 index 0000000..6dd7d79 --- /dev/null +++ b/nmsr/config.toml.tmpl @@ -0,0 +1,37 @@ +# NMSR-aas config — renders to nmsr/config.toml (gitignored) via +# tooling/render-config.sh. Only ${BASE_DOMAIN} is substituted. +# +# Skins come from Drasl, NOT Mojang. Drasl exposes Mojang-compatible routes at +# the bare BaseURL: +# session_server -> {url}/session/minecraft/profile/{uuid} +# mojang_api_server -> {url}/users/profiles/minecraft/{name} +# and embeds absolute skin URLs (http://auth.${BASE_DOMAIN}/web/texture/skin/…) +# in the profile, which NMSR fetches directly (textures_server is only a +# fallback). NMSR resolves auth.${BASE_DOMAIN} internally over the docker +# network (caddy holds the mcnet alias → drasl), so no public round-trip. + +[server] +address = "0.0.0.0" +port = 8080 + +[caching] +cleanup_interval = "1h" +resolve_cache_duration = "15m" +texture_cache_duration = "48h" + +[caching.cache_biases] + +[mojank] +# Point every Mojang endpoint at Drasl (internal, plain http via the caddy alias). +session_server = "http://auth.${BASE_DOMAIN}" +textures_server = "http://auth.${BASE_DOMAIN}" +mojang_api_server = "http://auth.${BASE_DOMAIN}" +session_server_rate_limit = 10 +# This is an offline-mode (authlib-injector) server — Drasl issues v3 offline +# UUIDs, so they must be accepted. +allow_offline_mode_uuids = true +use_dashless_uuids = false + +[rendering] +sample_count = 1 +use_smaa = true diff --git a/tooling/issue-letsencrypt.sh b/tooling/issue-letsencrypt.sh index c2e2900..01b4933 100755 --- a/tooling/issue-letsencrypt.sh +++ b/tooling/issue-letsencrypt.sh @@ -17,7 +17,7 @@ # OVH_CK OVH consumer key (obtained on first run — see below) # OVH_END_POINT OVH API endpoint (e.g. ovh-eu) # optional: -# LE_SUBDOMAINS space-separated (default: "auth pack distribution www") +# LE_SUBDOMAINS space-separated (default: "auth pack distribution www avatar") # LE_STAGING=1 use the LE staging CA (untrusted, for dry runs) # # First-time OVH consumer key: set OVH_AK/OVH_AS/OVH_END_POINT, leave OVH_CK @@ -53,7 +53,7 @@ ACME="${ACME_SH:-$HOME/.acme.sh/acme.sh}" then re-run (or set ACME_SH=/path/to/acme.sh)." CERT_DIR="${CERT_DIR:-certs}" -read -r -a SUBDOMAINS <<< "${LE_SUBDOMAINS:-auth pack distribution www}" +read -r -a SUBDOMAINS <<< "${LE_SUBDOMAINS:-auth pack distribution www avatar}" if [ "${LE_STAGING:-0}" = "1" ]; then SERVER="letsencrypt_test"; warn "STAGING mode — certs will NOT be trusted by browsers" diff --git a/tooling/render-config.sh b/tooling/render-config.sh index 62163f9..f249560 100755 --- a/tooling/render-config.sh +++ b/tooling/render-config.sh @@ -18,6 +18,7 @@ render() { # $1=template $2=output } render drasl/config/config.toml.tmpl drasl/config/config.toml +render nmsr/config.toml.tmpl nmsr/config.toml render dnsmasq/dnsmasq.conf.tmpl dnsmasq/dnsmasq.conf # pack templates (mods/*.pw.toml.tmpl, CustomSkinLoader, …) + packwiz index.