Replace patchelf crack with Freeloader LD_PRELOAD approach
- Multi-stage Dockerfile: discover patterns from PMS binary (capstone), compile .so with zig (musl), layer onto lscr.io/linuxserver/plex - Uses LD_PRELOAD instead of patchelf (which corrupts Plex's musl loader) - Auto-discovery: broad structural patterns with string-anchored fallback (//feature) and relationship-based fallback (BITSET_REF within BS_INIT) - hook.cpp uses __has_include for generated patterns with hardcoded fallbacks - Custom wrapper.sh (no traffic_logger preload) - Vendored Freeloader source (github.com/authrequest/Freeloader, AGPL-3.0) - Removed stale plexmediaserver_crack.so binary - Supports Plex 1.43.3+ (verified against 1.43.2 and 1.43.3)
This commit is contained in:
1 parent
4399a8288d
commit
72f4661bdc
72 files changed
+77927
-17
No files matched your search
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
#
|
||||
# headscale-server-setup.sh -- OPTIONAL self-hosted coordination server.
|
||||
#
|
||||
# Use instead of Tailscale's control plane when you want no account limits and
|
||||
# full control over who may join. Run on a PUBLIC Debian 12+/Ubuntu 22.04+ VPS
|
||||
# with a DNS name pointing at it. It:
|
||||
# 1. installs the official Headscale .deb (latest release, or --version)
|
||||
# 2. points server_url at https://<domain> and enables built-in Let's Encrypt
|
||||
# TLS (unless --no-tls, for running behind your own reverse proxy)
|
||||
# 3. starts the systemd service
|
||||
# 4. creates a user and mints a reusable pre-auth key
|
||||
#
|
||||
# The Plex host and every client then join with:
|
||||
# sudo tailscale up --login-server https://<domain> --authkey <preauthkey>
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=lib/common.sh
|
||||
source "${SCRIPT_DIR}/lib/common.sh" || { echo "missing ${SCRIPT_DIR}/lib/common.sh" >&2; exit 1; }
|
||||
enable_error_trap
|
||||
|
||||
readonly CFG="/etc/headscale/config.yaml"
|
||||
DOMAIN=""
|
||||
USER_NAME="plex"
|
||||
VERSION="" # auto-detect latest if empty
|
||||
EXPIRY="720h" # preauth key lifetime (30 days)
|
||||
USE_TLS=1
|
||||
readonly LISTEN_PLAIN="127.0.0.1:8080"
|
||||
PREAUTH_KEY=""
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: sudo $0 --domain hs.example.com [options]
|
||||
|
||||
--domain NAME Public DNS name for this Headscale server (required).
|
||||
--user NAME Headscale user to create (default: $USER_NAME).
|
||||
--version VER Headscale version (default: latest GitHub release).
|
||||
--expiration DUR Pre-auth key lifetime, Go duration (default: $EXPIRY).
|
||||
--no-tls Listen on $LISTEN_PLAIN for a reverse proxy (no built-in TLS).
|
||||
--dry-run Print actions without changing anything.
|
||||
-h, --help This help.
|
||||
EOF
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--domain) DOMAIN="$2"; shift 2;;
|
||||
--user) USER_NAME="$2"; shift 2;;
|
||||
--version) VERSION="$2"; shift 2;;
|
||||
--expiration) EXPIRY="$2"; shift 2;;
|
||||
--no-tls) USE_TLS=0; shift;;
|
||||
--dry-run) DRY_RUN=1; shift;;
|
||||
-h|--help) usage; exit 0;;
|
||||
*) die "unknown option: $1 (see --help)";;
|
||||
esac
|
||||
done
|
||||
require_root
|
||||
[[ -n "$DOMAIN" ]] || die "--domain is required"
|
||||
[[ "$DOMAIN" =~ ^[A-Za-z0-9.-]+$ ]] || die "--domain looks invalid: $DOMAIN"
|
||||
[[ "$EXPIRY" =~ ^[0-9]+[smhd]$ ]] || die "--expiration must be a Go duration like 720h, got: $EXPIRY"
|
||||
[[ -n "$USER_NAME" ]] || die "--user must not be empty"
|
||||
need_cmd curl
|
||||
need_cmd dpkg
|
||||
}
|
||||
|
||||
detect_version() {
|
||||
[[ -n "$VERSION" ]] && { printf '%s' "$VERSION"; return; }
|
||||
local tag
|
||||
tag="$(curl -fsSL https://api.github.com/repos/juanfont/headscale/releases/latest \
|
||||
| sed -n 's/.*"tag_name":[[:space:]]*"v\{0,1\}\([^"]*\)".*/\1/p' | head -n1)"
|
||||
[[ -n "$tag" ]] || die "could not detect latest Headscale version; pass --version X.Y.Z"
|
||||
printf '%s' "$tag"
|
||||
}
|
||||
|
||||
install_headscale() {
|
||||
if have_cmd headscale; then
|
||||
ok "headscale already installed ($(headscale version 2>/dev/null | head -n1))"
|
||||
return
|
||||
fi
|
||||
local ver arch url tmp
|
||||
ver="$(detect_version)"
|
||||
arch="$(dpkg --print-architecture)"
|
||||
url="https://github.com/juanfont/headscale/releases/download/v${ver}/headscale_${ver}_linux_${arch}.deb"
|
||||
tmp="$(mktemp --suffix=.deb)"
|
||||
log "downloading Headscale v${ver} (${arch})"
|
||||
run curl -fsSL -o "$tmp" "$url"
|
||||
log "installing package"
|
||||
run apt-get install -y "$tmp"
|
||||
run rm -f "$tmp"
|
||||
}
|
||||
|
||||
# set or append a top-level scalar key in the YAML config (other keys untouched)
|
||||
set_yaml() {
|
||||
local key="$1" val="$2"
|
||||
if grep -qE "^[[:space:]]*${key}:" "$CFG"; then
|
||||
run sed -i -E "s|^([[:space:]]*)${key}:.*|\1${key}: ${val}|" "$CFG"
|
||||
elif [[ $DRY_RUN -eq 1 ]]; then
|
||||
echo " + append ${key}: ${val} >> $CFG"
|
||||
else
|
||||
printf '%s: %s\n' "$key" "$val" >> "$CFG"
|
||||
fi
|
||||
}
|
||||
|
||||
configure_headscale() {
|
||||
[[ -f "$CFG" ]] || die "expected config at $CFG (did the package install correctly?)"
|
||||
run cp -a "$CFG" "${CFG}.bak.$(date +%Y%m%d%H%M%S)"
|
||||
set_yaml server_url "https://${DOMAIN}"
|
||||
if [[ $USE_TLS -eq 1 ]]; then
|
||||
set_yaml listen_addr "0.0.0.0:443"
|
||||
set_yaml tls_letsencrypt_hostname "${DOMAIN}"
|
||||
set_yaml tls_letsencrypt_challenge_type "HTTP-01"
|
||||
set_yaml tls_letsencrypt_listen ":http"
|
||||
warn "built-in TLS: ports 80 (ACME challenge) and 443 must be reachable."
|
||||
else
|
||||
set_yaml listen_addr "$LISTEN_PLAIN"
|
||||
warn "--no-tls: terminate TLS at a reverse proxy in front of $LISTEN_PLAIN."
|
||||
fi
|
||||
ok "configured $CFG (server_url=https://${DOMAIN})"
|
||||
}
|
||||
|
||||
start_headscale() {
|
||||
run systemctl enable --now headscale
|
||||
if [[ $DRY_RUN -eq 0 ]]; then
|
||||
sleep 2
|
||||
systemctl is-active --quiet headscale \
|
||||
&& ok "headscale is running" \
|
||||
|| warn "headscale not active; check 'journalctl -u headscale -e'"
|
||||
fi
|
||||
}
|
||||
|
||||
provision_user() {
|
||||
if [[ $DRY_RUN -eq 1 ]]; then
|
||||
echo " + headscale users create $USER_NAME"
|
||||
echo " + headscale preauthkeys create --user $USER_NAME --reusable --expiration $EXPIRY"
|
||||
return
|
||||
fi
|
||||
if ! headscale users list 2>/dev/null | grep -qw "$USER_NAME"; then
|
||||
log "creating user '$USER_NAME'"
|
||||
headscale users create "$USER_NAME" || warn "users create failed (may already exist)"
|
||||
else
|
||||
ok "user '$USER_NAME' already exists"
|
||||
fi
|
||||
log "minting reusable pre-auth key (valid $EXPIRY)"
|
||||
# Newer headscale wants the user id; older accepts the name. Try name, then id.
|
||||
PREAUTH_KEY="$(headscale preauthkeys create --user "$USER_NAME" --reusable --expiration "$EXPIRY" 2>/dev/null | tail -n1 || true)"
|
||||
if [[ -z "$PREAUTH_KEY" || "$PREAUTH_KEY" == *" "* ]]; then
|
||||
local uid
|
||||
uid="$(headscale users list 2>/dev/null | awk -v u="$USER_NAME" '$0 ~ u {print $1; exit}')"
|
||||
[[ -n "$uid" ]] && PREAUTH_KEY="$(headscale preauthkeys create --user "$uid" --reusable --expiration "$EXPIRY" 2>/dev/null | tail -n1 || true)"
|
||||
fi
|
||||
if [[ -n "$PREAUTH_KEY" ]]; then
|
||||
ok "pre-auth key (treat as a secret): $PREAUTH_KEY"
|
||||
else
|
||||
warn "could not auto-mint a key; run: headscale preauthkeys create --user $USER_NAME --reusable --expiration $EXPIRY"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
install_headscale
|
||||
configure_headscale
|
||||
start_headscale
|
||||
provision_user
|
||||
|
||||
cat <<EOF
|
||||
|
||||
$(ok "Headscale ready at https://${DOMAIN}")
|
||||
|
||||
Join the Plex server and every client with:
|
||||
sudo tailscale up --login-server https://${DOMAIN} --authkey ${PREAUTH_KEY:-<preauth-key>}
|
||||
|
||||
On the Plex host, do VPN + Plex config in one step:
|
||||
sudo ./plex-tailscale-setup.sh --login-server https://${DOMAIN} --authkey ${PREAUTH_KEY:-<preauth-key>}
|
||||
|
||||
Manage access:
|
||||
headscale users list
|
||||
headscale nodes list
|
||||
headscale preauthkeys create --user ${USER_NAME} --reusable --expiration ${EXPIRY}
|
||||
EOF
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in new issue
Block a user