debian-mirror-gitlab/lib/gitlab/ssh_public_key.rb

146 lines
3.9 KiB
Ruby
Raw Normal View History

2018-12-13 13:39:08 +05:30
# frozen_string_literal: true
2018-03-17 18:26:18 +05:30
module Gitlab
class SSHPublicKey
2023-03-17 16:20:25 +05:30
include Gitlab::Utils::StrongMemoize
2022-03-02 08:16:31 +05:30
Technology = Struct.new(:name, :key_class, :supported_sizes, :supported_algorithms)
2018-03-17 18:26:18 +05:30
2022-03-02 08:16:31 +05:30
# See https://man.openbsd.org/sshd#AUTHORIZED_KEYS_FILE_FORMAT for the list of
# supported algorithms.
2019-02-15 15:39:39 +05:30
TECHNOLOGIES = [
2022-04-04 11:22:00 +05:30
Technology.new(:rsa, SSHData::PublicKey::RSA, [1024, 2048, 3072, 4096], %w(ssh-rsa)),
Technology.new(:dsa, SSHData::PublicKey::DSA, [1024, 2048, 3072], %w(ssh-dss)),
Technology.new(:ecdsa, SSHData::PublicKey::ECDSA, [256, 384, 521], %w(ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521)),
Technology.new(:ed25519, SSHData::PublicKey::ED25519, [256], %w(ssh-ed25519)),
Technology.new(:ecdsa_sk, SSHData::PublicKey::SKECDSA, [256], %w(sk-ecdsa-sha2-nistp256@openssh.com)),
Technology.new(:ed25519_sk, SSHData::PublicKey::SKED25519, [256], %w(sk-ssh-ed25519@openssh.com))
2018-03-17 18:26:18 +05:30
].freeze
2022-06-21 17:19:12 +05:30
def self.technologies
if Gitlab::FIPS.enabled?
Gitlab::FIPS::SSH_KEY_TECHNOLOGIES
else
TECHNOLOGIES
end
end
2018-03-17 18:26:18 +05:30
def self.technology(name)
2022-06-21 17:19:12 +05:30
technologies.find { |tech| tech.name.to_s == name.to_s }
2018-03-17 18:26:18 +05:30
end
def self.technology_for_key(key)
2022-06-21 17:19:12 +05:30
technologies.find { |tech| key.instance_of?(tech.key_class) }
2018-03-17 18:26:18 +05:30
end
2022-03-02 08:16:31 +05:30
def self.supported_types
2022-06-21 17:19:12 +05:30
technologies.map(&:name)
2022-03-02 08:16:31 +05:30
end
2018-03-17 18:26:18 +05:30
def self.supported_sizes(name)
2022-03-02 08:16:31 +05:30
technology(name).supported_sizes
end
def self.supported_algorithms
2022-06-21 17:19:12 +05:30
technologies.flat_map { |tech| tech.supported_algorithms }
2022-03-02 08:16:31 +05:30
end
def self.supported_algorithms_for_name(name)
technology(name).supported_algorithms
2018-03-17 18:26:18 +05:30
end
2018-03-27 19:54:05 +05:30
def self.sanitize(key_content)
ssh_type, *parts = key_content.strip.split
return key_content if parts.empty?
2018-12-13 13:39:08 +05:30
parts.each_with_object(+"#{ssh_type} ").with_index do |(part, content), index|
2018-03-27 19:54:05 +05:30
content << part
2022-04-04 11:22:00 +05:30
if self.new(content).valid?
2018-03-27 19:54:05 +05:30
break [content, parts[index + 1]].compact.join(' ') # Add the comment part if present
elsif parts.size == index + 1 # return original content if we've reached the last element
break key_content
end
end
end
2018-03-17 18:26:18 +05:30
attr_reader :key_text, :key
def initialize(key_text)
@key_text = key_text
2022-04-04 11:22:00 +05:30
# We need to strip options to parse key with options or in known_hosts
# format. See https://man.openbsd.org/sshd#AUTHORIZED_KEYS_FILE_FORMAT
# and https://man.openbsd.org/sshd#SSH_KNOWN_HOSTS_FILE_FORMAT
key_text_without_options = @key_text.to_s.match(/(\A|\s)(#{self.class.supported_algorithms.join('|')}).*/).to_s
2018-03-17 18:26:18 +05:30
@key =
begin
2022-04-04 11:22:00 +05:30
SSHData::PublicKey.parse_openssh(key_text_without_options)
rescue SSHData::DecodeError
2018-03-17 18:26:18 +05:30
end
end
def valid?
2022-04-04 11:22:00 +05:30
key.present?
2018-03-17 18:26:18 +05:30
end
def type
2022-04-04 11:22:00 +05:30
technology.name if valid?
end
def fingerprint
key.fingerprint(md5: true) if valid?
end
def fingerprint_sha256
'SHA256:' + key.fingerprint(md5: false) if valid?
2018-03-17 18:26:18 +05:30
end
def bits
2022-04-04 11:22:00 +05:30
return unless valid?
2018-03-17 18:26:18 +05:30
case type
when :rsa
2022-04-04 11:22:00 +05:30
key.n.num_bits
2018-03-17 18:26:18 +05:30
when :dsa
2022-04-04 11:22:00 +05:30
key.p.num_bits
2018-03-17 18:26:18 +05:30
when :ecdsa
2022-04-04 11:22:00 +05:30
key.openssl.group.order.num_bits
2018-03-17 18:26:18 +05:30
when :ed25519
256
2022-04-04 11:22:00 +05:30
when :ecdsa_sk
256
when :ed25519_sk
256
2018-03-17 18:26:18 +05:30
end
end
2022-07-23 23:45:48 +05:30
def banned?
2023-03-17 16:20:25 +05:30
return false unless valid?
banned_ssh_keys.fetch(type.to_s, []).include?(fingerprint_sha256)
2022-07-23 23:45:48 +05:30
end
2018-03-17 18:26:18 +05:30
private
2023-03-17 16:20:25 +05:30
def banned_ssh_keys
path = Rails.root.join('config/security/banned_ssh_keys.yml')
config = YAML.load_file(path) if File.exist?(path)
config || {}
end
strong_memoize_attr :banned_ssh_keys
2018-03-17 18:26:18 +05:30
def technology
@technology ||=
2022-04-04 11:22:00 +05:30
self.class.technology_for_key(key) || raise_unsupported_key_type_error
end
def raise_unsupported_key_type_error
raise("Unsupported key type: #{key.class}")
2018-03-17 18:26:18 +05:30
end
end
end