added the ability to prohibit network access for code submissions executed using Docker

This commit is contained in:
Hauke Klement
2015-03-17 17:14:25 +01:00
parent b1218e0b80
commit 15d8984a9e
12 changed files with 41 additions and 4 deletions

View File

@ -29,7 +29,7 @@ class ExecutionEnvironmentsController < ApplicationController
end end
def execution_environment_params def execution_environment_params
params[:execution_environment].permit(:docker_image, :exposed_ports, :editor_mode, :file_extension, :file_type_id, :help, :indent_size, :memory_limit, :name, :permitted_execution_time, :pool_size, :run_command, :test_command, :testing_framework).merge(user_id: current_user.id, user_type: current_user.class.name) params[:execution_environment].permit(:docker_image, :exposed_ports, :editor_mode, :file_extension, :file_type_id, :help, :indent_size, :memory_limit, :name, :network_enabled, :permitted_execution_time, :pool_size, :run_command, :test_command, :testing_framework).merge(user_id: current_user.id, user_type: current_user.class.name)
end end
private :execution_environment_params private :execution_environment_params

View File

@ -16,6 +16,7 @@ class ExecutionEnvironment < ActiveRecord::Base
validate :working_docker_image?, if: :validate_docker_image? validate :working_docker_image?, if: :validate_docker_image?
validates :docker_image, presence: true validates :docker_image, presence: true
validates :memory_limit, numericality: {greater_than_or_equal_to: DockerClient::MINIMUM_MEMORY_LIMIT, only_integer: true}, presence: true validates :memory_limit, numericality: {greater_than_or_equal_to: DockerClient::MINIMUM_MEMORY_LIMIT, only_integer: true}, presence: true
validates :network_enabled, inclusion: {in: [true, false]}
validates :name, presence: true validates :name, presence: true
validates :permitted_execution_time, numericality: {only_integer: true}, presence: true validates :permitted_execution_time, numericality: {only_integer: true}, presence: true
validates :pool_size, numericality: {only_integer: true}, presence: true validates :pool_size, numericality: {only_integer: true}, presence: true

View File

@ -20,6 +20,10 @@
.form-group .form-group
= f.label(:memory_limit) = f.label(:memory_limit)
= f.number_field(:memory_limit, class: 'form-control', min: DockerClient::MINIMUM_MEMORY_LIMIT, value: f.object.memory_limit || DockerClient::DEFAULT_MEMORY_LIMIT) = f.number_field(:memory_limit, class: 'form-control', min: DockerClient::MINIMUM_MEMORY_LIMIT, value: f.object.memory_limit || DockerClient::DEFAULT_MEMORY_LIMIT)
.checkbox
label
= f.check_box(:network_enabled)
= t('activerecord.attributes.execution_environment.network_enabled')
.form-group .form-group
= f.label(:permitted_execution_time) = f.label(:permitted_execution_time)
= f.number_field(:permitted_execution_time, class: 'form-control', min: 1) = f.number_field(:permitted_execution_time, class: 'form-control', min: 1)

View File

@ -5,7 +5,7 @@ h1
= row(label: 'execution_environment.name', value: @execution_environment.name) = row(label: 'execution_environment.name', value: @execution_environment.name)
= row(label: 'execution_environment.user', value: link_to(@execution_environment.author, @execution_environment.author)) = row(label: 'execution_environment.user', value: link_to(@execution_environment.author, @execution_environment.author))
= row(label: 'execution_environment.file_type', value: @execution_environment.file_type.present? ? link_to(@execution_environment.file_type, @execution_environment.file_type) : nil) = row(label: 'execution_environment.file_type', value: @execution_environment.file_type.present? ? link_to(@execution_environment.file_type, @execution_environment.file_type) : nil)
- [:docker_image, :exposed_ports, :memory_limit, :permitted_execution_time, :pool_size, :run_command, :test_command].each do |attribute| - [:docker_image, :exposed_ports, :memory_limit, :network_enabled, :permitted_execution_time, :pool_size, :run_command, :test_command].each do |attribute|
= row(label: "execution_environment.#{attribute}", value: @execution_environment.send(attribute)) = row(label: "execution_environment.#{attribute}", value: @execution_environment.send(attribute))
= row(label: 'execution_environment.testing_framework', value: @testing_framework_adapter.try(:framework_name)) = row(label: 'execution_environment.testing_framework', value: @testing_framework_adapter.try(:framework_name))
= row(label: 'execution_environment.help', value: render_markdown(@execution_environment.help)) = row(label: 'execution_environment.help', value: render_markdown(@execution_environment.help))

View File

@ -14,6 +14,7 @@ de:
file_type_id: Standard-Dateityp file_type_id: Standard-Dateityp
help: Hilfetext help: Hilfetext
memory_limit: Speicher-Limit (in MB) memory_limit: Speicher-Limit (in MB)
network_enabled: Netzwerkzugriff
name: Name name: Name
permitted_execution_time: Erlaubte Ausführungszeit (in Sekunden) permitted_execution_time: Erlaubte Ausführungszeit (in Sekunden)
pool_size: Docker-Container-Pool-Größe pool_size: Docker-Container-Pool-Größe

View File

@ -15,6 +15,7 @@ en:
help: Help Text help: Help Text
memory_limit: Memory Limit (in MB) memory_limit: Memory Limit (in MB)
name: Name name: Name
network_enabled: Network Enabled
permitted_execution_time: Permitted Execution Time (in Seconds) permitted_execution_time: Permitted Execution Time (in Seconds)
pool_size: Docker Container Pool Size pool_size: Docker Container Pool Size
run_command: Run Command run_command: Run Command

View File

@ -0,0 +1,11 @@
class AddNetworkEnabledToExecutionEnvironments < ActiveRecord::Migration
def change
add_column :execution_environments, :network_enabled, :boolean
reversible do |direction|
direction.up do
ExecutionEnvironment.update_all(network_enabled: true)
end
end
end
end

View File

@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150317083739) do ActiveRecord::Schema.define(version: 20150317115338) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -47,6 +47,7 @@ ActiveRecord::Schema.define(version: 20150317083739) do
t.integer "pool_size" t.integer "pool_size"
t.integer "file_type_id" t.integer "file_type_id"
t.integer "memory_limit" t.integer "memory_limit"
t.boolean "network_enabled"
end end
create_table "exercises", force: true do |t| create_table "exercises", force: true do |t|

View File

@ -29,6 +29,7 @@ class DockerClient
{ {
'Image' => find_image_by_tag(execution_environment.docker_image).info['RepoTags'].first, 'Image' => find_image_by_tag(execution_environment.docker_image).info['RepoTags'].first,
'Memory' => execution_environment.memory_limit.megabytes, 'Memory' => execution_environment.memory_limit.megabytes,
'NetworkDisabled' => !execution_environment.network_enabled?,
'OpenStdin' => true, 'OpenStdin' => true,
'StdinOnce' => true 'StdinOnce' => true
} }

View File

@ -6,6 +6,7 @@ FactoryGirl.define do
association :file_type, factory: :dot_coffee association :file_type, factory: :dot_coffee
help help
name 'CoffeeScript' name 'CoffeeScript'
network_enabled false
permitted_execution_time 10.seconds permitted_execution_time 10.seconds
pool_size 0 pool_size 0
run_command 'coffee' run_command 'coffee'
@ -19,6 +20,7 @@ FactoryGirl.define do
association :file_type, factory: :dot_html association :file_type, factory: :dot_html
help help
name 'HTML5' name 'HTML5'
network_enabled false
permitted_execution_time 10.seconds permitted_execution_time 10.seconds
pool_size 0 pool_size 0
run_command 'touch' run_command 'touch'
@ -34,6 +36,7 @@ FactoryGirl.define do
association :file_type, factory: :dot_java association :file_type, factory: :dot_java
help help
name 'Java 8' name 'Java 8'
network_enabled false
permitted_execution_time 10.seconds permitted_execution_time 10.seconds
pool_size 0 pool_size 0
run_command 'make run' run_command 'make run'
@ -49,6 +52,7 @@ FactoryGirl.define do
association :file_type, factory: :dot_rb association :file_type, factory: :dot_rb
help help
name 'JRuby 1.7' name 'JRuby 1.7'
network_enabled false
permitted_execution_time 10.seconds permitted_execution_time 10.seconds
pool_size 0 pool_size 0
run_command 'jruby %{filename}' run_command 'jruby %{filename}'
@ -64,6 +68,7 @@ FactoryGirl.define do
association :file_type, factory: :dot_js association :file_type, factory: :dot_js
help help
name 'Node.js' name 'Node.js'
network_enabled false
permitted_execution_time 10.seconds permitted_execution_time 10.seconds
pool_size 0 pool_size 0
run_command 'node %{filename}' run_command 'node %{filename}'
@ -77,6 +82,7 @@ FactoryGirl.define do
association :file_type, factory: :dot_py association :file_type, factory: :dot_py
help help
name 'Python 3.4' name 'Python 3.4'
network_enabled false
permitted_execution_time 10.seconds permitted_execution_time 10.seconds
pool_size 0 pool_size 0
run_command 'python3 %{filename}' run_command 'python3 %{filename}'
@ -92,6 +98,7 @@ FactoryGirl.define do
association :file_type, factory: :dot_rb association :file_type, factory: :dot_rb
help help
name 'Ruby 2.2' name 'Ruby 2.2'
network_enabled false
permitted_execution_time 10.seconds permitted_execution_time 10.seconds
pool_size 0 pool_size 0
run_command 'ruby %{filename}' run_command 'ruby %{filename}'
@ -108,6 +115,7 @@ FactoryGirl.define do
exposed_ports '4567' exposed_ports '4567'
help help
name 'Sinatra' name 'Sinatra'
network_enabled true
permitted_execution_time 15.minutes permitted_execution_time 15.minutes
pool_size 0 pool_size 0
run_command 'ruby %{filename}' run_command 'ruby %{filename}'
@ -123,6 +131,7 @@ FactoryGirl.define do
association :file_type, factory: :dot_sql association :file_type, factory: :dot_sql
help help
name 'SQLite' name 'SQLite'
network_enabled false
permitted_execution_time 1.minute permitted_execution_time 1.minute
pool_size 0 pool_size 0
run_command 'sqlite3 /database.db -init %{filename} -html' run_command 'sqlite3 /database.db -init %{filename} -html'

View File

@ -36,6 +36,10 @@ describe DockerClient, docker: true do
expect(container_creation_options).to include('Memory' => execution_environment.memory_limit.megabytes) expect(container_creation_options).to include('Memory' => execution_environment.memory_limit.megabytes)
end end
it 'specifies whether network access is enabled' do
expect(container_creation_options).to include('NetworkDisabled' => !execution_environment.network_enabled?)
end
it 'specifies to open the standard input stream once' do it 'specifies to open the standard input stream once' do
expect(container_creation_options).to include('OpenStdin' => true, 'StdinOnce' => true) expect(container_creation_options).to include('OpenStdin' => true, 'StdinOnce' => true)
end end

View File

@ -1,7 +1,7 @@
require 'rails_helper' require 'rails_helper'
describe ExecutionEnvironment do describe ExecutionEnvironment do
let(:execution_environment) { described_class.create } let(:execution_environment) { described_class.create.tap { |execution_environment| execution_environment.update(network_enabled: nil) } }
it 'validates that the Docker image works', docker: true do it 'validates that the Docker image works', docker: true do
expect(execution_environment).to receive(:validate_docker_image?).and_return(true) expect(execution_environment).to receive(:validate_docker_image?).and_return(true)
@ -32,6 +32,10 @@ describe ExecutionEnvironment do
expect(execution_environment.errors[:name]).to be_present expect(execution_environment.errors[:name]).to be_present
end end
it 'validates the presence of the network enabled flag' do
expect(execution_environment.errors[:network_enabled]).to be_present
end
it 'validates the numericality of the permitted run time' do it 'validates the numericality of the permitted run time' do
execution_environment.update(permitted_execution_time: Math::PI) execution_environment.update(permitted_execution_time: Math::PI)
expect(execution_environment.errors[:permitted_execution_time]).to be_present expect(execution_environment.errors[:permitted_execution_time]).to be_present