Harden Docker ownership startup

This commit is contained in:
Codex
2026-08-10 11:15:22 +02:00
parent a41011ae17
commit db8b9f3560
5 changed files with 22 additions and 10 deletions
+5
View File
@@ -1,5 +1,10 @@
# Changelog # Changelog
## 0.52.10 - 2026-08-10
- Made Docker UID/GID startup ownership fixes best-effort per mount so read-only or policy-managed Jellyfin bind mounts do not prevent Kaizoku from starting.
- Removed an unused Docker entrypoint helper.
## 0.52.9 - 2026-08-10 ## 0.52.9 - 2026-08-10
- Removed the unused legacy AllManga/AllAnime GraphQL metadata helper and constants from active code. - Removed the unused legacy AllManga/AllAnime GraphQL metadata helper and constants from active code.
+2
View File
@@ -85,6 +85,8 @@ KAIZOKU_ALLOW_REMOTE=1 KAIZOKU_AUTH_USERNAME=admin KAIZOKU_AUTH_PASSWORD='change
USER_UID=$(id -u) USER_GID=$(id -g) docker compose up -d USER_UID=$(id -u) USER_GID=$(id -g) docker compose up -d
``` ```
When `USER_UID` and `USER_GID` are set, the entrypoint attempts to adjust ownership for `/downloads`, `/jellyfin/tv`, `/jellyfin/movies`, and Kaizoku state paths. Ownership changes are best-effort so policy-managed or read-only Jellyfin mounts can still be browsed and used when their existing permissions allow it.
Keep `./.kaizoku` mounted for production instances. That directory contains the SQLite database, configuration, queue history, watchlist data, thumbnail cache, remote sessions, and temporary staging files. Removing the mount starts Kaizoku with a fresh empty state. Keep `./.kaizoku` mounted for production instances. That directory contains the SQLite database, configuration, queue history, watchlist data, thumbnail cache, remote sessions, and temporary staging files. Removing the mount starts Kaizoku with a fresh empty state.
## Download Flow ## Download Flow
+1 -1
View File
@@ -1 +1 @@
0.52.9 0.52.10
+11 -8
View File
@@ -1,13 +1,6 @@
#!/bin/sh #!/bin/sh
set -eu set -eu
is_true() {
case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
1 | true | yes | on) return 0 ;;
*) return 1 ;;
esac
}
is_integer() { is_integer() {
case "${1:-}" in case "${1:-}" in
'' | *[!0-9]*) return 1 ;; '' | *[!0-9]*) return 1 ;;
@@ -15,6 +8,14 @@ is_integer() {
esac esac
} }
chown_path() {
owner="$1"
path="$2"
if ! chown "$owner" "$path"; then
echo "Warning: could not change ownership of $path to $owner; continuing." >&2
fi
}
run_as_requested_user() { run_as_requested_user() {
uid="${USER_UID:-}" uid="${USER_UID:-}"
gid="${USER_GID:-}" gid="${USER_GID:-}"
@@ -34,7 +35,9 @@ run_as_requested_user() {
fi fi
mkdir -p /app/.kaizoku/home mkdir -p /app/.kaizoku/home
chown "$uid:$gid" /downloads /jellyfin/tv /jellyfin/movies /app/.kaizoku /app/.kaizoku/home for path in /downloads /jellyfin/tv /jellyfin/movies /app/.kaizoku /app/.kaizoku/home; do
chown_path "$uid:$gid" "$path"
done
cd /app cd /app
exec setpriv \ exec setpriv \
+3 -1
View File
@@ -4088,7 +4088,9 @@ class TemplateHelperTests(unittest.TestCase):
self.assertIn("${JELLYFIN_TV_DIR:-./jellyfin/tv}:/jellyfin/tv", compose) self.assertIn("${JELLYFIN_TV_DIR:-./jellyfin/tv}:/jellyfin/tv", compose)
self.assertIn("${JELLYFIN_MOVIE_DIR:-./jellyfin/movies}:/jellyfin/movies", compose) self.assertIn("${JELLYFIN_MOVIE_DIR:-./jellyfin/movies}:/jellyfin/movies", compose)
self.assertIn("mkdir -p /downloads /jellyfin/tv /jellyfin/movies /app/.kaizoku", dockerfile) self.assertIn("mkdir -p /downloads /jellyfin/tv /jellyfin/movies /app/.kaizoku", dockerfile)
self.assertIn("chown \"$uid:$gid\" /downloads /jellyfin/tv /jellyfin/movies", entrypoint) self.assertIn("chown_path()", entrypoint)
self.assertIn("Warning: could not change ownership", entrypoint)
self.assertIn("for path in /downloads /jellyfin/tv /jellyfin/movies /app/.kaizoku /app/.kaizoku/home", entrypoint)
def test_active_code_does_not_reference_retired_metadata_source(self): def test_active_code_does_not_reference_retired_metadata_source(self):
active_files = ("app.py", "app_support.py", "http_handler.py", "provider_bridge.py", "provider_downloader.py") active_files = ("app.py", "app_support.py", "http_handler.py", "provider_bridge.py", "provider_downloader.py")