Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorNick Thomas <nick@gitlab.com>2017-08-21 13:30:03 +0300
committerNick Thomas <nick@gitlab.com>2017-08-30 22:50:44 +0300
commitb0f982fbdf69c292ab4530c0aaaf1ab42f4e7a01 (patch)
tree0d76c74fb6260de1e3c9694a8501491b2eb486ef /lib
parent81f08d30e641dc1a6666022ab1f5d36dbcdced7e (diff)
Add settings for minimum key strength and allowed key type
This is an amalgamation of: * Cory Hinshaw: Initial implementation !5552 * Rémy Coutable: Updates !9350 * Nick Thomas: Resolve conflicts and add ED25519 support !13712
Diffstat (limited to 'lib')
-rw-r--r--lib/api/entities.rb1
-rw-r--r--lib/api/settings.rb6
-rw-r--r--lib/gitlab/git_access.rb9
-rw-r--r--lib/gitlab/key_fingerprint.rb48
-rw-r--r--lib/gitlab/ssh_public_key.rb84
5 files changed, 100 insertions, 48 deletions
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