Compare commits

...

4 Commits

Author SHA1 Message Date
Thomas Hobson 72e1eb1457
deploy: api privilege 2021-02-21 22:53:42 +13:00
Thomas Hobson 64b2882450
api: gpg key importing 2021-02-21 21:37:38 +13:00
Thomas Hobson 1f5d4b8eb1
api: config options 2021-02-21 21:37:13 +13:00
Thomas Hobson 514006058b
api: harden runaway code 2021-02-21 21:36:49 +13:00
4 changed files with 40 additions and 13 deletions

View File

@ -95,6 +95,18 @@ const options = [
desc: 'Maximum gid to use for runner', desc: 'Maximum gid to use for runner',
default: 1500, default: 1500,
validators: [] 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: []
} }
]; ];

View File

@ -73,9 +73,10 @@ class Job {
logger.info(`Executing job uuid=${this.uuid} uid=${this.uid} gid=${this.gid} runtime=${this.runtime.toString()}`); logger.info(`Executing job uuid=${this.uuid} uid=${this.uid} gid=${this.gid} runtime=${this.runtime.toString()}`);
logger.debug('Compiling'); logger.debug('Compiling');
const compile = this.runtime.compiled && await new Promise((resolve, reject) => { const compile = this.runtime.compiled && await new Promise((resolve, reject) => {
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 stdout = '';
var stderr = ''; var stderr = '';
const proc = cp.spawn('unshare', ['-n', 'bash', path.join(this.runtime.pkgdir, 'compile'),this.main, ...this.files] ,{ const proc = cp.spawn(proc_call[0], proc_call.splice(1) ,{
env: this.runtime.env_vars, env: this.runtime.env_vars,
stdio: ['pipe', 'pipe', 'pipe'], stdio: ['pipe', 'pipe', 'pipe'],
cwd: this.dir, cwd: this.dir,
@ -83,18 +84,24 @@ class Job {
gid: this.gid 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.stderr.on('data', d=>{if(stderr.length>config.output_max_size) proc.kill('SIGKILL'); else stderr += d;});
proc.stdout.on('data', d=>stdout += d); proc.stdout.on('data', d=>{if(stdout.length>config.output_max_size) proc.kill('SIGKILL'); else stdout += d;});
proc.on('exit', (code, signal)=>{ proc.on('exit', (code, signal)=>{
clearTimeout(kill_timeout); clearTimeout(kill_timeout);
proc.stderr.destroy()
proc.stdout.destroy()
resolve({stdout, stderr, code, signal}); resolve({stdout, stderr, code, signal});
}); });
proc.on('error', (err) => { proc.on('error', (err) => {
clearTimeout(kill_timeout); clearTimeout(kill_timeout);
proc.stderr.destroy()
proc.stdout.destroy()
reject({error: err, stdout, stderr}); reject({error: err, stdout, stderr});
}); });
}); });
@ -102,9 +109,10 @@ class Job {
logger.debug('Running'); logger.debug('Running');
const run = await new Promise((resolve, reject) => { 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 stdout = '';
var stderr = ''; 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, env: this.runtime.env_vars,
stdio: ['pipe', 'pipe', 'pipe'], stdio: ['pipe', 'pipe', 'pipe'],
cwd: this.dir, cwd: this.dir,
@ -112,18 +120,25 @@ class Job {
gid: this.gid gid: this.gid
}); });
const kill_timeout = setTimeout(proc.kill, this.timeouts.run, 'SIGKILL'); const kill_timeout = setTimeout(_ => proc.kill('SIGKILL'), this.timeouts.run);
proc.stderr.on('data', d=>stderr += d); proc.stderr.on('data', d=>{if(stderr.length>config.output_max_size) proc.kill('SIGKILL'); else stderr += d;});
proc.stdout.on('data', d=>stdout += d); proc.stdout.on('data', d=>{if(stdout.length>config.output_max_size) proc.kill('SIGKILL'); else stdout += d;});
proc.stdin.write(this.stdin)
proc.stdin.end()
proc.on('exit', (code, signal)=>{ proc.on('exit', (code, signal)=>{
clearTimeout(kill_timeout); clearTimeout(kill_timeout);
proc.stderr.destroy()
proc.stdout.destroy()
resolve({stdout, stderr, code, signal}); resolve({stdout, stderr, code, signal});
}); });
proc.on('error', (err) => { proc.on('error', (err) => {
clearTimeout(kill_timeout); clearTimeout(kill_timeout);
proc.stderr.destroy()
proc.stdout.destroy()
reject({error: err, stdout, stderr}); reject({error: err, stdout, stderr});
}); });
}); });

View File

@ -44,9 +44,8 @@ class Repository {
async import_keys(){ async import_keys(){
await this.load(); await this.load();
logger.info(`Importing keys for repo ${this.slug}`); logger.info(`Importing keys for repo ${this.slug}`);
await new Promise((resolve,reject)=>{ 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'] stdio: ['ignore', 'ignore', 'ignore']
}); });

View File

@ -3,6 +3,7 @@ version: '3.8'
services: services:
piston_api: piston_api:
build: api build: api
privileged: true
restart: always restart: always
ports: ports:
- 6969:6969 - 6969:6969
@ -17,7 +18,7 @@ services:
build: repo build: repo
command: > command: >
bash -c '/repo/make.sh && 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" echo -e "\nAn error here is fine, it just means its already added it. Perhaps you restarted this container"
' '
volumes: volumes: