Fix fresh-install schema gap and OIDC provider CA trust
- Session::prepareTablesIfNeeded(): Wt::Dbo's automatic createTables() silently fails to create 4 tables (share, starred_artist, starred_release, starred_track) on a genuinely fresh database, with no exception raised and no clear reason found in Wt::Dbo internals. These tables are otherwise only ever created via historical schema migrations, which never run for a fresh install (new DBs are stamped directly at LMS_DATABASE_VERSION). Reproduced consistently across multiple from-scratch database attempts. Added defensive CREATE TABLE IF NOT EXISTS statements for all 4 right after createTables() as a targeted workaround; harmless no-op on databases where they already exist correctly. - Dockerfile-release / docker-entrypoint.sh: the OIDC token exchange (OAuthProcess::handleToken(), a server-to-server HTTPS call from LMS to the provider's token endpoint) has no way to trust a private/ internal CA, so any OIDC provider behind non-publicly-trusted TLS fails with 'certificate verify failed' immediately after the user authenticates - the callback silently drops back to the login page with no user-visible error, and no account ever gets created (the auto-registration logic in OIDCAuth::OIDCAuth was already correct; it just never got a chance to run). Added ca-certificates + su-exec to the runtime image and a docker-entrypoint.sh that runs update-ca-certificates against anything mounted into /usr/local/share/ca-certificates before dropping privileges and exec'ing lms, so operators can trust a custom CA via a volume mount.
This commit is contained in:
+9
-3
@@ -155,6 +155,7 @@ ARG RUNTIME_PACKAGES=" \
|
||||
boost-iostreams \
|
||||
boost-program_options \
|
||||
boost-thread \
|
||||
ca-certificates \
|
||||
lame-libs \
|
||||
libarchive \
|
||||
libconfig++ \
|
||||
@@ -170,6 +171,7 @@ ARG RUNTIME_PACKAGES=" \
|
||||
opus \
|
||||
pugixml \
|
||||
sqlite-libs \
|
||||
su-exec \
|
||||
zlib"
|
||||
|
||||
ARG LMS_USER=lms
|
||||
@@ -191,12 +193,16 @@ RUN addgroup -S ${LMS_GROUP} && \
|
||||
VOLUME /var/lms
|
||||
VOLUME /music
|
||||
VOLUME /usr/local/etc
|
||||
|
||||
USER ${LMS_USER}:${LMS_GROUP}
|
||||
VOLUME /usr/local/share/ca-certificates
|
||||
|
||||
COPY --from=build /tmp/fakeroot/ /usr
|
||||
COPY --from=build /tmp/fakeroot/share/lms/lms.conf /etc/lms.conf
|
||||
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
|
||||
EXPOSE 5082
|
||||
|
||||
ENTRYPOINT ["/usr/bin/lms"]
|
||||
# Stays root so the entrypoint can run update-ca-certificates; it drops to
|
||||
# ${LMS_USER}:${LMS_GROUP} via su-exec before running lms itself.
|
||||
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
||||
CMD ["/usr/bin/lms"]
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
# If the operator mounted extra trusted CA certificates (e.g. an internal
|
||||
# PKI root, common when LMS's OIDC provider sits behind a private CA),
|
||||
# pick them up before starting. update-ca-certificates needs root, so this
|
||||
# runs before we drop privileges to the lms user.
|
||||
set -e
|
||||
|
||||
if [ -d /usr/local/share/ca-certificates ] && [ -n "$(ls -A /usr/local/share/ca-certificates 2>/dev/null)" ]; then
|
||||
update-ca-certificates
|
||||
fi
|
||||
|
||||
exec su-exec lms:lms "$@"
|
||||
@@ -182,6 +182,67 @@ namespace lms::db
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround: on a genuinely fresh database, Wt::Dbo's createTables()
|
||||
// above silently fails to create "share", "starred_artist",
|
||||
// "starred_release" and "starred_track" (no exception raised, and no
|
||||
// discernible difference from the ~57 other mapped classes that DO
|
||||
// get created correctly). Root cause not identified. These four are
|
||||
// otherwise only ever created via historical schema migrations
|
||||
// (Migration.cpp), which never run for a fresh install (a new
|
||||
// database is stamped directly at LMS_DATABASE_VERSION). Create them
|
||||
// here defensively; IF NOT EXISTS makes this a no-op on databases
|
||||
// where createTables() (or, on upgraded databases, the migration
|
||||
// path) already created them correctly.
|
||||
{
|
||||
auto transaction{ createWriteTransaction() };
|
||||
execute(R"(CREATE TABLE IF NOT EXISTS "share" (
|
||||
"id" integer primary key autoincrement,
|
||||
"version" integer not null,
|
||||
"token" text not null,
|
||||
"media_ids" text not null,
|
||||
"description" text not null,
|
||||
"created" text not null,
|
||||
"expires" text,
|
||||
"last_visited" text,
|
||||
"visit_count" integer not null,
|
||||
"user_id" bigint not null,
|
||||
constraint "fk_share_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
|
||||
))");
|
||||
execute(R"(CREATE TABLE IF NOT EXISTS "starred_artist" (
|
||||
"id" integer primary key autoincrement,
|
||||
"version" integer not null,
|
||||
"backend" integer not null,
|
||||
"sync_state" integer not null,
|
||||
"date_time" text,
|
||||
"artist_id" bigint,
|
||||
"user_id" bigint,
|
||||
constraint "fk_starred_artist_artist" foreign key ("artist_id") references "artist" ("id") on delete cascade deferrable initially deferred,
|
||||
constraint "fk_starred_artist_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
|
||||
))");
|
||||
execute(R"(CREATE TABLE IF NOT EXISTS "starred_release" (
|
||||
"id" integer primary key autoincrement,
|
||||
"version" integer not null,
|
||||
"backend" integer not null,
|
||||
"sync_state" integer not null,
|
||||
"date_time" text,
|
||||
"release_id" bigint,
|
||||
"user_id" bigint,
|
||||
constraint "fk_starred_release_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred,
|
||||
constraint "fk_starred_release_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
|
||||
))");
|
||||
execute(R"(CREATE TABLE IF NOT EXISTS "starred_track" (
|
||||
"id" integer primary key autoincrement,
|
||||
"version" integer not null,
|
||||
"backend" integer not null,
|
||||
"sync_state" integer not null,
|
||||
"date_time" text,
|
||||
"track_id" bigint,
|
||||
"user_id" bigint,
|
||||
constraint "fk_starred_track_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred,
|
||||
constraint "fk_starred_track_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
|
||||
))");
|
||||
}
|
||||
}
|
||||
|
||||
bool Session::migrateSchemaIfNeeded()
|
||||
|
||||
Reference in New Issue
Block a user