feat(bootstrap): federate LittleSkin by default and let Velocity own its runtime dir
Point Velocity's authlib (mojang.sessionserver) at the felis-api hasJoined multiplexer so a full install federates Mojang plus the configured [[auth_source]] set out of the box, not just the standalone `felis nano`, and ship a default LittleSkin auth_source in the generated felis.toml (delete the block for a Mojang-only server). Velocity now owns velocity.toml and its working tree — it migrates the config version and extracts localizations on start — while the jar and forwarding secret stay root-owned read-only and ReadWritePaths widens to VELOCITY_DIR; the felis-api-internal ClusterIP lookup is factored into a felis_internal_ip helper shared by the link config and the sessionserver override. Re-include plugins/velocity in the docker context because the felis binary now embeds all four plugin trees for `felis bootstrap-assets game-stack`.
This commit is contained in:
2 files changed
+50
-24
No files matched your search
+8
-7
@@ -7,18 +7,19 @@
|
||||
.git
|
||||
.claude
|
||||
**/node_modules
|
||||
# plugins/ is code-only and excluded from the MAIN felis image — EXCEPT the source
|
||||
# trees the two game images compile: the login-limbo image (deploy/limbo/Dockerfile)
|
||||
# builds plugins/limbo, the lobby image (deploy/lobby/Dockerfile) builds plugins/paper,
|
||||
# and both srcDir-include the shared link core (plugins/shared).
|
||||
# The MAIN felis binary //go:embed-s all four plugin source trees (see
|
||||
# bootstrap_asset.go): `felis bootstrap-assets game-stack` emits them as the tar the
|
||||
# limbo/lobby images and the host Velocity install build from. Every plugins/ source
|
||||
# tree the embed names MUST stay in this context or `go build ./cmd/felis` fails with
|
||||
# "pattern plugins/<x>/...: no matching files found".
|
||||
# `plugins/*` excludes each sibling module individually (so plugins/ itself is still
|
||||
# walked and the negations below can re-include), then the build trees are pulled
|
||||
# back in and their Gradle outputs re-excluded to keep the context lean. The main
|
||||
# image copies none of plugins/, so carrying these small source dirs is harmless.
|
||||
# walked and the negations below can re-include), then the embedded source trees are
|
||||
# pulled back in and their Gradle outputs re-excluded to keep the context lean.
|
||||
plugins/*
|
||||
!plugins/limbo
|
||||
!plugins/paper
|
||||
!plugins/shared
|
||||
!plugins/velocity
|
||||
plugins/**/build
|
||||
plugins/**/.gradle
|
||||
docs
|
||||
|
||||
+42
-17
@@ -853,15 +853,17 @@ ensure_velocity_directory() {
|
||||
install -d -o "$owner" -g "$group" -m "$mode" "$path"
|
||||
}
|
||||
|
||||
# Config and executable parents stay root-owned. The proxy can write only runtime
|
||||
# output directories, so it cannot replace a future root-written secret/config path
|
||||
# with a symlink before a bootstrap re-run.
|
||||
# Velocity manages its own working directory: on startup it migrates velocity.toml to
|
||||
# the running config-version, extracts localizations, and creates plugins/bStats — all
|
||||
# fatal or noisy if it cannot write. So the service owns the tree and velocity.toml. The
|
||||
# immutable artifacts (velocity.jar, felis-velocity.jar) and the forwarding secret stay
|
||||
# root-owned and read-only to the proxy; ProtectSystem=strict confines writes to VELOCITY_DIR.
|
||||
prepare_velocity_layout() {
|
||||
id -u "$VELOCITY_USER" >/dev/null 2>&1 \
|
||||
|| useradd --system --home-dir "$VELOCITY_DIR" --shell /usr/sbin/nologin "$VELOCITY_USER"
|
||||
ensure_velocity_directory "$VELOCITY_DIR" 0750 root "$VELOCITY_USER"
|
||||
ensure_velocity_directory "${VELOCITY_DIR}/plugins" 0750 root "$VELOCITY_USER"
|
||||
ensure_velocity_directory "${VELOCITY_DIR}/plugins/felis-link" 0750 root "$VELOCITY_USER"
|
||||
ensure_velocity_directory "$VELOCITY_DIR" 0750 "$VELOCITY_USER" "$VELOCITY_USER"
|
||||
ensure_velocity_directory "${VELOCITY_DIR}/plugins" 0750 "$VELOCITY_USER" "$VELOCITY_USER"
|
||||
ensure_velocity_directory "${VELOCITY_DIR}/plugins/felis-link" 0750 "$VELOCITY_USER" "$VELOCITY_USER"
|
||||
ensure_velocity_directory "${VELOCITY_DIR}/logs" 0750 "$VELOCITY_USER" "$VELOCITY_USER"
|
||||
ensure_velocity_directory "${VELOCITY_DIR}/crash-reports" 0750 "$VELOCITY_USER" "$VELOCITY_USER"
|
||||
}
|
||||
@@ -944,15 +946,23 @@ install_velocity() {
|
||||
configure_velocity_firewall
|
||||
}
|
||||
|
||||
# felis_internal_ip echoes the felis-api-internal Service ClusterIP. Cluster DNS does not
|
||||
# resolve from the host, but a Service ClusterIP DOES route from the node (kube-proxy programs
|
||||
# the host netns) — the same trick the on-node break-glass console uses. The internal face is
|
||||
# deliberately ClusterIP-only: it is service-token authenticated and must never be published on
|
||||
# a node's external IP. Both the felis-link plugin config and the Velocity sessionserver
|
||||
# override (install_velocity_service) point at it, so the lookup lives here once.
|
||||
felis_internal_ip() {
|
||||
local ip
|
||||
ip="$(kube -n "$CONTROL_NS" get svc felis-api-internal -o jsonpath='{.spec.clusterIP}')" \
|
||||
|| die "could not resolve the felis-api-internal ClusterIP"
|
||||
[ -n "$ip" ] || die "felis-api-internal has no ClusterIP"
|
||||
printf '%s' "$ip"
|
||||
}
|
||||
|
||||
write_velocity_config() {
|
||||
local api_ip tmp
|
||||
# Cluster DNS does not resolve from the host, but a Service ClusterIP DOES route from
|
||||
# the node (kube-proxy programs the host netns) — the same trick the on-node break-glass
|
||||
# console uses. The internal face is deliberately ClusterIP-only: it is service-token
|
||||
# authenticated and must never be published on a node's external IP.
|
||||
api_ip="$(kube -n "$CONTROL_NS" get svc felis-api-internal -o jsonpath='{.spec.clusterIP}')" \
|
||||
|| die "could not resolve the felis-api-internal ClusterIP"
|
||||
[ -n "$api_ip" ] || die "felis-api-internal has no ClusterIP"
|
||||
api_ip="$(felis_internal_ip)"
|
||||
|
||||
prepare_velocity_layout
|
||||
tmp="$(mktemp -d)"
|
||||
@@ -1021,13 +1031,19 @@ EOF
|
||||
)
|
||||
|
||||
atomic_install_file "${tmp}/forwarding.secret" "${VELOCITY_DIR}/forwarding.secret" 0640 root "$VELOCITY_USER"
|
||||
atomic_install_file "${tmp}/velocity.toml" "${VELOCITY_DIR}/velocity.toml" 0640 root "$VELOCITY_USER"
|
||||
atomic_install_file "${tmp}/velocity.toml" "${VELOCITY_DIR}/velocity.toml" 0640 "$VELOCITY_USER" "$VELOCITY_USER"
|
||||
atomic_install_file "${tmp}/felis-link.properties" \
|
||||
"${VELOCITY_DIR}/plugins/felis-link/felis-link.properties" 0640 root "$VELOCITY_USER"
|
||||
ok "velocity.toml + forwarding secret + felis-link.properties written (${VELOCITY_DIR})"
|
||||
}
|
||||
|
||||
install_velocity_service() {
|
||||
local api_ip
|
||||
# Point Velocity's authlib (mojang.sessionserver) at the felis-api hasJoined multiplexer so a
|
||||
# full install federates Mojang + the configured [[auth_source]] set (LittleSkin by default)
|
||||
# out of the box — not just the standalone `felis nano`. felis-api enforces the reclaim
|
||||
# blacklist on this route; a loopback nano would bypass it.
|
||||
api_ip="$(felis_internal_ip)"
|
||||
cat > "$VELOCITY_SERVICE" <<EOF
|
||||
[Unit]
|
||||
Description=Felis Velocity proxy (Mojang authentication + modern forwarding)
|
||||
@@ -1039,14 +1055,14 @@ Type=simple
|
||||
User=${VELOCITY_USER}
|
||||
Group=${VELOCITY_USER}
|
||||
WorkingDirectory=${VELOCITY_DIR}
|
||||
ExecStart=${JRE_DIR}/bin/java -Xms512M -Xmx1G -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:+AlwaysPreTouch -jar ${VELOCITY_DIR}/velocity.jar
|
||||
ExecStart=${JRE_DIR}/bin/java -Xms512M -Xmx1G -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:+AlwaysPreTouch -Dmojang.sessionserver=http://${api_ip}:8081/session/minecraft/hasJoined -jar ${VELOCITY_DIR}/velocity.jar
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
ReadWritePaths=${VELOCITY_DIR}/logs ${VELOCITY_DIR}/crash-reports
|
||||
ReadWritePaths=${VELOCITY_DIR}
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1280,7 +1296,7 @@ namespace = "${MINECRAFT_NS}"
|
||||
egress_mode = "${FELIS_EGRESS_MODE}"
|
||||
|
||||
[velocity]
|
||||
# The two always-on system servers `felis setup` provisions. They are built and imported
|
||||
# The two always-on system servers that felis setup provisions. They are built and imported
|
||||
# into k3s by build_game_stack below, so setup never has to be told "build these first".
|
||||
login_image = "${FELIS_LIMBO_IMAGE}"
|
||||
lobby_image = "${FELIS_LOBBY_IMAGE}"
|
||||
@@ -1296,6 +1312,15 @@ local_path = "/var/lib/felis/archives"
|
||||
[auth]
|
||||
admin_hostname = "op.console.${FELIS_ROOT_DOMAIN}"
|
||||
panel_hostname = "console.${FELIS_ROOT_DOMAIN}"
|
||||
|
||||
# Third-party Yggdrasil sources federated by the hasJoined multiplexer. Mojang is
|
||||
# always the code-owned identity anchor (premium-first), prepended in Go; sources here
|
||||
# append as namespace-rewritten guests. Shipping LittleSkin by default lets Mojang and
|
||||
# LittleSkin both log in out of the box. Delete this block for a Mojang-only server.
|
||||
[[auth_source]]
|
||||
tag = "littleskin"
|
||||
prefix = "LS"
|
||||
url = "https://littleskin.cn/api/yggdrasil/sessionserver/session/minecraft/hasJoined"
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user