From a99c1bf1cbb4fb8bb45141fc9734dc894dac4aee Mon Sep 17 00:00:00 2001 From: Sam Van den Vonder Date: Wed, 4 Dec 2024 08:01:22 +0100 Subject: [PATCH 1/5] Enable Sandboxed Compiles feature --- server-ce/config/settings.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/server-ce/config/settings.js b/server-ce/config/settings.js index a7e8219858..c95d2b4fb4 100644 --- a/server-ce/config/settings.js +++ b/server-ce/config/settings.js @@ -464,6 +464,41 @@ switch (process.env.OVERLEAF_FILESTORE_BACKEND) { } } +// Overleaf Extended CE Compiler options to enable sandboxed compiles. +// ----------- +if (process.env.SANDBOXED_COMPILES === 'true') { + settings.clsi = { + ...settings.clsi, + dockerRunner: true, + docker: { + image: process.env.TEX_LIVE_DOCKER_IMAGE, + env: { + HOME: '/tmp', + PATH: + process.env.COMPILER_PATH || + '/usr/local/bin:/usr/bin:/bin', + }, + user: 'www-data', + } + } + + if (settings.path == null) { + settings.path = {} + } + settings.path.synctexBaseDir = () => '/compile' + if (process.env.SANDBOXED_COMPILES_SIBLING_CONTAINERS === 'true') { + console.log('Using sibling containers for sandboxed compiles') + if (process.env.SANDBOXED_COMPILES_HOST_DIR) { + settings.path.sandboxedCompilesHostDir = + process.env.SANDBOXED_COMPILES_HOST_DIR + } else { + console.error( + 'Sibling containers, but SANDBOXED_COMPILES_HOST_DIR not set' + ) + } + } +} + // With lots of incoming and outgoing HTTP connections to different services, // sometimes long running, it is a good idea to increase the default number // of sockets that Node will hold open. From 2efa3daec156dcf72d615c6a7ea84e08a63f07f9 Mon Sep 17 00:00:00 2001 From: yu-i-i Date: Fri, 6 Dec 2024 12:45:15 +0100 Subject: [PATCH 2/5] Allow selecting a TeX Live image for a project --- server-ce/config/settings.js | 8 +------- services/clsi/app/js/DockerRunner.js | 4 ++-- .../web/app/src/Features/Project/ProjectEditorHandler.js | 5 +---- .../app/src/Features/Project/ProjectOptionsHandler.js | 3 +-- services/web/config/settings.defaults.js | 9 +++++++++ 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/server-ce/config/settings.js b/server-ce/config/settings.js index c95d2b4fb4..02e11483dc 100644 --- a/server-ce/config/settings.js +++ b/server-ce/config/settings.js @@ -472,13 +472,7 @@ if (process.env.SANDBOXED_COMPILES === 'true') { dockerRunner: true, docker: { image: process.env.TEX_LIVE_DOCKER_IMAGE, - env: { - HOME: '/tmp', - PATH: - process.env.COMPILER_PATH || - '/usr/local/bin:/usr/bin:/bin', - }, - user: 'www-data', + user: process.env.TEX_LIVE_DOCKER_USER || 'www-data', } } diff --git a/services/clsi/app/js/DockerRunner.js b/services/clsi/app/js/DockerRunner.js index def02eaf5b..97053c1875 100644 --- a/services/clsi/app/js/DockerRunner.js +++ b/services/clsi/app/js/DockerRunner.js @@ -232,8 +232,8 @@ const DockerRunner = { } } // set the path based on the image year - const match = image.match(/:([0-9]+)\.[0-9]+/) - const year = match ? match[1] : '2014' + const match = image.match(/:([0-9]+)\.[0-9]+|:TL([0-9]+)/) + const year = match ? match[1] || match[2] : '2014' env.PATH = `/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/texlive/${year}/bin/x86_64-linux/` const options = { Cmd: command, diff --git a/services/web/app/src/Features/Project/ProjectEditorHandler.js b/services/web/app/src/Features/Project/ProjectEditorHandler.js index 05e5beba09..5c870573c8 100644 --- a/services/web/app/src/Features/Project/ProjectEditorHandler.js +++ b/services/web/app/src/Features/Project/ProjectEditorHandler.js @@ -22,10 +22,7 @@ module.exports = ProjectEditorHandler = { deletedByExternalDataSource: project.deletedByExternalDataSource || false, members: [], invites: this.buildInvitesView(invites), - imageName: - project.imageName != null - ? Path.basename(project.imageName) - : undefined, + imageName: project.imageName, } ;({ owner, ownerFeatures, members } = diff --git a/services/web/app/src/Features/Project/ProjectOptionsHandler.js b/services/web/app/src/Features/Project/ProjectOptionsHandler.js index c0c11c396c..5d0001bcf4 100644 --- a/services/web/app/src/Features/Project/ProjectOptionsHandler.js +++ b/services/web/app/src/Features/Project/ProjectOptionsHandler.js @@ -24,7 +24,6 @@ const ProjectOptionsHandler = { if (!imageName || !Array.isArray(settings.allowedImageNames)) { return } - imageName = imageName.toLowerCase() const isAllowed = settings.allowedImageNames.find( allowed => imageName === allowed.imageName ) @@ -32,7 +31,7 @@ const ProjectOptionsHandler = { throw new Error(`invalid imageName: ${imageName}`) } const conditions = { _id: projectId } - const update = { imageName: settings.imageRoot + '/' + imageName } + const update = { imageName: imageName } return Project.updateOne(conditions, update, {}) }, diff --git a/services/web/config/settings.defaults.js b/services/web/config/settings.defaults.js index a7ff970ef0..0b91ef503e 100644 --- a/services/web/config/settings.defaults.js +++ b/services/web/config/settings.defaults.js @@ -1031,6 +1031,15 @@ module.exports = { managedUsers: { enabled: false, }, + + allowedImageNames: process.env.SANDBOXED_COMPILES === 'true' + ? parseTextExtensions(process.env.ALL_TEX_LIVE_DOCKER_IMAGES) + .map((imageName, index) => ({ + imageName, + imageDesc: parseTextExtensions(process.env.ALL_TEX_LIVE_DOCKER_IMAGE_NAMES)[index] + || imageName.split(':')[1], + })) + : undefined, } module.exports.mergeWith = function (overrides) { From 8bfdf1fcc58c740b863868e13713096d3b3f1c86 Mon Sep 17 00:00:00 2001 From: yu-i-i Date: Mon, 20 Jan 2025 05:38:02 +0100 Subject: [PATCH 3/5] Allow adding extra flags to LaTeX compiler through environment variable --- services/web/app/src/Features/Compile/ClsiManager.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/web/app/src/Features/Compile/ClsiManager.js b/services/web/app/src/Features/Compile/ClsiManager.js index 6f11297248..94208e8607 100644 --- a/services/web/app/src/Features/Compile/ClsiManager.js +++ b/services/web/app/src/Features/Compile/ClsiManager.js @@ -692,7 +692,7 @@ async function _getContentFromMongo(projectId) { function _finaliseRequest(projectId, options, project, docs, files) { const resources = [] - let flags + let flags = [] let rootResourcePath = null let rootResourcePathOverride = null let hasMainFile = false @@ -771,6 +771,10 @@ function _finaliseRequest(projectId, options, project, docs, files) { flags = ['-file-line-error'] } + if (process.env.TEX_COMPILER_EXTRA_FLAGS) { + flags.push(...process.env.TEX_COMPILER_EXTRA_FLAGS.split(/\s+/).filter(Boolean)) + } + return { compile: { options: { From fae7bcd2633f288b768d1dae3492a9d123bdea98 Mon Sep 17 00:00:00 2001 From: yu-i-i Date: Sat, 3 May 2025 03:20:14 +0200 Subject: [PATCH 4/5] Add 'poll' to clsi seccomp profile, fixes minted. Thanks, David. --- services/clsi/seccomp/clsi-profile.json | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/services/clsi/seccomp/clsi-profile.json b/services/clsi/seccomp/clsi-profile.json index 084354b15c..ad95130f76 100644 --- a/services/clsi/seccomp/clsi-profile.json +++ b/services/clsi/seccomp/clsi-profile.json @@ -829,13 +829,19 @@ "args": [] }, { - "name": "gettimeofday", - "action": "SCMP_ACT_ALLOW", - "args": [] - }, { - "name": "epoll_pwait", - "action": "SCMP_ACT_ALLOW", - "args": [] + "name": "gettimeofday", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "epoll_pwait", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "poll", + "action": "SCMP_ACT_ALLOW", + "args": [] } ] -} \ No newline at end of file +} From 1c702d5f005d50ef467cb666d34dea9169946fc6 Mon Sep 17 00:00:00 2001 From: yu-i-i Date: Tue, 13 May 2025 17:13:27 +0200 Subject: [PATCH 5/5] Refactor Sandboxed Compiles --- server-ce/config/settings.js | 29 ------------------- services/clsi/config/settings.defaults.js | 2 +- services/web/config/settings.defaults.js | 9 +----- .../web/modules/sandboxed-compiles/index.mjs | 22 ++++++++++++++ 4 files changed, 24 insertions(+), 38 deletions(-) create mode 100644 services/web/modules/sandboxed-compiles/index.mjs diff --git a/server-ce/config/settings.js b/server-ce/config/settings.js index 02e11483dc..a7e8219858 100644 --- a/server-ce/config/settings.js +++ b/server-ce/config/settings.js @@ -464,35 +464,6 @@ switch (process.env.OVERLEAF_FILESTORE_BACKEND) { } } -// Overleaf Extended CE Compiler options to enable sandboxed compiles. -// ----------- -if (process.env.SANDBOXED_COMPILES === 'true') { - settings.clsi = { - ...settings.clsi, - dockerRunner: true, - docker: { - image: process.env.TEX_LIVE_DOCKER_IMAGE, - user: process.env.TEX_LIVE_DOCKER_USER || 'www-data', - } - } - - if (settings.path == null) { - settings.path = {} - } - settings.path.synctexBaseDir = () => '/compile' - if (process.env.SANDBOXED_COMPILES_SIBLING_CONTAINERS === 'true') { - console.log('Using sibling containers for sandboxed compiles') - if (process.env.SANDBOXED_COMPILES_HOST_DIR) { - settings.path.sandboxedCompilesHostDir = - process.env.SANDBOXED_COMPILES_HOST_DIR - } else { - console.error( - 'Sibling containers, but SANDBOXED_COMPILES_HOST_DIR not set' - ) - } - } -} - // With lots of incoming and outgoing HTTP connections to different services, // sometimes long running, it is a good idea to increase the default number // of sockets that Node will hold open. diff --git a/services/clsi/config/settings.defaults.js b/services/clsi/config/settings.defaults.js index 1d82258a8e..bd5614eb98 100644 --- a/services/clsi/config/settings.defaults.js +++ b/services/clsi/config/settings.defaults.js @@ -107,7 +107,7 @@ if ((process.env.DOCKER_RUNNER || process.env.SANDBOXED_COMPILES) === 'true') { CLSI: 1, }, socketPath: '/var/run/docker.sock', - user: process.env.TEXLIVE_IMAGE_USER || 'tex', + user: process.env.TEXLIVE_IMAGE_USER || 'www-data', }, optimiseInDocker: true, expireProjectAfterIdleMs: 24 * 60 * 60 * 1000, diff --git a/services/web/config/settings.defaults.js b/services/web/config/settings.defaults.js index 0b91ef503e..8effabe9c5 100644 --- a/services/web/config/settings.defaults.js +++ b/services/web/config/settings.defaults.js @@ -1005,6 +1005,7 @@ module.exports = { 'launchpad', 'server-ce-scripts', 'user-activate', + 'sandboxed-compiles', ], viewIncludes: {}, @@ -1032,14 +1033,6 @@ module.exports = { enabled: false, }, - allowedImageNames: process.env.SANDBOXED_COMPILES === 'true' - ? parseTextExtensions(process.env.ALL_TEX_LIVE_DOCKER_IMAGES) - .map((imageName, index) => ({ - imageName, - imageDesc: parseTextExtensions(process.env.ALL_TEX_LIVE_DOCKER_IMAGE_NAMES)[index] - || imageName.split(':')[1], - })) - : undefined, } module.exports.mergeWith = function (overrides) { diff --git a/services/web/modules/sandboxed-compiles/index.mjs b/services/web/modules/sandboxed-compiles/index.mjs new file mode 100644 index 0000000000..d494a3eec4 --- /dev/null +++ b/services/web/modules/sandboxed-compiles/index.mjs @@ -0,0 +1,22 @@ +import Settings from '@overleaf/settings' + +const parseTextExtensions = function (extensions) { + if (extensions) { + return extensions.split(',').map(ext => ext.trim()) + } else { + return [] + } +} + +if (process.env.SANDBOXED_COMPILES === 'true') { + Settings.allowedImageNames = parseTextExtensions(process.env.ALL_TEX_LIVE_DOCKER_IMAGES) + .map((imageName, index) => ({ + imageName, + imageDesc: parseTextExtensions(process.env.ALL_TEX_LIVE_DOCKER_IMAGE_NAMES)[index] + || imageName.split(':')[1], + })) + if(!process.env.TEX_LIVE_DOCKER_IMAGE) { + process.env.TEX_LIVE_DOCKER_IMAGE = Settings.allowedImageNames[0].imageName + } + Settings.currentImageName = process.env.TEX_LIVE_DOCKER_IMAGE +}