added the ability to prohibit network access for code submissions executed using Docker
This commit is contained in:
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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))
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
@ -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|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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'
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user