Commit 750b5b03 authored by Alishan Ladhani's avatar Alishan Ladhani

Create terraform state uploader

Uses lockbox gem to encrypt and decrypt files

Encryption is necessary because terraform state files contain secrets
parent 0bf3c56d
......@@ -4,4 +4,6 @@ class TerraformState < ApplicationRecord
belongs_to :project
validates :project_id, presence: true
mount_uploader :file, TerraformStateUploader
end
# frozen_string_literal: true
class TerraformStateUploader < GitlabUploader
include ObjectStorage::Concern
storage_options Gitlab.config.uploads
delegate :project_id, to: :model
# Use Lockbox to encrypt/decrypt the stored file (registers CarrierWave callbacks)
encrypt(key: :key)
def filename
"#{model.id}.tfstate"
end
def store_dir
project_id.to_s
end
def key
OpenSSL::HMAC.digest('SHA256', Gitlab::Application.secrets.db_key_base, project_id.to_s)
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :terraform_state do
project { create(:project) }
trait :with_file do
file { fixture_file_upload('spec/fixtures/terraform.tfstate') }
end
end
end
{
"version": 4,
"terraform_version": "0.12.21",
"serial": 1,
"lineage": "25e05991-243d-28d6-ebe3-ee0baae462cf",
"outputs": {},
"resources": []
}
\ No newline at end of file
# frozen_string_literal: true
require 'spec_helper'
describe TerraformStateUploader do
subject { terraform_state.file }
let(:terraform_state) { create(:terraform_state, file: fixture_file_upload('spec/fixtures/terraform.tfstate')) }
before do
stub_uploads_object_storage
end
describe '#filename' do
it 'contains the ID of the terraform state record' do
expect(subject.filename).to include(terraform_state.id.to_s)
end
end
describe '#store_dir' do
it 'contains the ID of the project' do
expect(subject.store_dir).to include(terraform_state.project_id.to_s)
end
end
describe '#key' do
it 'creates a digest with a secret key and the project id' do
expect(OpenSSL::HMAC)
.to receive(:digest)
.with('SHA256', Gitlab::Application.secrets.db_key_base, terraform_state.project_id.to_s)
.and_return('digest')
expect(subject.key).to eq('digest')
end
end
describe 'encryption' do
it 'encrypts the stored file' do
expect(subject.file.read).not_to eq(fixture_file('terraform.tfstate'))
end
it 'decrypts the file when reading' do
expect(subject.read).to eq(fixture_file('terraform.tfstate'))
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment