Skip to content

Commit 7cd6501

Browse files
committed
encrypt access keys
1 parent dd823f3 commit 7cd6501

File tree

7 files changed

+101
-25
lines changed

7 files changed

+101
-25
lines changed

app/controllers/autograders_controller.rb

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,14 @@ def edit
4141

4242
action_auth_level :update, :instructor
4343
def update
44-
if @autograder.update(autograder_params) && @assessment.update(assessment_params)
44+
# Clear secrets if use_access_key is disabled
45+
params_to_update = autograder_params
46+
unless params_to_update[:use_access_key]
47+
params_to_update[:access_key] = nil
48+
params_to_update[:access_key_id] = nil
49+
end
50+
51+
if @autograder.update(params_to_update) && @assessment.update(assessment_params)
4552
flash[:success] = "Autograder saved."
4653
begin
4754
upload
@@ -115,8 +122,10 @@ def set_autograder
115122
end
116123

117124
def autograder_params
118-
params[:autograder].permit(:autograde_timeout, :autograde_image, :release_score, :access_key,
119-
:access_key_id, :instance_type)
125+
params.require(:autograder).permit(
126+
:autograde_timeout, :autograde_image, :release_score,
127+
:use_access_key, :access_key, :access_key_id, :instance_type
128+
)
120129
end
121130

122131
def assessment_params

app/helpers/assessment_autograde_core.rb

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -157,26 +157,27 @@ def get_job_status(job_id)
157157
# Returns the Tango response
158158
#
159159
def tango_add_job(course, assessment, upload_file_list, callback_url, job_name, output_file)
160-
job_properties = { "image" => @autograde_prop.autograde_image,
161-
"files" => upload_file_list.map do |f|
162-
{ "localFile" => f["remoteFile"],
163-
"destFile" => Pathname.new(f["destFile"]).basename.to_s }
164-
end,
165-
"output_file" => output_file,
166-
"timeout" => @autograde_prop.autograde_timeout,
167-
"callback_url" => callback_url,
168-
"jobName" => job_name,
169-
"disable_network" => assessment.disable_network}
170-
if Rails.configuration.x.ec2_ssh.present?
160+
job_properties = {
161+
"image" => @autograde_prop.autograde_image,
162+
"files" => upload_file_list.map do |f|
163+
{ "localFile" => f["remoteFile"],
164+
"destFile" => Pathname.new(f["destFile"]).basename.to_s }
165+
end,
166+
"output_file" => output_file,
167+
"timeout" => @autograde_prop.autograde_timeout,
168+
"callback_url" => callback_url,
169+
"jobName" => job_name,
170+
"disable_network" => assessment.disable_network
171+
}
172+
173+
if Rails.configuration.x.ec2_ssh
171174
job_properties["ec2Vmms"] = true
175+
job_properties["instanceType"] = @autograde_prop.instance_type.presence || "t3.micro"
176+
172177
if @autograde_prop.use_access_key?
173178
job_properties["accessKey"] = @autograde_prop.access_key
174179
job_properties["accessKeyId"] = @autograde_prop.access_key_id
175-
else
176-
job_properties["accessKey"] = ""
177-
job_properties["accessKeyId"] = ""
178180
end
179-
job_properties["instanceType"] = @autograde_prop.instance_type
180181
end
181182

182183
job_properties = job_properties.to_json

app/models/autograder.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,26 @@
55
class Autograder < ApplicationRecord
66
belongs_to :assessment
77

8+
# Encryption for AWS credentials
9+
has_encrypted :access_key
10+
has_encrypted :access_key_id
11+
812
trim_field :autograde_image
913

10-
# extremely short timeout values cause the backend to throw system errors
14+
# Validations
1115
validates :autograde_timeout,
1216
numericality: { greater_than_or_equal_to: 10, less_than_or_equal_to: 900 }
1317
validates :autograde_image, :autograde_timeout, presence: true
1418
validates :autograde_image, length: { maximum: 64 }
1519

20+
with_options if: :use_access_key do
21+
validates :access_key_id, presence: true, format: {
22+
with: /\A[A-Z0-9]{16,24}\z/,
23+
message: "looks invalid"
24+
}
25+
validates :access_key, presence: true
26+
end
27+
1628
after_commit -> { assessment.dump_yaml }
1729

1830
SERIALIZABLE = Set.new %w[autograde_image autograde_timeout release_score]

app/views/autograders/_ec2_settings.html.erb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,16 @@
66
<%= f.check_box :use_access_key,
77
display_name: "Enable Access Key",
88
help_text: "(Optional) Use your own provided access key to authenticate to different EC2 instances than the default one on Tango" %>
9-
<%= f.text_field :access_key, display_name: "Access Key" %>
10-
<%= f.text_field :access_key_id, display_name: "Access Key ID" %>
9+
<%= f.password_field :access_key,
10+
display_name: "Access Key",
11+
value: nil,
12+
autocomplete: "new-password",
13+
placeholder: "Leave blank to keep existing secret" %>
14+
<%= f.text_field :access_key_id,
15+
display_name: "Access Key ID",
16+
value: nil,
17+
autocomplete: "off",
18+
placeholder: "Leave blank to keep existing secret" %>
1119

1220
<%
1321
# Group EC2 instance options by category for better organization
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Be sure to restart your server when you modify this file.
22

33
# Configure sensitive parameters which will be filtered from the log file.
4-
Rails.application.config.filter_parameters += [:password, :session, :warden, :secret, :salt, :cookie, :csrf, :restful_key, :lockbox_master_key, :lti_tool_private_key]
4+
Rails.application.config.filter_parameters += [:password, :session, :warden, :secret, :salt, :cookie, :csrf, :restful_key, :lockbox_master_key, :lti_tool_private_key, :access_key, :access_key_id]
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
class EncryptAutograderCredentials < ActiveRecord::Migration[6.1]
2+
def up
3+
# Add encrypted columns for access keys
4+
add_column :autograders, :access_key_ciphertext, :text
5+
add_column :autograders, :access_key_id_ciphertext, :text
6+
7+
# Migrate existing plaintext data to encrypted columns
8+
Autograder.reset_column_information
9+
Autograder.find_each do |autograder|
10+
if autograder.read_attribute(:access_key).present?
11+
autograder.access_key = autograder.read_attribute(:access_key)
12+
end
13+
if autograder.read_attribute(:access_key_id).present?
14+
autograder.access_key_id = autograder.read_attribute(:access_key_id)
15+
end
16+
autograder.save(validate: false) # Skip validation during migration
17+
end
18+
19+
# Remove plaintext columns after migration
20+
remove_column :autograders, :access_key
21+
remove_column :autograders, :access_key_id
22+
end
23+
24+
def down
25+
# Add back plaintext columns
26+
add_column :autograders, :access_key, :string, default: ""
27+
add_column :autograders, :access_key_id, :string, default: ""
28+
29+
# Migrate encrypted data back to plaintext columns
30+
Autograder.reset_column_information
31+
Autograder.find_each do |autograder|
32+
if autograder.access_key_ciphertext.present?
33+
autograder.update_column(:access_key, autograder.access_key || "")
34+
end
35+
if autograder.access_key_id_ciphertext.present?
36+
autograder.update_column(:access_key_id, autograder.access_key_id || "")
37+
end
38+
end
39+
40+
# Remove encrypted columns
41+
remove_column :autograders, :access_key_ciphertext
42+
remove_column :autograders, :access_key_id_ciphertext
43+
end
44+
end

db/schema.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema.define(version: 2024_12_11_042124) do
13+
ActiveRecord::Schema.define(version: 2025_10_21_034813) do
1414

1515
create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
1616
t.string "name", null: false
@@ -150,9 +150,11 @@
150150
t.string "autograde_image"
151151
t.boolean "release_score"
152152
t.string "instance_type", default: ""
153-
t.string "access_key", default: ""
154-
t.string "access_key_id", default: ""
155153
t.boolean "use_access_key", default: false
154+
t.string "ami", default: ""
155+
t.string "security_group", default: ""
156+
t.text "access_key_ciphertext"
157+
t.text "access_key_id_ciphertext"
156158
end
157159

158160
create_table "course_user_data", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|

0 commit comments

Comments
 (0)