2015-03-09 31 views
21

Per la mia tesi di laurea all'Università, sto lavorando a un sistema di classifiche di codifica in cui gli utenti possono compilare/eseguire codice non affidabile tramite contenitori di finestra mobile temporanei. Il sistema sembra funzionare bene finora, ma un problema che sto affrontando è che quando viene inviato il codice per un ciclo infinito, per esempio:Timeout Docker per container?

while True: 
    print "infinite loop" 

il sistema va in tilt. Il problema è che quando creo un nuovo contenitore finestra mobile, l'interprete Python impedisce alla finestra mobile di uccidere il contenitore secondario mentre i dati vengono ancora stampati su STDOUT (per sempre). Questo porta alla enorme vulnerabilità del lavoratore portuale di mangiare tutte le risorse di sistema disponibili fino a quando la macchina utilizzando il sistema completamente blocca (illustrato di seguito):

enter image description here

Quindi la mia domanda è: esiste un modo migliore di impostare un timeout su un contenitore di finestra mobile rispetto al metodo corrente che in realtà sarà il uccidere il contenitore finestra mobile e rendere il mio sistema protetto (codice originariamente preso da here)?

#!/bin/bash 
set -e 

to=$1 
shift 

cont=$(docker run --rm "[email protected]") 
code=$(timeout "$to" docker wait "$cont" || true) 
docker kill $cont &> /dev/null 
echo -n 'status: ' 
if [ -z "$code" ]; then 
    echo timeout 
else 
    echo exited: $code 
fi 

echo output: 
# pipe to sed simply for pretty nice indentation 
docker logs $cont | sed 's/^/\t/' 

docker rm $cont &> /dev/null 

Edit: Il timeout predefinito nella mia applicazione (passato alla variabile $to) è "10s"/10 secondi.


Ho provato esaminando l'aggiunta di un timer e sys.exit() alla sorgente Python direttamente, ma questo non è davvero una valida opzione come sembra piuttosto insicuro perché l'utente potrebbe inviare il codice per evitare che l'esecuzione, il che significa che il problema persisterà ancora. Oh, le gioie di essere bloccati su una tesi ... :(

+2

Potrei mancare qualcosa, ma perché non puoi fare qualcosa del tipo: 'ID = $ (finestra mobile run -d unsecure) && finestra mobile run --rm cleanup $ ID', dove cleanup è un contenitore che uccide un altro container dopo X secondi? –

+0

"per sempre" - proviamo a diminuire la dimensione del buffer stdout. Qual è il tuo stato containerizzato ''python' dopo aver inviato il segnale? –

+0

Puoi impostare un limite sul tempo della CPU usando python con le risorse lib, uno script python può essere controllato e modificato in fase di esecuzione usando ast –

risposta

3

È possibile impostare il contenitore con un ulimit sul tempo massimo della CPU, che uccide il processo di loop. Un utente malintenzionato può aggirare questo, però, se sono radice all'interno del contenitore.

C'è un'altra domanda SO, "Setting absolute limits on CPU for Docker containers", che descrive come limitare il consumo della CPU di contenitori. Ciò consentirebbe di ridurre l'effetto di utenti malintenzionati.

io sono d'accordo con Abdullah, però, dovresti essere in grado di docker kill di scappare dal tuo supervisore.

0

Immagino che puoi usare i segnali in python come unix per impostare il timeout. puoi usare l'allarme di un'ora specifica per dire 50 secondi e prenderlo. Il seguente link potrebbe aiutarti. signals in python

0

Se si desidera eseguire i contenitori senza fornire alcuna protezione al loro interno, è possibile utilizzare runtime constraints on resources.

Nel tuo caso, -m 100M --cpu-quota 50000 potrebbe essere ragionevole.

In questo modo non mangerà le risorse del sistema del genitore finché non si arriva a ucciderlo.

0

Ho raggiunto una soluzione per questo problema.

In primo luogo si deve uccidere contenitore finestra mobile quando limite di tempo si ottiene:

#!/bin/bash 
set -e 
did=$(docker run -it -d -v "/my_real_path/$1":/usercode virtual_machine ./usercode/compilerun.sh 2>> $1/error.txt) 
sleep 10 && docker kill $did &> /dev/null && echo -n "timeout" >> $1/error.txt & 
docker wait "$did" &> /dev/null 
docker rm -f $ &> /dev/null 

Le piste container in modalità indipendente (opzione -d), quindi è eseguito in background. Quindi si esegue il sonno anche in background. Quindi attendere che il contenitore si fermi. Se non si ferma in 10 secondi (sleep timer), il contenitore verrà ucciso.

Come si può vedere, il processo di esecuzione finestra mobile chiama uno script chiamato compilerun.sh:

#!/bin/bash 
gcc -o /usercode/file /usercode/file.c 2> /usercode/error.txt && ./usercode/file < /usercode/input.txt | head -c 1M > /usercode/output.txt 
maxsize=1048576 
actualsize=$(wc -c <"/usercode/output.txt") 
if [ $actualsize -ge $maxsize ]; then 
    echo -e "1MB file size limit exceeded\n\n$(cat /usercode/output.txt)" > /usercode/output.txt 
fi 

Si parte da compilare ed eseguire un programma in C (è il mio caso d'uso, sono sicuro che lo stesso può essere fatto per il compilatore Python).

Questa parte:

command | head -c 1M > /usercode/output.txt 

è responsabile per la massima cosa formato di output. Consente all'output di avere un massimo di 1 MB.

Successivamente, controllo solo se il file è 1MB. Se è vero, scrivi un messaggio all'interno (all'inizio del) del file di output.

Problemi correlati