Commit 8b47dfae authored by Stan Hu's avatar Stan Hu

Merge branch '63502-encrypt-deploy-token' into 'master'

Resolve "Store deploy tokens encrypted"

Closes #63502

See merge request gitlab-org/gitlab-ce!30679
parents 53c39cc1 2dd6f423
......@@ -5,7 +5,7 @@ class DeployToken < ApplicationRecord
include TokenAuthenticatable
include PolicyActor
include Gitlab::Utils::StrongMemoize
add_authentication_token_field :token
add_authentication_token_field :token, encrypted: :optional
AVAILABLE_SCOPES = %i(read_repository read_registry).freeze
GITLAB_DEPLOY_TOKEN_NAME = 'gitlab-deploy-token'.freeze
......
---
title: Encrypt existing and new deploy tokens
merge_request: 30679
author:
type: other
# frozen_string_literal: true
class ChangeDeployTokensTokenNotNull < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
change_column_null :deploy_tokens, :token, true
end
end
# frozen_string_literal: true
class AddTokenEncryptedToDeployTokens < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :deploy_tokens, :token_encrypted, :string, limit: 255
end
end
# frozen_string_literal: true
class AddIndexToDeployTokensTokenEncrypted < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :deploy_tokens, :token_encrypted, unique: true, name: "index_deploy_tokens_on_token_encrypted"
end
def down
remove_concurrent_index_by_name :deploy_tokens, "index_deploy_tokens_on_token_encrypted"
end
end
# frozen_string_literal: true
class EncryptDeployTokensTokens < ActiveRecord::Migration[5.1]
DOWNTIME = false
class DeploymentTokens < ActiveRecord::Base
self.table_name = 'deploy_tokens'
end
def up
say_with_time("Encrypting tokens from deploy_tokens") do
DeploymentTokens.where('token_encrypted is NULL AND token IS NOT NULL').find_each(batch_size: 10000) do |deploy_token|
token_encrypted = Gitlab::CryptoHelper.aes256_gcm_encrypt(deploy_token.token)
deploy_token.update!(token_encrypted: token_encrypted)
end
end
end
def down
say_with_time("Decrypting tokens from deploy_tokens") do
DeploymentTokens.where('token_encrypted IS NOT NULL AND token IS NULL').find_each(batch_size: 10000) do |deploy_token|
token = Gitlab::CryptoHelper.aes256_gcm_decrypt(deploy_token.token_encrypted)
deploy_token.update!(token: token)
end
end
end
end
......@@ -1121,10 +1121,12 @@ ActiveRecord::Schema.define(version: 2019_08_20_163320) do
t.datetime_with_timezone "expires_at", null: false
t.datetime_with_timezone "created_at", null: false
t.string "name", null: false
t.string "token", null: false
t.string "token"
t.string "username"
t.string "token_encrypted", limit: 255
t.index ["token", "expires_at", "id"], name: "index_deploy_tokens_on_token_and_expires_at_and_id", where: "(revoked IS FALSE)"
t.index ["token"], name: "index_deploy_tokens_on_token", unique: true
t.index ["token_encrypted"], name: "index_deploy_tokens_on_token_encrypted", unique: true
end
create_table "deployments", id: :serial, force: :cascade do |t|
......
......@@ -198,12 +198,10 @@ module Gitlab
end.uniq
end
# rubocop: disable CodeReuse/ActiveRecord
def deploy_token_check(login, password)
return unless password.present?
token =
DeployToken.active.find_by(token: password)
token = DeployToken.active.find_by_token(password)
return unless token && login
return if login != token.username
......@@ -214,7 +212,6 @@ module Gitlab
Gitlab::Auth::Result.new(token, token.project, :deploy_token, scopes)
end
end
# rubocop: enable CodeReuse/ActiveRecord
def lfs_token_check(login, encoded_token, project)
deploy_key_matches = login.match(/\Alfs\+deploy-key-(\d+)\z/)
......
......@@ -2,7 +2,8 @@
FactoryBot.define do
factory :deploy_token do
token { SecureRandom.hex(50) }
token nil
token_encrypted { Gitlab::CryptoHelper.aes256_gcm_encrypt( SecureRandom.hex(50) ) }
sequence(:name) { |n| "PDT #{n}" }
read_repository true
read_registry true
......
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20190711201818_encrypt_deploy_tokens_tokens.rb')
describe EncryptDeployTokensTokens, :migration do
let(:migration) { described_class.new }
let(:deployment_tokens) { table(:deploy_tokens) }
let(:plaintext) { "secret-token" }
let(:expires_at) { DateTime.now + 1.year }
let(:ciphertext) { Gitlab::CryptoHelper.aes256_gcm_encrypt(plaintext) }
describe '#up' do
it 'keeps plaintext token the same and populates token_encrypted if not present' do
deploy_token = deployment_tokens.create!(
name: 'test_token',
read_repository: true,
expires_at: expires_at,
username: 'gitlab-token-1',
token: plaintext
)
migration.up
expect(deploy_token.reload.token).to eq(plaintext)
expect(deploy_token.reload.token_encrypted).to eq(ciphertext)
end
end
describe '#down' do
it 'decrypts encrypted token and saves it' do
deploy_token = deployment_tokens.create!(
name: 'test_token',
read_repository: true,
expires_at: expires_at,
username: 'gitlab-token-1',
token_encrypted: ciphertext
)
migration.down
expect(deploy_token.reload.token).to eq(plaintext)
expect(deploy_token.reload.token_encrypted).to eq(ciphertext)
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