Unraid/Nas Entrypoint.sh hardening + unraid xml template (#4)

* Unraid/Nas Entrypoint.sh hardening + unraid xml template

* further entrypoint.sh hardening
This commit is contained in:
Harvey
2026-04-04 13:45:00 +01:00
committed by GitHub
parent eed1ce700d
commit c141b9fcdf
4 changed files with 94 additions and 9 deletions
+2
View File
@@ -180,6 +180,8 @@ MusicSeerr stores its config in `config/config.json` inside the mapped config vo
Run `id` on your host to find your PUID and PGID values. Run `id` on your host to find your PUID and PGID values.
> **Unraid / NAS users:** Unraid defaults to `nobody:users` (PUID=99, PGID=100). If you see `chown: Operation not permitted` at startup, your volume mount is on a filesystem that rejects ownership changes (FUSE/shfs, NFS, CIFS). The container skips `chown` when the directories and their contents are already writable, so this is usually fine as long as the host paths are owned by the correct UID:GID.
### In-App Settings ### In-App Settings
| Setting | Location | | Setting | Location |
+2
View File
@@ -5,6 +5,8 @@ services:
environment: environment:
- PUID=1000 # User ID — run `id` on your host to find yours - PUID=1000 # User ID — run `id` on your host to find yours
- PGID=1000 # Group ID — run `id` on your host to find yours - PGID=1000 # Group ID — run `id` on your host to find yours
# Unraid: use 99/100 (nobody:users) unless you changed Unraid's defaults.
# If using Docker --user, PUID/PGID are ignored (user mapping is external).
- PORT=8688 # Internal port (must match the right side of "ports" below) - PORT=8688 # Internal port (must match the right side of "ports" below)
- TZ=Etc/UTC # Timezone — e.g. America/New_York, Europe/London - TZ=Etc/UTC # Timezone — e.g. America/New_York, Europe/London
ports: ports:
+63 -9
View File
@@ -1,23 +1,77 @@
#!/bin/sh #!/bin/sh
set -e set -e
umask 027
PUID=${PUID:-1000} PUID=${PUID:-1000}
PGID=${PGID:-1000} PGID=${PGID:-1000}
groupmod -o -g "$PGID" musicseerr 2>/dev/null || true case "$PUID" in ''|*[!0-9]*) echo "[init] FATAL: PUID='$PUID' is not a valid numeric UID."; exit 1;; esac
usermod -o -u "$PUID" musicseerr 2>/dev/null || true case "$PGID" in ''|*[!0-9]*) echo "[init] FATAL: PGID='$PGID' is not a valid numeric GID."; exit 1;; esac
check_writable() {
_dir="$1"
_identity="$2"
_probe="$_dir/.musicseerr_write_test_$$"
if [ -n "$_identity" ]; then
gosu "$_identity" touch "$_probe" 2>/dev/null; _rc=$?
gosu "$_identity" rm -f "$_probe" 2>/dev/null
else
touch "$_probe" 2>/dev/null; _rc=$?
rm -f "$_probe" 2>/dev/null
fi
return "$_rc"
}
if [ "$(id -u)" -ne 0 ]; then
echo "[init] Running as uid=$(id -u) gid=$(id -g) (non-root); skipping user/group setup."
for dir in /app/cache /app/config; do
mkdir -p "$dir" 2>/dev/null || true
if ! check_writable "$dir"; then
echo "[init] FATAL: $dir is not writable by uid=$(id -u)."
echo "[init] Ensure the host directory is owned by this UID/GID."
echo "[init] Run: chown $(id -u):$(id -g) <host-path>"
exit 1
fi
done
exec "$@"
fi
if ! groupmod -o -g "$PGID" musicseerr 2>/dev/null; then
echo "[init] WARNING: Could not set musicseerr group to GID=$PGID."
fi
if ! usermod -o -u "$PUID" musicseerr 2>/dev/null; then
echo "[init] WARNING: Could not set musicseerr user to UID=$PUID."
fi
TARGET_UID=$(id -u musicseerr) TARGET_UID=$(id -u musicseerr)
TARGET_GID=$(id -g musicseerr) TARGET_GID=$(id -g musicseerr)
echo "[init] Runtime user: musicseerr (uid=$TARGET_UID gid=$TARGET_GID)"
if [ "$TARGET_UID" != "$PUID" ]; then
echo "[init] WARNING: Requested PUID=$PUID but running as uid=$TARGET_UID (usermod may have failed)."
fi
if [ "$TARGET_GID" != "$PGID" ]; then
echo "[init] WARNING: Requested PGID=$PGID but running as gid=$TARGET_GID (groupmod may have failed)."
fi
# Only chown directories whose top-level ownership differs from the target.
# Nested mismatches from manual edits require a rebuild or manual chown.
for dir in /app/cache /app/config; do for dir in /app/cache /app/config; do
if [ -d "$dir" ]; then mkdir -p "$dir" 2>/dev/null || true
CURRENT=$(stat -c '%u:%g' "$dir")
if [ "$CURRENT" != "$TARGET_UID:$TARGET_GID" ]; then if check_writable "$dir" "$TARGET_UID:$TARGET_GID"; then
chown -R musicseerr:musicseerr "$dir" continue
fi fi
if chown musicseerr:musicseerr "$dir" 2>/dev/null; then
echo "[init] Adjusted ownership of $dir - verifying write access."
else
echo "[init] WARNING: Could not chown $dir (mount may not support ownership changes)."
fi
if ! check_writable "$dir" "$TARGET_UID:$TARGET_GID"; then
echo "[init] FATAL: $dir is not writable by uid=$TARGET_UID gid=$TARGET_GID."
echo "[init] Common causes: FUSE/shfs (Unraid), NFS root_squash, CIFS/SMB, dropped CAP_CHOWN."
echo "[init] Fix: ensure the host directory is writable by uid=$TARGET_UID gid=$TARGET_GID."
exit 1
fi fi
done done
+27
View File
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<Container version="2">
<Name>musicseerr</Name>
<Repository>ghcr.io/habirabbu/musicseerr:latest</Repository>
<Registry>https://github.com/habirabbu/musicseerr/pkgs/container/musicseerr</Registry>
<Network>bridge</Network>
<Privileged>false</Privileged>
<Support>https://github.com/HabiRabbu/Musicseerr/issues</Support>
<Project>https://github.com/HabiRabbu/Musicseerr</Project>
<Overview>MusicSeerr is a self-hosted music request and discovery app built around Lidarr. Search the full MusicBrainz catalogue, request albums, stream music from Jellyfin or Navidrome, discover new music based on your listening history, and scrobble to ListenBrainz and Last.fm. Requires a running Lidarr instance.</Overview>
<Category>MediaApp:Music</Category>
<WebUI>http://[IP]:[PORT:8688]</WebUI>
<TemplateURL>https://raw.githubusercontent.com/HabiRabbu/Musicseerr/main/templates/musicseerr.xml</TemplateURL>
<Icon>https://raw.githubusercontent.com/HabiRabbu/Musicseerr/main/Images/logo_icon.png</Icon>
<Requires>A running Lidarr instance with an API key. Lidarr nightly build is recommended.</Requires>
<ExtraSearchTerms>lidarr music request scrobble listenbrainz lastfm jellyfin navidrome</ExtraSearchTerms>
<Screenshot>https://raw.githubusercontent.com/HabiRabbu/Musicseerr/main/Images/HomePage.png</Screenshot>
<Screenshot>https://raw.githubusercontent.com/HabiRabbu/Musicseerr/main/Images/ArtistPage.png</Screenshot>
<Screenshot>https://raw.githubusercontent.com/HabiRabbu/Musicseerr/main/Images/DiscoverPage.png</Screenshot>
<Config Name="WebUI Port" Target="8688" Default="8688" Mode="tcp" Description="Port the MusicSeerr web interface listens on." Type="Port" Display="always" Required="true" Mask="false">8688</Config>
<Config Name="Config" Target="/app/config" Default="/mnt/user/appdata/musicseerr/config" Mode="rw" Description="Persistent application configuration and database." Type="Path" Display="always" Required="true" Mask="false">/mnt/user/appdata/musicseerr/config</Config>
<Config Name="Cache" Target="/app/cache" Default="/mnt/user/appdata/musicseerr/cache" Mode="rw" Description="Cover art and metadata cache." Type="Path" Display="always" Required="true" Mask="false">/mnt/user/appdata/musicseerr/cache</Config>
<Config Name="Music Library" Target="/music" Default="" Mode="ro" Description="Optional: mount your music library root for local file playback. Must match the root folder Lidarr uses and the Music Directory Path in MusicSeerr Settings &gt; Local Files." Type="Path" Display="advanced" Required="false" Mask="false"></Config>
<Config Name="PUID" Target="PUID" Default="99" Mode="" Description="User ID for file permissions inside the container. Run 'id' on your Unraid terminal to find your value." Type="Variable" Display="advanced" Required="false" Mask="false">99</Config>
<Config Name="PGID" Target="PGID" Default="100" Mode="" Description="Group ID for file permissions inside the container. Run 'id' on your Unraid terminal to find your value." Type="Variable" Display="advanced" Required="false" Mask="false">100</Config>
<Config Name="Timezone" Target="TZ" Default="Europe/London" Mode="" Description="Your timezone. Examples: America/New_York, Europe/Berlin, Asia/Tokyo." Type="Variable" Display="always" Required="false" Mask="false">Europe/London</Config>
</Container>