Class: Gitlab::SSHPublicKey

Inherits:
Object
  • Object
show all
Includes:
Utils::StrongMemoize
Defined in:
lib/gitlab/ssh_public_key.rb

Direct Known Subclasses

SshHostKey::Fingerprint

Defined Under Namespace

Classes: Technology, WeakKeyWarning

Constant Summary collapse

TECHNOLOGIES =

See man.openbsd.org/sshd#AUTHORIZED_KEYS_FILE_FORMAT for the list of supported algorithms.

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[[email protected]]),
  Technology.new(:ed25519_sk, SSHData::PublicKey::SKED25519, [256], %w[[email protected]])
].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key_text) ⇒ SSHPublicKey

Returns a new instance of SSHPublicKey.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/gitlab/ssh_public_key.rb', line 71

def initialize(key_text)
  @key_text = key_text

  # 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

  @key =
    begin
      SSHData::PublicKey.parse_openssh(key_text_without_options)
    rescue SSHData::DecodeError
    end
end

Instance Attribute Details

#keyObject (readonly)

Returns the value of attribute key.



69
70
71
# File 'lib/gitlab/ssh_public_key.rb', line 69

def key
  @key
end

#key_textObject (readonly)

Returns the value of attribute key_text.



69
70
71
# File 'lib/gitlab/ssh_public_key.rb', line 69

def key_text
  @key_text
end

Class Method Details

.sanitize(key_content) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/gitlab/ssh_public_key.rb', line 53

def self.sanitize(key_content)
  ssh_type, *parts = key_content.strip.split

  return key_content if parts.empty?

  parts.each_with_object("#{ssh_type} ").with_index do |(part, content), index|
    content << part

    if self.new(content).valid?
      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

.supported_algorithmsObject



45
46
47
# File 'lib/gitlab/ssh_public_key.rb', line 45

def self.supported_algorithms
  technologies.flat_map { |tech| tech.supported_algorithms }
end

.supported_algorithms_for_name(name) ⇒ Object



49
50
51
# File 'lib/gitlab/ssh_public_key.rb', line 49

def self.supported_algorithms_for_name(name)
  technology(name).supported_algorithms
end

.supported_sizes(name) ⇒ Object



41
42
43
# File 'lib/gitlab/ssh_public_key.rb', line 41

def self.supported_sizes(name)
  technology(name).supported_sizes
end

.supported_typesObject



37
38
39
# File 'lib/gitlab/ssh_public_key.rb', line 37

def self.supported_types
  technologies.map(&:name)
end

.technologiesObject



21
22
23
24
25
26
27
# File 'lib/gitlab/ssh_public_key.rb', line 21

def self.technologies
  if Gitlab::FIPS.enabled?
    Gitlab::FIPS::SSH_KEY_TECHNOLOGIES
  else
    TECHNOLOGIES
  end
end

.technology(name) ⇒ Object



29
30
31
# File 'lib/gitlab/ssh_public_key.rb', line 29

def self.technology(name)
  technologies.find { |tech| tech.name.to_s == name.to_s }
end

.technology_for_key(key) ⇒ Object



33
34
35
# File 'lib/gitlab/ssh_public_key.rb', line 33

def self.technology_for_key(key)
  technologies.find { |tech| key.instance_of?(tech.key_class) }
end

Instance Method Details

#banned?Boolean

Returns:

  • (Boolean)


121
122
123
124
125
# File 'lib/gitlab/ssh_public_key.rb', line 121

def banned?
  return false unless valid?

  banned_ssh_keys.fetch(type.to_s, []).include?(fingerprint_sha256)
end

#bitsObject



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/gitlab/ssh_public_key.rb', line 102

def bits
  return unless valid?

  case type
  when :rsa
    key.n.num_bits
  when :dsa
    key.p.num_bits
  when :ecdsa
    key.openssl.group.order.num_bits
  when :ed25519
    256
  when :ecdsa_sk
    256
  when :ed25519_sk
    256
  end
end

#fingerprintObject



94
95
96
# File 'lib/gitlab/ssh_public_key.rb', line 94

def fingerprint
  key.fingerprint(md5: true) if valid?
end

#fingerprint_sha256Object



98
99
100
# File 'lib/gitlab/ssh_public_key.rb', line 98

def fingerprint_sha256
  'SHA256:' + key.fingerprint(md5: false) if valid?
end

#typeObject



90
91
92
# File 'lib/gitlab/ssh_public_key.rb', line 90

def type
  technology.name if valid?
end

#valid?Boolean

Returns:

  • (Boolean)


86
87
88
# File 'lib/gitlab/ssh_public_key.rb', line 86

def valid?
  key.present?
end

#weak_key_warningObject



127
128
129
130
131
132
133
134
135
# File 'lib/gitlab/ssh_public_key.rb', line 127

def weak_key_warning
  return unless valid?

  if type == :dsa
    WeakKeyWarning.new(:dsa_deprecated, _('DSA keys are considered deprecated.'))
  elsif type == :rsa && bits < 2048
    WeakKeyWarning.new(:small_key, _('Key length should be at least 2048 bits.'))
  end
end