Commit ff8b3ef3 authored by Stan Hu's avatar Stan Hu

Fix mirrors that have invalid SSH public auth mode set

From https://gitlab.com/gitlab-org/gitlab-ce/issues/55729, there are
more than 129 pull mirrors that have an invalid auth set because
the user may have had previously configured the mirror with SSH
credentials. This post-deploy migration switches any pull
mirrors to password mode that fall under the criteria:

1. They are using SSH public key auth
2. They have an import_url that starts with `http`
parent 6e54f823
---
title: Fix mirrors that have invalid SSH public auth mode set
merge_request: 9135
author:
type: fixed
# frozen_string_literal: true
class FixImportDataAuthMethodForMirrors < ActiveRecord::Migration[5.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
class Project < ActiveRecord::Base
self.table_name = 'projects'
has_one :import_data, class_name: '::FixImportDataAuthMethodForMirrors::ProjectImportData', inverse_of: :project
end
class ProjectImportData < ActiveRecord::Base
self.table_name = 'project_import_data'
belongs_to :project, inverse_of: :import_data
attr_encrypted :credentials,
key: Settings.attr_encrypted_db_key_base,
marshal: true,
encode: true,
mode: :per_attribute_iv_and_salt,
insecure_mode: true,
algorithm: 'aes-256-cbc'
serialize :data, JSON
def auth_method
auth_method = credentials.fetch(:auth_method, nil) if credentials.present?
auth_method.presence || 'password'
end
def auth_method=(value)
creds = self.credentials || {}
creds[:auth_method] = value
# For some reason, we need to reassign it back to attr_encrypted
# for changes to take effect.
self.credentials = creds
end
end
def up
# There are about 60,000 project mirrors that match this criteria on GitLab.com.
# Only 129 had this issue, so most of the time will be spent decrypting secrets.
# It took about 3 minutes to complete.
Project.where(mirror: true).where("import_url LIKE 'http%'").preload(:import_data).find_each do |project|
begin
import_data = project.import_data
next unless import_data
if import_data.auth_method == 'ssh_public_key'
import_data.auth_method = 'password'
import_data.save
end
rescue OpenSSL::Cipher::CipherError
Rails.logger.warn "Error decrypting credentials in import data #{import_data&.id}"
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('ee', 'db', 'post_migrate', '20190111231855_fix_import_data_auth_method_for_mirrors.rb')
describe FixImportDataAuthMethodForMirrors, :migration do
let(:namespaces) { table(:namespaces) }
let(:namespace) { namespaces.create(id: 1000, name: 'gitlab-org', path: 'gitlab-test') }
def create_mirror(import_url:, enable_import_data: true)
project = described_class::Project.new(namespace_id: namespace.id, mirror: true, import_url: import_url)
described_class::ProjectImportData.new(project: project) if enable_import_data
project
end
def set_credentials(project, auth_type:, user: nil, password: nil)
project.import_data.credentials =
{
auth_method: auth_type,
user: user,
password: password
}
project.import_data.save
end
describe '#up' do
let!(:http_project) { create_mirror(import_url: 'https://example.com') }
let!(:bad_http_project) { create_mirror(import_url: 'https://bad.example.com') }
let!(:git_project) { create_mirror(import_url: 'git://example.com') }
let!(:null_project) { create_mirror(import_url: nil, enable_import_data: false) }
before do
set_credentials(http_project, auth_type: 'password', user: 'foo', password: 'pass')
set_credentials(bad_http_project, auth_type: 'ssh_public_key', user: 'bar', password: 'baz')
set_credentials(git_project, auth_type: 'ssh_public_key')
end
it 'fixes the auth method for projects' do
subject.up
expect(http_project.reload.import_data.auth_method).to eq('password')
expect(bad_http_project.reload.import_data.auth_method).to eq('password')
expect(git_project.reload.import_data.auth_method).to eq('ssh_public_key')
end
it 'retains current auth settings' do
expect { subject.up }.not_to change { git_project.import_data.credentials }
http_project.reload
expect(http_project.import_data.credentials[:user]).to eq('foo')
expect(http_project.import_data.credentials[:password]).to eq('pass')
bad_http_project.reload
expect(bad_http_project.import_data.credentials[:user]).to eq('bar')
expect(bad_http_project.import_data.credentials[:password]).to eq('baz')
end
it 'handles bad SSL decryptions' do
allow_any_instance_of(described_class::Project).to receive(:import_data).and_raise(OpenSSL::Cipher::CipherError)
expect { subject.up }.not_to change { described_class::ProjectImportData.find_by(project_id: bad_http_project.id).credentials }
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