diff options
Diffstat (limited to 'app/services/packages/debian')
3 files changed, 133 insertions, 61 deletions
diff --git a/app/services/packages/debian/generate_distribution_key_service.rb b/app/services/packages/debian/generate_distribution_key_service.rb index 28c97c7681e..917965da58e 100644 --- a/app/services/packages/debian/generate_distribution_key_service.rb +++ b/app/services/packages/debian/generate_distribution_key_service.rb @@ -5,20 +5,42 @@ module Packages class GenerateDistributionKeyService include Gitlab::Utils::StrongMemoize - def initialize(current_user:, params: {}) - @current_user = current_user + def initialize(params: {}) @params = params end def execute - raise ArgumentError, 'Please provide a user' unless current_user.is_a?(User) + using_pinentry do |ctx| + # Generate key + ctx.generate_key generate_key_params + + key = ctx.keys.first # rubocop:disable Gitlab/KeysFirstAndValuesFirst + fingerprint = key.fingerprint + + # Export private key + data = GPGME::Data.new + ctx.export_keys fingerprint, data, GPGME::EXPORT_MODE_SECRET + data.seek 0 + private_key = data.read - generate_key + # Export public key + data = GPGME::Data.new + ctx.export_keys fingerprint, data + data.seek 0 + public_key = data.read + + { + private_key: private_key, + public_key: public_key, + passphrase: passphrase, + fingerprint: fingerprint + } + end end private - attr_reader :current_user, :params + attr_reader :params def passphrase strong_memoize(:passphrase) do @@ -72,35 +94,6 @@ module Packages }.map { |k, v| "#{k}: #{v}\n" }.join + '</GnupgKeyParms>' end - - def generate_key - using_pinentry do |ctx| - # Generate key - ctx.generate_key generate_key_params - - key = ctx.keys.first # rubocop:disable Gitlab/KeysFirstAndValuesFirst - fingerprint = key.fingerprint - - # Export private key - data = GPGME::Data.new - ctx.export_keys fingerprint, data, GPGME::EXPORT_MODE_SECRET - data.seek 0 - private_key = data.read - - # Export public key - data = GPGME::Data.new - ctx.export_keys fingerprint, data - data.seek 0 - public_key = data.read - - { - private_key: private_key, - public_key: public_key, - passphrase: passphrase, - fingerprint: fingerprint - } - end - end end end end diff --git a/app/services/packages/debian/generate_distribution_service.rb b/app/services/packages/debian/generate_distribution_service.rb index 651325c49a0..74b07e05aa6 100644 --- a/app/services/packages/debian/generate_distribution_service.rb +++ b/app/services/packages/debian/generate_distribution_service.rb @@ -12,7 +12,7 @@ module Packages DEFAULT_LEASE_TIMEOUT = 1.hour.to_i.freeze # From https://salsa.debian.org/ftp-team/dak/-/blob/991aaa27a7f7aa773bb9c0cf2d516e383d9cffa0/setup/core-init.d/080_metadatakeys#L9 - BINARIES_METADATA = %w( + METADATA_KEYS = %w( Package Source Binary @@ -89,15 +89,18 @@ module Packages @distribution.components.ordered_by_name.each do |component| @distribution.architectures.ordered_by_name.each do |architecture| generate_component_file(component, :packages, architecture, :deb) + generate_component_file(component, :di_packages, architecture, :udeb) end + generate_component_file(component, :source, nil, :dsc) end end def generate_component_file(component, component_file_type, architecture, package_file_type) paragraphs = @distribution.package_files + .preload_package .preload_debian_file_metadata .with_debian_component_name(component.name) - .with_debian_architecture_name(architecture.name) + .with_debian_architecture_name(architecture&.name) .with_debian_file_type(package_file_type) .find_each .map(&method(:package_stanza_from_fields)) @@ -106,21 +109,49 @@ module Packages def package_stanza_from_fields(package_file) [ - BINARIES_METADATA.map do |metadata_key| - rfc822_field(metadata_key, package_file.debian_fields[metadata_key]) + METADATA_KEYS.map do |metadata_key| + metadata_name = metadata_key + metadata_value = package_file.debian_fields[metadata_key] + + if package_file.debian_dsc? + metadata_name = 'Package' if metadata_key == 'Source' + checksum = case metadata_key + when 'Files' then package_file.file_md5 + when 'Checksums-Sha256' then package_file.file_sha256 + when 'Checksums-Sha1' then package_file.file_sha1 + end + + if checksum + metadata_value = "\n#{checksum} #{package_file.size} #{package_file.file_name}#{metadata_value}" + end + end + + rfc822_field(metadata_name, metadata_value) end, rfc822_field('Section', package_file.debian_fields['Section'] || 'misc'), rfc822_field('Priority', package_file.debian_fields['Priority'] || 'extra'), - rfc822_field('Filename', package_filename(package_file)), - rfc822_field('Size', package_file.size), - rfc822_field('MD5sum', package_file.file_md5), - rfc822_field('SHA256', package_file.file_sha256) + package_file_extra_fields(package_file) ].flatten.compact.join('') end - def package_filename(package_file) + def package_file_extra_fields(package_file) + if package_file.debian_dsc? + [ + rfc822_field('Directory', package_dirname(package_file)) + ] + else + [ + rfc822_field('Filename', "#{package_dirname(package_file)}/#{package_file.file_name}"), + rfc822_field('Size', package_file.size), + rfc822_field('MD5sum', package_file.file_md5), + rfc822_field('SHA256', package_file.file_sha256) + ] + end + end + + def package_dirname(package_file) letter = package_file.package.name.start_with?('lib') ? package_file.package.name[0..3] : package_file.package.name[0] - "#{pool_prefix(package_file)}/#{letter}/#{package_file.package.name}/#{package_file.file_name}" + "#{pool_prefix(package_file)}/#{letter}/#{package_file.package.name}/#{package_file.package.version}" end def pool_prefix(package_file) @@ -128,7 +159,7 @@ module Packages when ::Packages::Debian::GroupDistribution "pool/#{@distribution.codename}/#{package_file.package.project_id}" else - "pool/#{@distribution.codename}/#{@distribution.container_id}" + "pool/#{@distribution.codename}" end end @@ -161,28 +192,37 @@ module Packages end def generate_release - @distribution.file = CarrierWaveStringFile.new(release_header + release_sums) + @distribution.key || @distribution.create_key(GenerateDistributionKeyService.new.execute) + @distribution.file = CarrierWaveStringFile.new(release_content) + @distribution.file_signature = SignDistributionService.new(@distribution, release_content, detach: true).execute + @distribution.signed_file = CarrierWaveStringFile.new( + SignDistributionService.new(@distribution, release_content).execute + ) @distribution.updated_at = release_date @distribution.save! end - def release_header - strong_memoize(:release_header) do - [ - %w[origin label suite version codename].map do |attribute| - rfc822_field(attribute.capitalize, @distribution.attributes[attribute]) - end, - rfc822_field('Date', release_date.to_formatted_s(:rfc822)), - valid_until_field, - rfc822_field('NotAutomatic', !@distribution.automatic, !@distribution.automatic), - rfc822_field('ButAutomaticUpgrades', @distribution.automatic_upgrades, !@distribution.automatic && @distribution.automatic_upgrades), - rfc822_field('Architectures', @distribution.architectures.map { |architecture| architecture.name }.sort.join(' ')), - rfc822_field('Components', @distribution.components.map { |component| component.name }.sort.join(' ')), - rfc822_field('Description', @distribution.description) - ].flatten.compact.join('') + def release_content + strong_memoize(:release_content) do + release_header + release_sums end end + def release_header + [ + %w[origin label suite version codename].map do |attribute| + rfc822_field(attribute.capitalize, @distribution.attributes[attribute]) + end, + rfc822_field('Date', release_date.to_formatted_s(:rfc822)), + valid_until_field, + rfc822_field('NotAutomatic', !@distribution.automatic, !@distribution.automatic), + rfc822_field('ButAutomaticUpgrades', @distribution.automatic_upgrades, !@distribution.automatic && @distribution.automatic_upgrades), + rfc822_field('Architectures', @distribution.architectures.map { |architecture| architecture.name }.sort.join(' ')), + rfc822_field('Components', @distribution.components.map { |component| component.name }.sort.join(' ')), + rfc822_field('Description', @distribution.description) + ].flatten.compact.join('') + end + def release_date strong_memoize(:release_date) do Time.now.utc @@ -197,7 +237,8 @@ module Packages return unless condition return if value.blank? - "#{name}: #{value.to_s.gsub("\n\n", "\n.\n").gsub("\n", "\n ")}\n" + value = " #{value}" unless value[0] == "\n" + "#{name}:#{value.to_s.gsub("\n\n", "\n.\n").gsub("\n", "\n ")}\n" end def valid_until_field diff --git a/app/services/packages/debian/sign_distribution_service.rb b/app/services/packages/debian/sign_distribution_service.rb new file mode 100644 index 00000000000..7797f7e9c0a --- /dev/null +++ b/app/services/packages/debian/sign_distribution_service.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Packages + module Debian + class SignDistributionService + include Gitlab::Utils::StrongMemoize + + def initialize(distribution, content, detach: false) + @distribution = distribution + @content = content + @detach = detach + end + + def execute + raise ArgumentError, 'distribution key is missing' unless @distribution.key + + sig_mode = GPGME::GPGME_SIG_MODE_CLEAR + + sig_mode = GPGME::GPGME_SIG_MODE_DETACH if @detach + + Gitlab::Gpg.using_tmp_keychain do + GPGME::Ctx.new( + armor: true, + offline: true, + pinentry_mode: GPGME::PINENTRY_MODE_LOOPBACK, + password: @distribution.key.passphrase + ) do |ctx| + ctx.import(GPGME::Data.from_str(@distribution.key.public_key)) + ctx.import(GPGME::Data.from_str(@distribution.key.private_key)) + signature = GPGME::Data.new + ctx.sign(GPGME::Data.from_str(@content), signature, sig_mode) + signature.to_s + end + end + end + end + end +end |