diff --git a/CHANGES b/CHANGES index a7b4ec62c..09e736eb7 100644 --- a/CHANGES +++ b/CHANGES @@ -2708,4 +2708,5 @@ [7.6.2] * mail: fix issue with redis emitting warnings non-stop +* dockerproxy: allow child containers to access volumes diff --git a/src/dockerproxy.js b/src/dockerproxy.js index 3c1de80f7..7e7fed2c6 100644 --- a/src/dockerproxy.js +++ b/src/dockerproxy.js @@ -17,7 +17,8 @@ const apps = require('./apps.js'), path = require('path'), paths = require('./paths.js'), safe = require('safetydance'), - util = require('util'); + util = require('util'), + volumes = require('./volumes.js'); let gHttpServer = null; @@ -62,7 +63,7 @@ function attachDockerRequest(req, res, next) { } // eslint-disable-next-line no-unused-vars -function containersCreate(req, res, next) { +async function containersCreate(req, res, next) { safe.set(req.body, 'HostConfig.NetworkMode', 'cloudron'); // overwrite the network the container lives in safe.set(req.body, 'NetworkingConfig', {}); // drop any custom network configs safe.set(req.body, 'Labels', Object.assign({}, safe.query(req.body, 'Labels'), { appId: req.app.id, isCloudronManaged: String(false) })); // overwrite the app id to track containers of an app @@ -70,19 +71,28 @@ function containersCreate(req, res, next) { const appDataDir = path.join(paths.APPS_DATA_DIR, req.app.id, 'data'); - debug('Original bind mounts:', req.body.HostConfig.Binds); + debug('containersCreate: original bind mounts:', req.body.HostConfig.Binds); + + const result = await volumes.list(); + const volumesByName = {}; + result.forEach(r => volumesByName[r.name] = r); const binds = []; - for (const bind of (req.body.HostConfig.Binds || [])) { - if (!bind.startsWith('/app/data/')) { + for (const bind of (req.body.HostConfig.Binds || [])) { // bind is of the host:container:rw format + if (bind.startsWith('/app/data')) { + binds.push(bind.replace(new RegExp('^/app/data/'), appDataDir + '/')); + } else if (bind.startsWith('/media/')) { + const volumeName = bind.match(new RegExp('/media/([^:/]+)/?'))[1]; + const volume = volumesByName[volumeName]; + if (volume) binds.push(bind.replace(new RegExp(`^/media/${volumeName}`), volume.hostPath)); + else debug(`containersCreate: dropped unknown volume ${volumeName}`); + } else { req.dockerRequest.abort(); - return next(new HttpError(400, 'Binds must be under /app/data/')); + return next(new HttpError(400, 'Binds must be under /app/data/ or /media')); } - - binds.push(bind.replace(new RegExp('^/app/data/'), appDataDir + '/')); } - debug('Rewritten bind mounts:', binds); + debug('containersCreate: rewritten bind mounts:', binds); safe.set(req.body, 'HostConfig.Binds', binds); const plainBody = JSON.stringify(req.body);