From 9b1a9bf8b37040d94716e37e092d78d2184e14a8 Mon Sep 17 00:00:00 2001 From: Thomas Hobson Date: Mon, 22 Feb 2021 22:51:19 +1300 Subject: [PATCH] api: harden process limit --- api/Dockerfile | 10 +--------- api/src/executor/job.js | 29 +++++++++++++++++++---------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/api/Dockerfile b/api/Dockerfile index faa6b8b..3b07adf 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -1,13 +1,5 @@ FROM node:15.8.0-alpine3.13 -RUN apk add --no-cache gnupg tar bash coreutils shadow -RUN for i in $(seq 1000 1500); do \ - groupadd -g $i runner$i && \ - useradd -M runner$i -g $i -u $i && \ - echo "runner$i soft nproc 64" >> /etc/security/limits.conf && \ - echo "runner$i hard nproc 64" >> /etc/security/limits.conf && \ - echo "runner$i soft nofile 2048" >> /etc/security/limits.conf && \ - echo "runner$i hard nofile 2048" >> /etc/security/limits.conf ;\ - done +RUN apk add --no-cache gnupg tar bash coreutils util-linux ENV NODE_ENV=production WORKDIR /piston_api diff --git a/api/src/executor/job.js b/api/src/executor/job.js index 421655d..7c0e83c 100644 --- a/api/src/executor/job.js +++ b/api/src/executor/job.js @@ -70,7 +70,14 @@ class Job { async safe_call(file, args, timeout){ return await new Promise((resolve, reject) => { - const proc_call = ['unshare','-n','-r','bash',file, ...args].slice(!config.enable_unshare*3) + const unshare = config.enable_unshare ? ['unshare','-n','-r'] : []; + const prlimit = ['prlimit','--nproc=64']; + + const proc_call = [ + ...prlimit, + ...unshare, + 'bash',file, ...args + ]; var stdout = ''; var stderr = ''; const proc = cp.spawn(proc_call[0], proc_call.splice(1) ,{ @@ -88,23 +95,25 @@ class Job { proc.stderr.on('data', d=>{if(stderr.length>config.output_max_size) proc.kill('SIGKILL'); else stderr += d;}); proc.stdout.on('data', d=>{if(stdout.length>config.output_max_size) proc.kill('SIGKILL'); else stdout += d;}); - function exitCleanup(){ + function exit_cleanup(){ clearTimeout(kill_timeout); - proc.stderr.destroy() - proc.stdout.destroy() + proc.stderr.destroy(); + proc.stdout.destroy(); try{ - process.kill(-proc.pid, 'SIGKILL') - }catch{} //Probably already dead! + process.kill(-proc.pid, 'SIGKILL'); + }catch{ + // Process will be dead alread, so nothing to kill. + } } proc.on('exit', (code, signal)=>{ - exitCleanup() + exit_cleanup(); resolve({stdout, stderr, code, signal}); }); proc.on('error', (err) => { - exitCleanup() + exit_cleanup(); reject({error: err, stdout, stderr}); }); @@ -118,14 +127,14 @@ class Job { const compile = this.runtime.compiled && await this.safe_call( path.join(this.runtime.pkgdir, 'compile'), [this.main, ...this.files], - this.timeouts.compile) + this.timeouts.compile); logger.debug('Running'); const run = await this.safe_call( path.join(this.runtime.pkgdir, 'run'), [this.main, ...this.args], - this.timeouts.run) + this.timeouts.run); this.state = job_states.EXECUTED;