From 514006058b61c0d18ea285b6b5bc20ee60c4c4ae Mon Sep 17 00:00:00 2001 From: Thomas Hobson Date: Sun, 21 Feb 2021 21:36:49 +1300 Subject: [PATCH 1/4] api: harden runaway code --- api/src/executor/job.js | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/api/src/executor/job.js b/api/src/executor/job.js index b28589e..a6cabd3 100644 --- a/api/src/executor/job.js +++ b/api/src/executor/job.js @@ -73,9 +73,10 @@ class Job { logger.info(`Executing job uuid=${this.uuid} uid=${this.uid} gid=${this.gid} runtime=${this.runtime.toString()}`); logger.debug('Compiling'); const compile = this.runtime.compiled && await new Promise((resolve, reject) => { - var stdout = ''; - var stderr = ''; - const proc = cp.spawn('unshare', ['-n', 'bash', path.join(this.runtime.pkgdir, 'compile'),this.main, ...this.files] ,{ + const proc_call = ['unshare', '-n', '-r', 'bash', path.join(this.runtime.pkgdir, 'compile'),this.main, ...this.files].slice(!config.enable_unshare * 3) + var stdout = ''; + var stderr = ''; + const proc = cp.spawn(proc_call[0], proc_call.splice(1) ,{ env: this.runtime.env_vars, stdio: ['pipe', 'pipe', 'pipe'], cwd: this.dir, @@ -83,18 +84,24 @@ class Job { gid: this.gid }); - const kill_timeout = setTimeout(proc.kill, this.timeouts.compile, 'SIGKILL'); + const kill_timeout = setTimeout(_ => proc.kill('SIGKILL'), this.timeouts.compile); - proc.stderr.on('data', d=>stderr += d); - proc.stdout.on('data', d=>stdout += d); + 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;}); proc.on('exit', (code, signal)=>{ clearTimeout(kill_timeout); + proc.stderr.destroy() + proc.stdout.destroy() + resolve({stdout, stderr, code, signal}); }); proc.on('error', (err) => { clearTimeout(kill_timeout); + proc.stderr.destroy() + proc.stdout.destroy() + reject({error: err, stdout, stderr}); }); }); @@ -102,28 +109,36 @@ class Job { logger.debug('Running'); const run = await new Promise((resolve, reject) => { + const proc_call = ['unshare', '-n', '-r', 'bash', path.join(this.runtime.pkgdir, 'run'), this.main, ...this.args].slice(!config.enable_unshare * 3); var stdout = ''; var stderr = ''; - const proc = cp.spawn('unshare', ['-n', 'bash', path.join(this.runtime.pkgdir, 'run'),this.main, ...this.args] ,{ + const proc = cp.spawn(proc_call[0], proc_call.slice(1) ,{ env: this.runtime.env_vars, stdio: ['pipe', 'pipe', 'pipe'], cwd: this.dir, uid: this.uid, gid: this.gid }); + + const kill_timeout = setTimeout(_ => proc.kill('SIGKILL'), this.timeouts.run); - const kill_timeout = setTimeout(proc.kill, this.timeouts.run, 'SIGKILL'); + 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;}); - proc.stderr.on('data', d=>stderr += d); - proc.stdout.on('data', d=>stdout += d); + proc.stdin.write(this.stdin) + proc.stdin.end() proc.on('exit', (code, signal)=>{ clearTimeout(kill_timeout); + proc.stderr.destroy() + proc.stdout.destroy() resolve({stdout, stderr, code, signal}); }); proc.on('error', (err) => { clearTimeout(kill_timeout); + proc.stderr.destroy() + proc.stdout.destroy() reject({error: err, stdout, stderr}); }); }); From 1f5d4b8eb1501eafb508c1ae27f618c4effba6d9 Mon Sep 17 00:00:00 2001 From: Thomas Hobson Date: Sun, 21 Feb 2021 21:37:13 +1300 Subject: [PATCH 2/4] api: config options --- api/src/config.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/api/src/config.js b/api/src/config.js index a2503bf..e541ce9 100644 --- a/api/src/config.js +++ b/api/src/config.js @@ -95,6 +95,18 @@ const options = [ desc: 'Maximum gid to use for runner', default: 1500, validators: [] + }, + { + key: 'enable_unshare', + desc: 'Enable using unshare to disable networking', + default: true, + validators: [] + }, + { + key: 'output_max_size', + desc: 'Max size of each stdio buffer', + default: 1024, + validators: [] } ]; From 64b28824500692659f58f110231032cfb05f47af Mon Sep 17 00:00:00 2001 From: Thomas Hobson Date: Sun, 21 Feb 2021 21:37:38 +1300 Subject: [PATCH 3/4] api: gpg key importing --- api/src/ppman/repo.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/src/ppman/repo.js b/api/src/ppman/repo.js index 4c6deab..d05c1a9 100644 --- a/api/src/ppman/repo.js +++ b/api/src/ppman/repo.js @@ -44,9 +44,8 @@ class Repository { async import_keys(){ await this.load(); logger.info(`Importing keys for repo ${this.slug}`); - await new Promise((resolve,reject)=>{ - const gpgspawn = cp.spawn('gpg', ['--receive-keys', this.keys], { + const gpgspawn = cp.spawn('gpg', ['--receive-keys', ...this.keys], { stdio: ['ignore', 'ignore', 'ignore'] }); From 72e1eb145784123b960653871d0520813d6be4fa Mon Sep 17 00:00:00 2001 From: Thomas Hobson Date: Sun, 21 Feb 2021 22:53:42 +1300 Subject: [PATCH 4/4] deploy: api privilege --- docker-compose.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 5cd8ede..1776a45 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -3,6 +3,7 @@ version: '3.8' services: piston_api: build: api + privileged: true restart: always ports: - 6969:6969 @@ -17,7 +18,7 @@ services: build: repo command: > bash -c '/repo/make.sh && - curl http://piston_api:6969/repos -XPOST -d "slug=local&url=file:///repo/index.yaml"; + true || curl http://piston_api:6969/repos -XPOST -d "slug=local&url=file:///repo/index.yaml"; echo -e "\nAn error here is fine, it just means its already added it. Perhaps you restarted this container" ' volumes: