diff options
author | Robert Speicher <robert@gitlab.com> | 2017-09-01 22:23:08 +0300 |
---|---|---|
committer | Robert Speicher <robert@gitlab.com> | 2017-09-01 22:23:08 +0300 |
commit | 223849fa173b7059e717ba4f9c02075a452cbd49 (patch) | |
tree | 19dd9e840010234164a4461ac081889c07e05c9a /lib | |
parent | dd825c0f5d00cef3cac89480bcf2027064c5d89d (diff) | |
parent | 29b40db58944a32db6cf1ae9906653a2e5f4be9d (diff) |
Merge branch '17849-allow-admin-to-restrict-min-key-length-and-techno' into 'master'
Add settings for minimum key strength and allowed key type
Closes #17849
See merge request !13712
Diffstat (limited to 'lib')
-rw-r--r-- | lib/api/settings.rb | 7 | ||||
-rw-r--r-- | lib/gitlab/git_access.rb | 9 | ||||
-rw-r--r-- | lib/gitlab/key_fingerprint.rb | 48 | ||||
-rw-r--r-- | lib/gitlab/ssh_public_key.rb | 71 |
4 files changed, 87 insertions, 48 deletions
diff --git a/lib/api/settings.rb b/lib/api/settings.rb index 667ba468ce6..851b226e9e5 100644 --- a/lib/api/settings.rb +++ b/lib/api/settings.rb @@ -122,6 +122,13 @@ 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.' + ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type| + optional :"#{type}_key_restriction", + type: Integer, + values: KeyRestrictionValidator.supported_key_restrictions(type), + desc: "Restrictions on the complexity of uploaded #{type.upcase} keys. A value of #{ApplicationSetting::FORBIDDEN_KEY_VALUE} disables all #{type.upcase} keys." + end + 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..62d1ecae676 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -35,6 +35,7 @@ module Gitlab def check(cmd, changes) check_protocol! + check_valid_actor! check_active_user! check_project_accessibility! check_project_moved! @@ -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..89ca1298120 --- /dev/null +++ b/lib/gitlab/ssh_public_key.rb @@ -0,0 +1,71 @@ +module Gitlab + class SSHPublicKey + Technology = Struct.new(:name, :key_class, :supported_sizes) + + Technologies = [ + Technology.new(:rsa, OpenSSL::PKey::RSA, [1024, 2048, 3072, 4096]), + Technology.new(:dsa, OpenSSL::PKey::DSA, [1024, 2048, 3072]), + Technology.new(:ecdsa, OpenSSL::PKey::EC, [256, 384, 521]), + Technology.new(:ed25519, Net::SSH::Authentication::ED25519::PubKey, [256]) + ].freeze + + def self.technology(name) + Technologies.find { |tech| tech.name.to_s == name.to_s } + end + + def self.technology_for_key(key) + Technologies.find { |tech| key.is_a?(tech.key_class) } + end + + def self.supported_sizes(name) + technology(name)&.supported_sizes + 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 + technology.name if valid? + 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 + + private + + def technology + @technology ||= + self.class.technology_for_key(key) || raise("Unsupported key type: #{key.class}") + end + end +end |