Restrict Library Paths

This patch introduces the new environment variable `MEDIA_BASE_PATH`
allowing to restrict the media paths that can be added to libraries and
therefore limiting which sections of the filesystem audiobookshelf will
scan and potentially serve to the web.

This means, you can, for example, specify that all media are in
`/audiobooks`, preventing users to access file system paths not starting
with this.

This also prevents things like users specifying `/` as a library path
which seems to be a good way for a denial of service attack right now.

The patch also adds the new environment variable to the example Docker
commands and docker-compose files, matching the media file specified in
those examples.
This commit is contained in:
Lars Kiesow 2022-11-25 22:36:41 +01:00
parent 6dec750d3e
commit 4a125276b3
No known key found for this signature in database
GPG key ID: 5DAFE8D9C823CE73
3 changed files with 18 additions and 7 deletions

View file

@ -22,8 +22,9 @@ const UID = process.env.AUDIOBOOKSHELF_UID || 99
const GID = process.env.AUDIOBOOKSHELF_GID || 100
const SOURCE = process.env.SOURCE || 'docker'
const ROUTER_BASE_PATH = process.env.ROUTER_BASE_PATH || ''
const MEDIA_BASE_PATH = process.env.MEDIA_BASE_PATH || ''
console.log('Config', CONFIG_PATH, METADATA_PATH)
const Server = new server(SOURCE, PORT, HOST, UID, GID, CONFIG_PATH, METADATA_PATH, ROUTER_BASE_PATH)
const Server = new server(SOURCE, PORT, HOST, UID, GID, CONFIG_PATH, METADATA_PATH, ROUTER_BASE_PATH, MEDIA_BASE_PATH)
Server.start()

View file

@ -38,7 +38,7 @@ const TaskManager = require('./managers/TaskManager')
const EBookManager = require('./managers/EBookManager')
class Server {
constructor(SOURCE, PORT, HOST, UID, GID, CONFIG_PATH, METADATA_PATH, ROUTER_BASE_PATH) {
constructor(SOURCE, PORT, HOST, UID, GID, CONFIG_PATH, METADATA_PATH, ROUTER_BASE_PATH, MEDIA_BASE_PATH) {
this.Port = PORT
this.Host = HOST
global.Source = SOURCE
@ -48,6 +48,7 @@ class Server {
global.ConfigPath = fileUtils.filePathToPOSIX(Path.normalize(CONFIG_PATH))
global.MetadataPath = fileUtils.filePathToPOSIX(Path.normalize(METADATA_PATH))
global.RouterBasePath = ROUTER_BASE_PATH
global.MediaBasePath = MEDIA_BASE_PATH
if (!fs.pathExistsSync(global.ConfigPath)) {
fs.mkdirSync(global.ConfigPath)

View file

@ -27,15 +27,20 @@ class LibraryController {
return f
})
for (const folder of newLibraryPayload.folders) {
const path = folder.fullPath
if (!path.startsWith(global.MediaBasePath)) {
Logger.warn(`${path} is not in media base directory`)
return res.status(400).send(`${path} is not in media base directory`)
}
try {
const direxists = await fs.pathExists(folder.fullPath)
const direxists = await fs.pathExists(path)
if (!direxists) { // If folder does not exist try to make it and set file permissions/owner
await fs.mkdir(folder.fullPath)
await filePerms.setDefault(folder.fullPath)
await fs.mkdir(path)
await filePerms.setDefault(path)
}
} catch (error) {
Logger.error(`[LibraryController] Failed to ensure folder dir "${folder.fullPath}"`, error)
return res.status(400).send(`Invalid folder directory "${folder.fullPath}"`)
Logger.error(`Failed to ensure folder dir "${path}"`, error)
return res.status(400).send(`Invalid folder directory "${path}"`)
}
}
@ -97,6 +102,10 @@ class LibraryController {
return f
})
for (const path of newFolderPaths) {
if (!path.startsWith(global.MediaBasePath)) {
Logger.warn(`${path} is not in media base directory`)
return res.status(400).send(`${path} is not in media base directory`)
}
const pathExists = await fs.pathExists(path)
if (!pathExists) {
// Ensure dir will recursively create directories which might be preferred over mkdir