From b0f982fbdf69c292ab4530c0aaaf1ab42f4e7a01 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Mon, 21 Aug 2017 11:30:03 +0100 Subject: Add settings for minimum key strength and allowed key type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an amalgamation of: * Cory Hinshaw: Initial implementation !5552 * Rémy Coutable: Updates !9350 * Nick Thomas: Resolve conflicts and add ED25519 support !13712 --- lib/api/entities.rb | 1 + lib/api/settings.rb | 6 ++++ lib/gitlab/git_access.rb | 9 +++++ lib/gitlab/key_fingerprint.rb | 48 ------------------------- lib/gitlab/ssh_public_key.rb | 84 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 100 insertions(+), 48 deletions(-) delete mode 100644 lib/gitlab/key_fingerprint.rb create mode 100644 lib/gitlab/ssh_public_key.rb (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 803b48dd88a..8f766ba4f8d 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -744,6 +744,7 @@ module API expose(:default_snippet_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_snippet_visibility) } expose(:default_group_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_group_visibility) } expose :password_authentication_enabled, as: :signin_enabled + expose :allowed_key_types end class Release < Grape::Entity diff --git a/lib/api/settings.rb b/lib/api/settings.rb index 667ba468ce6..6ace0e1e390 100644 --- a/lib/api/settings.rb +++ b/lib/api/settings.rb @@ -122,6 +122,12 @@ module API optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.' optional :polling_interval_multiplier, type: BigDecimal, desc: 'Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling.' + optional :minimum_rsa_bits, type: Integer, values: Gitlab::SSHPublicKey.allowed_sizes('rsa'), desc: 'The minimum allowed bit length of an uploaded RSA key.' + optional :minimum_dsa_bits, type: Integer, values: Gitlab::SSHPublicKey.allowed_sizes('dsa'), desc: 'The minimum allowed bit length of an uploaded DSA key.' + optional :minimum_ecdsa_bits, type: Integer, values: Gitlab::SSHPublicKey.allowed_sizes('ecdsa'), desc: 'The minimum allowed curve size (in bits) of an uploaded ECDSA key.' + optional :minimum_ed25519_bits, type: Integer, values: Gitlab::SSHPublicKey.allowed_sizes('ed25519'), desc: 'The minimum allowed curve size (in bits) of an uploaded ED25519 key.' + optional :allowed_key_types, type: Array[String], values: Gitlab::SSHPublicKey.technology_names, desc: 'The SSH key types accepted by the application (`rsa`, `dsa`, `ecdsa` or `ed25519`).' + optional(*::ApplicationSettingsHelper.visible_attributes) at_least_one_of(*::ApplicationSettingsHelper.visible_attributes) end diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 3e8b83c0f90..9eab26d111e 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -34,6 +34,7 @@ module Gitlab end def check(cmd, changes) + check_valid_actor! check_protocol! check_active_user! check_project_accessibility! @@ -70,6 +71,14 @@ module Gitlab private + def check_valid_actor! + return unless actor.is_a?(Key) + + unless actor.valid? + raise UnauthorizedError, "Your SSH key #{actor.errors[:key].first}." + end + end + def check_protocol! unless protocol_allowed? raise UnauthorizedError, "Git access over #{protocol.upcase} is not allowed" diff --git a/lib/gitlab/key_fingerprint.rb b/lib/gitlab/key_fingerprint.rb deleted file mode 100644 index d9a79f7c291..00000000000 --- a/lib/gitlab/key_fingerprint.rb +++ /dev/null @@ -1,48 +0,0 @@ -module Gitlab - class KeyFingerprint - attr_reader :key, :ssh_key - - # Unqualified MD5 fingerprint for compatibility - delegate :fingerprint, to: :ssh_key, allow_nil: true - - def initialize(key) - @key = key - - @ssh_key = - begin - Net::SSH::KeyFactory.load_data_public_key(key) - rescue Net::SSH::Exception, NotImplementedError - end - end - - def valid? - ssh_key.present? - end - - def type - return unless valid? - - parts = ssh_key.ssh_type.split('-') - parts.shift if parts[0] == 'ssh' - - parts[0].upcase - end - - def bits - return unless valid? - - case type - when 'RSA' - ssh_key.n.num_bits - when 'DSS', 'DSA' - ssh_key.p.num_bits - when 'ECDSA' - ssh_key.group.order.num_bits - when 'ED25519' - 256 - else - raise "Unsupported key type: #{type}" - end - end - end -end diff --git a/lib/gitlab/ssh_public_key.rb b/lib/gitlab/ssh_public_key.rb new file mode 100644 index 00000000000..2df31bcc246 --- /dev/null +++ b/lib/gitlab/ssh_public_key.rb @@ -0,0 +1,84 @@ +module Gitlab + class SSHPublicKey + TYPES = %w[rsa dsa ecdsa ed25519].freeze + + Technology = Struct.new(:name, :allowed_sizes) + + Technologies = [ + Technology.new('rsa', [1024, 2048, 3072, 4096]), + Technology.new('dsa', [1024, 2048, 3072]), + Technology.new('ecdsa', [256, 384, 521]), + Technology.new('ed25519', [256]) + ].freeze + + def self.technology_names + Technologies.map(&:name) + end + + def self.technology(name) + Technologies.find { |ssh_key_technology| ssh_key_technology.name == name } + end + private_class_method :technology + + def self.allowed_sizes(name) + technology(name).allowed_sizes + end + + def self.allowed_type?(type) + technology_names.include?(type.to_s) + end + + attr_reader :key_text, :key + + # Unqualified MD5 fingerprint for compatibility + delegate :fingerprint, to: :key, allow_nil: true + + def initialize(key_text) + @key_text = key_text + + @key = + begin + Net::SSH::KeyFactory.load_data_public_key(key_text) + rescue StandardError, NotImplementedError + end + end + + def valid? + key.present? + end + + def type + return unless valid? + + case key + when OpenSSL::PKey::EC + :ecdsa + when OpenSSL::PKey::RSA + :rsa + when OpenSSL::PKey::DSA + :dsa + when Net::SSH::Authentication::ED25519::PubKey + :ed25519 + else + raise "Unsupported key type: #{key.class}" + end + end + + def bits + return unless valid? + + case type + when :rsa + key.n.num_bits + when :dsa + key.p.num_bits + when :ecdsa + key.group.order.num_bits + when :ed25519 + 256 + else + raise "Unsupported key type: #{type}" + end + end + end +end -- cgit v1.2.3