Skip to content
Snippets Groups Projects
Unverified Commit 3a82184e authored by Markus Koller's avatar Markus Koller
Browse files

Use encrypted properties and v2 migrations

parent 6d9b98e4
No related branches found
No related tags found
1 merge request!80302Backfill SSL verification for integrations with known-good hostnames
# frozen_string_literal: true
class AddTemporaryIndexForBackfillIntegrationsEnableSslVerification < Gitlab::Database::Migration[1.0]
class AddTemporaryIndexForBackfillIntegrationsEnableSslVerification < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
INDEX_NAME = 'tmp_index_integrations_on_id_where_type_droneci_or_teamcity'
INDEX_CONDITION = "type IN ('DroneCiService', 'TeamcityService') AND properties IS NOT NULL"
disable_ddl_transaction!
def up
# this index is used in 20220209121435_backfill_integrations_enable_ssl_verification
add_concurrent_index :integrations, :id, where: INDEX_CONDITION, name: INDEX_NAME
......
# frozen_string_literal: true
class BackfillIntegrationsEnableSslVerification < Gitlab::Database::Migration[1.0]
class BackfillIntegrationsEnableSslVerification < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_main
MIGRATION = 'BackfillIntegrationsEnableSslVerification'
INTERVAL = 5.minutes
......
# frozen_string_literal: true
class RemoveTemporaryIndexForBackfillIntegrationsEnableSslVerification < Gitlab::Database::Migration[1.0]
class RemoveTemporaryIndexForBackfillIntegrationsEnableSslVerification < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
INDEX_NAME = 'tmp_index_integrations_on_id_where_type_droneci_or_teamcity'
INDEX_CONDITION = "type IN ('DroneCiService', 'TeamcityService')"
disable_ddl_transaction!
def up
# this index was used in 20220209121435_backfill_integrations_enable_ssl_verification
remove_concurrent_index_by_name :integrations, INDEX_NAME
......
......@@ -33,9 +33,25 @@ class Integration < ActiveRecord::Base
self.table_name = :integrations
self.inheritance_column = :_type_disabled
serialize :properties, JSON
scope :affected, -> { where(type: INTEGRATIONS.keys).where.not(properties: nil) }
scope :affected, -> { where(type: INTEGRATIONS.keys).where.not(encrypted_properties: nil) }
attr_encrypted :properties,
mode: :per_attribute_iv,
key: Settings.attr_encrypted_db_key_base_32,
algorithm: 'aes-256-gcm',
marshal: true,
marshaler: ::Gitlab::Json,
encode: false,
encode_iv: false
# Handle assignment of props with symbol keys.
# To do this correctly, we need to call the method generated by attr_encrypted.
alias_method :attr_encrypted_props=, :properties=
private :attr_encrypted_props=
def properties=(props)
self.attr_encrypted_props = props&.with_indifferent_access&.freeze
end
end
def perform(start_id, stop_id)
......@@ -59,13 +75,13 @@ def perform(start_id, stop_id)
def process_integration(integration)
url_field, known_hostnames = INTEGRATIONS.fetch(integration.type)
url = integration.properties[url_field.to_s]
url = integration.properties[url_field.to_s] if integration.properties.present?
return unless url.present?
parsed_url = Addressable::URI.parse(url)
return unless parsed_url.scheme == 'https' && parsed_url.hostname =~ known_hostnames
integration.properties['enable_ssl_verification'] = true
integration.properties = integration.properties.merge('enable_ssl_verification' => true)
integration.save!(touch: false)
rescue Addressable::URI::InvalidURIError, ActiveRecord::RecordInvalid
......
......@@ -4,26 +4,37 @@
RSpec.describe Gitlab::BackgroundMigration::BackfillIntegrationsEnableSslVerification, schema: 20220209121435 do
let(:migration) { described_class.new }
let(:integrations) { table(:integrations) }
let(:integrations) { described_class::Integration }
before do
integrations.create!(id: 1, type: 'BambooService') # unaffected integration
integrations.create!(id: 2, type: 'DroneCiService') # no properties
integrations.create!(id: 3, type: 'DroneCiService', properties: {}.to_json) # no URL
integrations.create!(id: 4, type: 'DroneCiService', properties: { 'drone_url' => '' }.to_json) # blank URL
integrations.create!(id: 5, type: 'DroneCiService', properties: { 'drone_url' => 'https://example.com:foo' }.to_json) # invalid URL
integrations.create!(id: 6, type: 'DroneCiService', properties: { 'drone_url' => 'https://example.com' }.to_json) # unknown URL
integrations.create!(id: 7, type: 'DroneCiService', properties: { 'drone_url' => 'http://cloud.drone.io' }.to_json) # no HTTPS
integrations.create!(id: 8, type: 'DroneCiService', properties: { 'drone_url' => 'https://cloud.drone.io' }.to_json) # known URL
integrations.create!(id: 9, type: 'TeamcityService', properties: { 'teamcity_url' => 'https://example.com' }.to_json) # unknown URL
integrations.create!(id: 10, type: 'TeamcityService', properties: { 'teamcity_url' => 'https://foo.bar.teamcity.com' }.to_json) # unknown URL
integrations.create!(id: 11, type: 'TeamcityService', properties: { 'teamcity_url' => 'https://teamcity.com' }.to_json) # unknown URL
integrations.create!(id: 12, type: 'TeamcityService', properties: { 'teamcity_url' => 'https://customer.teamcity.com' }.to_json) # known URL
integrations.create!(
id: 1, type: 'BambooService') # unaffected integration
integrations.create!(
id: 2, type: 'DroneCiService') # no properties
integrations.create!(
id: 3, type: 'DroneCiService', properties: {}) # no URL
integrations.create!(
id: 4, type: 'DroneCiService', properties: { 'drone_url' => '' }) # blank URL
integrations.create!(
id: 5, type: 'DroneCiService', properties: { 'drone_url' => 'https://example.com:foo' }) # invalid URL
integrations.create!(
id: 6, type: 'DroneCiService', properties: { 'drone_url' => 'https://example.com' }) # unknown URL
integrations.create!(
id: 7, type: 'DroneCiService', properties: { 'drone_url' => 'http://cloud.drone.io' }) # no HTTPS
integrations.create!(
id: 8, type: 'DroneCiService', properties: { 'drone_url' => 'https://cloud.drone.io' }) # known URL
integrations.create!(
id: 9, type: 'TeamcityService', properties: { 'teamcity_url' => 'https://example.com' }) # unknown URL
integrations.create!(
id: 10, type: 'TeamcityService', properties: { 'teamcity_url' => 'https://foo.bar.teamcity.com' }) # unknown URL
integrations.create!(
id: 11, type: 'TeamcityService', properties: { 'teamcity_url' => 'https://teamcity.com' }) # unknown URL
integrations.create!(
id: 12, type: 'TeamcityService', properties: { 'teamcity_url' => 'https://customer.teamcity.com' }) # known URL
end
def properties(id)
properties = integrations.find(id).properties
Gitlab::Json.parse(properties) if properties
integrations.find(id).properties
end
it 'enables SSL verification for known-good hostnames', :aggregate_failures do
......
......@@ -5,17 +5,17 @@
RSpec.describe BackfillIntegrationsEnableSslVerification do
let_it_be(:migration) { described_class::MIGRATION }
let_it_be(:integrations) { table(:integrations) }
let_it_be(:integrations) { Gitlab::BackgroundMigration::BackfillIntegrationsEnableSslVerification::Integration }
before do
stub_const("#{described_class.name}::BATCH_SIZE", 2)
integrations.create!(id: 1, type: 'DroneCiService')
integrations.create!(id: 2, type: 'DroneCiService', properties: '{}')
integrations.create!(id: 3, type: 'BambooService', properties: '{}')
integrations.create!(id: 4, type: 'TeamcityService', properties: '{}')
integrations.create!(id: 5, type: 'DroneCiService', properties: '{}')
integrations.create!(id: 6, type: 'TeamcityService', properties: '{}')
integrations.create!(id: 2, type: 'DroneCiService', properties: {})
integrations.create!(id: 3, type: 'BambooService', properties: {})
integrations.create!(id: 4, type: 'TeamcityService', properties: {})
integrations.create!(id: 5, type: 'DroneCiService', properties: {})
integrations.create!(id: 6, type: 'TeamcityService', properties: {})
end
describe '#up' do
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment