Skip to content
This repository was archived by the owner on Jun 19, 2020. It is now read-only.

Commit 8aad8c3

Browse files
author
Oana Tanasoiu
committed
(FACT-2330) Add ssh fact for Windows OpenSSH feature
1 parent 43fc506 commit 8aad8c3

10 files changed

Lines changed: 338 additions & 97 deletions

File tree

.rubocop_todo.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# This configuration was generated by
22
# `rubocop --auto-gen-config --exclude-limit 1000`
3-
# on 2020-04-07 19:23:35 +0300 using RuboCop version 0.74.0.
3+
# on 2020-04-08 15:29:11 +0300 using RuboCop version 0.74.0.
44
# The point is for the user to remove these configuration records
55
# one by one as the offenses are removed from the code base.
66
# Note that changes in the inspected code, or installation of new
77
# versions of RuboCop, may require this file to be generated again.
88

9-
# Offense count: 79
9+
# Offense count: 80
1010
# Configuration parameters: CustomTransform, IgnoreMethods.
1111
RSpec/FilePath:
1212
Exclude:
@@ -57,6 +57,7 @@ RSpec/FilePath:
5757
- 'spec/facter/resolvers/system_profile_resolver_spec.rb'
5858
- 'spec/facter/resolvers/utils/aix/odm_query_spec.rb'
5959
- 'spec/facter/resolvers/utils/macosx/filesystem_helper_spec.rb'
60+
- 'spec/facter/resolvers/utils/ssh_helper_spec.rb'
6061
- 'spec/facter/resolvers/utils/windows/win32ole_spec.rb'
6162
- 'spec/facter/resolvers/windows/dmi_bios_resolver_spec.rb'
6263
- 'spec/facter/resolvers/windows/dmi_computersystem_resolver_spec.rb'
@@ -190,7 +191,7 @@ RSpec/SubjectStub:
190191
- 'spec/custom_facts/util/fact_spec.rb'
191192
- 'spec/custom_facts/util/resolution_spec.rb'
192193

193-
# Offense count: 193
194+
# Offense count: 187
194195
# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
195196
RSpec/VerifiedDoubles:
196197
Exclude:
@@ -233,7 +234,6 @@ RSpec/VerifiedDoubles:
233234
- 'spec/facter/resolvers/solaris/zfs_resolver_spec.rb'
234235
- 'spec/facter/resolvers/solaris/zone_resolver_spec.rb'
235236
- 'spec/facter/resolvers/solaris/zpool_resolver_spec.rb'
236-
- 'spec/facter/resolvers/ssh_resolver_spec.rb'
237237
- 'spec/facter/resolvers/utils/windows/network_utils_spec.rb'
238238
- 'spec/facter/resolvers/utils/windows/win32ole_spec.rb'
239239
- 'spec/facter/resolvers/windows/dmi_bios_resolver_spec.rb'

lib/facts/windows/ssh.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# frozen_string_literal: true
2+
3+
module Facts
4+
module Windows
5+
class Ssh
6+
FACT_NAME = 'ssh'
7+
8+
def call_the_resolver
9+
privileged = Facter::Resolvers::Identity.resolve(:privileged)
10+
ssh_info = Facter::Resolvers::Windows::Ssh.resolve(:ssh) if privileged
11+
ssh_facts = {}
12+
ssh_info&.each { |ssh| ssh_facts.merge!(create_ssh_fact(ssh)) }
13+
Facter::ResolvedFact.new(FACT_NAME, ssh_facts.empty? ? nil : ssh_facts)
14+
end
15+
16+
private
17+
18+
def create_ssh_fact(ssh)
19+
{ ssh.name.to_sym =>
20+
{ fingerprints: { sha1: ssh.fingerprint.sha1,
21+
sha256: ssh.fingerprint.sha256 },
22+
key: ssh.key,
23+
type: ssh.type } }
24+
end
25+
end
26+
end
27+
end

lib/framework/core/file_loader.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def load_lib_dirs(*dirs)
3232
require "#{ROOT_DIR}/lib/framework/config/block_list"
3333
require "#{ROOT_DIR}/lib/resolvers/utils/fingerprint.rb"
3434
require "#{ROOT_DIR}/lib/resolvers/utils/ssh.rb"
35+
require "#{ROOT_DIR}/lib/resolvers/utils/ssh_helper.rb"
3536
require "#{ROOT_DIR}/lib/resolvers/utils/filesystem_helper.rb"
3637

3738
load_dir(['config'])

lib/resolvers/ssh_resolver.rb

Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
# frozen_string_literal: true
22

3-
require 'base64'
4-
require 'digest/sha1'
5-
63
module Facter
74
module Resolvers
85
class SshResolver < BaseResolver
@@ -24,52 +21,15 @@ def retrieve_info(fact_name)
2421
next unless File.directory?(file_path)
2522

2623
@file_names.each do |file_name|
27-
next unless FileTest.file?(File.join(file_path, file_name))
24+
next unless File.readable?(File.join(file_path, file_name))
2825

2926
key_type, key = File.read(File.join(file_path, file_name)).split(' ')
30-
key_name = determine_ssh_key_name(key_type)
31-
ssh_list << create_ssh(key_name, key_type, key)
27+
ssh_list << SshHelper.create_ssh(key_type, key)
3228
end
3329
end
3430
@fact_list[:ssh] = ssh_list
3531
@fact_list[fact_name]
3632
end
37-
38-
def create_ssh(key_name, key_type, key)
39-
decoded_key = Base64.decode64(key)
40-
ssh_fa = determine_ssh_fingerprint(key_name)
41-
sha1 = "SSHFP #{ssh_fa} 1 #{Digest::SHA1.new.update(decoded_key)}"
42-
sha256 = "SSHFP #{ssh_fa} 2 #{Digest::SHA2.new.update(decoded_key)}"
43-
44-
fingerprint = FingerPrint.new(sha1, sha256)
45-
Ssh.new(fingerprint, key_type, key, key_name)
46-
end
47-
48-
def determine_ssh_key_name(key)
49-
case key
50-
when 'ssh-dss'
51-
'dsa'
52-
when 'ecdsa-sha2-nistp256'
53-
'ecdsa'
54-
when 'ssh-ed25519'
55-
'ed25519'
56-
when 'ssh-rsa'
57-
'rsa'
58-
end
59-
end
60-
61-
def determine_ssh_fingerprint(key_name)
62-
case key_name
63-
when 'rsa'
64-
1
65-
when 'dsa'
66-
2
67-
when 'ecdsa'
68-
3
69-
when 'ed25519'
70-
4
71-
end
72-
end
7333
end
7434
end
7535
end

lib/resolvers/utils/ssh_helper.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# frozen_string_literal: true
2+
3+
require 'base64'
4+
require 'digest/sha1'
5+
6+
module Facter
7+
class SshHelper
8+
class << self
9+
SSH_NAME = { 'ssh-dss' => 'dsa', 'ecdsa-sha2-nistp256' => 'ecdsa',
10+
'ssh-ed25519' => 'ed25519', 'ssh-rsa' => 'rsa' }.freeze
11+
SSH_FINGERPRINT = { 'rsa' => 1, 'dsa' => 2, 'ecdsa' => 3, 'ed25519' => 4 }.freeze
12+
13+
def create_ssh(key_type, key)
14+
key_name = SSH_NAME[key]
15+
decoded_key = Base64.decode64(key)
16+
ssh_fp = SSH_FINGERPRINT[key_name]
17+
sha1 = "SSHFP #{ssh_fp} 1 #{Digest::SHA1.new.update(decoded_key)}"
18+
sha256 = "SSHFP #{ssh_fp} 2 #{Digest::SHA2.new.update(decoded_key)}"
19+
20+
fingerprint = FingerPrint.new(sha1, sha256)
21+
Ssh.new(fingerprint, key_type, key, key_name)
22+
end
23+
end
24+
end
25+
end

lib/resolvers/windows/ssh.rb

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# frozen_string_literal: true
2+
3+
module Facter
4+
module Resolvers
5+
module Windows
6+
class Ssh < BaseResolver
7+
@log = Facter::Log.new(self)
8+
@semaphore = Mutex.new
9+
@fact_list ||= {}
10+
FILE_NAMES = %w[ssh_host_rsa_key.pub ssh_host_dsa_key.pub
11+
ssh_host_ecdsa_key.pub ssh_host_ed25519_key.pub].freeze
12+
class << self
13+
private
14+
15+
def post_resolve(fact_name)
16+
@fact_list.fetch(fact_name) { retrieve_info(fact_name) }
17+
end
18+
19+
def retrieve_info(fact_name)
20+
ssh_dir = determine_ssh_dir
21+
return unless ssh_dir && File.directory?(ssh_dir)
22+
23+
ssh_list = []
24+
25+
FILE_NAMES.each do |file_name|
26+
next unless File.readable?(File.join(ssh_dir, file_name))
27+
28+
key_type, key = File.read(File.join(ssh_dir, file_name)).split(' ')
29+
ssh_list << SshHelper.create_ssh(key_type, key)
30+
end
31+
@fact_list[:ssh] = ssh_list.empty? ? nil : ssh_list
32+
@fact_list[fact_name]
33+
end
34+
35+
def determine_ssh_dir
36+
progdata_dir = ENV['programdata']
37+
38+
return if !progdata_dir || progdata_dir.empty?
39+
40+
File.join(progdata_dir, 'ssh')
41+
end
42+
end
43+
end
44+
end
45+
end
46+
end
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# frozen_string_literal: true
2+
3+
describe Facts::Windows::Ssh do
4+
describe '#call_the_resolver' do
5+
subject(:fact) { Facts::Windows::Ssh.new }
6+
7+
context 'when user is privileged' do
8+
let(:ssh) do
9+
[Facter::Ssh.new(Facter::FingerPrint.new('test', 'test'), 'ecdsa', 'test', 'ecdsa')]
10+
end
11+
let(:value) do
12+
{ 'ecdsa' => { 'fingerprints' =>
13+
{ 'sha1' => 'test', 'sha256' => 'test' },
14+
'key' => 'test',
15+
'type' => 'ecdsa' } }
16+
end
17+
18+
before do
19+
allow(Facter::Resolvers::Identity).to receive(:resolve).with(:privileged).and_return(true)
20+
allow(Facter::Resolvers::Windows::Ssh).to receive(:resolve).with(:ssh).and_return(ssh)
21+
end
22+
23+
it 'calls Facter::Resolvers::Windows::Ssh' do
24+
fact.call_the_resolver
25+
expect(Facter::Resolvers::Windows::Ssh).to have_received(:resolve).with(:ssh)
26+
end
27+
28+
it 'calls Facter::Resolvers::Windows::Identity' do
29+
fact.call_the_resolver
30+
expect(Facter::Resolvers::Identity).to have_received(:resolve).with(:privileged)
31+
end
32+
33+
it 'returns ssh fact' do
34+
expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
35+
have_attributes(name: 'ssh', value: value)
36+
end
37+
end
38+
39+
context 'when user is privileged but no ssh key found' do
40+
let(:value) { nil }
41+
42+
before do
43+
allow(Facter::Resolvers::Identity).to receive(:resolve).with(:privileged).and_return(true)
44+
allow(Facter::Resolvers::Windows::Ssh).to receive(:resolve).with(:ssh).and_return({})
45+
end
46+
47+
it 'calls Facter::Resolvers::Windows::Ssh' do
48+
fact.call_the_resolver
49+
expect(Facter::Resolvers::Windows::Ssh).to have_received(:resolve).with(:ssh)
50+
end
51+
52+
it 'calls Facter::Resolvers::Windows::Identity' do
53+
fact.call_the_resolver
54+
expect(Facter::Resolvers::Identity).to have_received(:resolve).with(:privileged)
55+
end
56+
57+
it 'returns ssh fact' do
58+
expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
59+
have_attributes(name: 'ssh', value: value)
60+
end
61+
end
62+
63+
context 'when user is not privileged' do
64+
let(:value) { nil }
65+
66+
before do
67+
allow(Facter::Resolvers::Identity).to receive(:resolve).with(:privileged).and_return(false)
68+
allow(Facter::Resolvers::Windows::Ssh).to receive(:resolve).with(:ssh).and_return(value)
69+
end
70+
71+
it "doesn't call Facter::Resolvers::Windows::Ssh" do
72+
fact.call_the_resolver
73+
expect(Facter::Resolvers::Windows::Ssh).not_to have_received(:resolve).with(:ssh)
74+
end
75+
76+
it 'calls Facter::Resolvers::Windows::Identity' do
77+
fact.call_the_resolver
78+
expect(Facter::Resolvers::Identity).to have_received(:resolve).with(:privileged)
79+
end
80+
81+
it 'returns ssh fact' do
82+
expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
83+
have_attributes(name: 'ssh', value: value)
84+
end
85+
end
86+
end
87+
end

0 commit comments

Comments
 (0)