Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(MODULES-10831) key is expired if all subkeys are expired #1104

Merged
merged 1 commit into from
May 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 37 additions & 31 deletions lib/puppet/provider/apt_key/apt_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,50 +8,46 @@
end
require 'tempfile'

Puppet::Type.type(:apt_key).provide(:apt_key) do
Puppet::Type.type(:apt_key).provide(:apt_key) do # rubocop:disable Metrics/BlockLength
desc 'apt-key provider for apt_key resource'

confine osfamily: :debian
defaultfor osfamily: :debian
commands apt_key: 'apt-key'
commands gpg: '/usr/bin/gpg'

def self.instances
def self.instances # rubocop:disable Metrics/AbcSize
key_array = []

cli_args = ['adv', '--no-tty', '--list-keys', '--with-colons', '--fingerprint', '--fixed-list-mode']

key_output = apt_key(cli_args).encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')

pub_line, sub_line, fpr_line = nil

key_array = key_output.split("\n").map do |line|
if line.start_with?('pub')
pub_line = line
# reset fpr_line, to skip any previous subkeys which were collected
fpr_line = nil
sub_line = nil
elsif line.start_with?('sub')
sub_line = line
elsif line.start_with?('fpr')
fpr_line = line
end

if sub_line && fpr_line
sub_line, fpr_line = nil
next
pub_line = nil
fpr_lines = []
sub_lines = []

lines = key_output.split("\n")

lines.each_index do |i|
if lines[i].start_with?('pub')
pub_line = lines[i]
# starting a new public key, so reset fpr_lines and sub_lines
fpr_lines = []
sub_lines = []
elsif lines[i].start_with?('fpr')
fpr_lines << lines[i]
elsif lines[i].start_with?('sub')
sub_lines << lines[i]
end

next unless pub_line && fpr_line

line_hash = key_line_hash(pub_line, fpr_line)

# reset everything
pub_line, fpr_line = nil
next unless (pub_line && !fpr_lines.empty?) && (!lines[i + 1] || lines[i + 1].start_with?('pub'))

expired = false
line_hash = key_line_hash(pub_line, fpr_lines)

expired = Time.now >= line_hash[:key_expiry] if line_hash[:key_expiry]
expired = line_hash[:key_expired] || subkeys_all_expired(sub_lines)

new(
key_array << new(
name: line_hash[:key_fingerprint],
id: line_hash[:key_long],
fingerprint: line_hash[:key_fingerprint],
Expand All @@ -65,7 +61,7 @@ def self.instances
created: line_hash[:key_created].strftime('%Y-%m-%d'),
)
end
key_array.compact!
key_array
end

def self.prefetch(resources)
Expand All @@ -85,9 +81,18 @@ def self.prefetch(resources)
end
end

def self.key_line_hash(pub_line, fpr_line)
def self.subkeys_all_expired(sub_lines)
return false if sub_lines.empty?

sub_lines.each do |line|
return false if line.split(':')[1] == '-'
end
true
end

def self.key_line_hash(pub_line, fpr_lines)
pub_split = pub_line.split(':')
fpr_split = fpr_line.split(':')
fpr_split = fpr_lines.first.split(':')

fingerprint = fpr_split.last
return_hash = {
Expand All @@ -97,6 +102,7 @@ def self.key_line_hash(pub_line, fpr_line)
key_size: pub_split[2],
key_type: nil,
key_created: Time.at(pub_split[5].to_i),
key_expired: pub_split[1] == 'e',
key_expiry: pub_split[6].empty? ? nil : Time.at(pub_split[6].to_i)
}

Expand Down
34 changes: 30 additions & 4 deletions spec/unit/puppet/provider/apt_key_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,34 @@
expect(described_class.instances.size).to eq(2)
expect(described_class.instances[0].name).to eq('630239CC130E1A7FD81A27B140976EAF437D05B5')
expect(described_class.instances[0].id).to eq('40976EAF437D05B5')
expect(described_class.instances[0].expired).to be_falsey
expect(described_class.instances[1].name).to eq('C5986B4F1257FFA86632CBA746181433FBB75451')
expect(described_class.instances[1].id).to eq('46181433FBB75451')
expect(described_class.instances[1].expired).to be_falsey
end
end

context 'with self.instances expired subkeys' do
before :each do
command_output = <<~OUTPUT
Executing: /tmp/apt-key-gpghome.0lru3TZOtF/gpg.1.sh --list-keys --with-colons --fingerprint 0x7721F63BD38B4796
tru:t:1:1682141947:0:3:1:5
pub:-:4096:1:7721F63BD38B4796:1460440275:::-:::scSC::::::23::0:
fpr:::::::::EB4C1BFD4F042F6DDDCCEC917721F63BD38B4796:
uid:-::::1460440275::7830FE2652F718E78EEE5881B7FA2CE8E3533BE4::Google Inc. (Linux Packages Signing Authority) <linux-packages-keymaster@google.com>::::::::::0:
sub:e:4096:1:1397BC53640DB551:1460440520:1555048520:::::s::::::23:
fpr:::::::::3B068FB4789ABE4AEFA3BB491397BC53640DB551:
sub:e:4096:1:6494C6D6997C215E:1485225932:1579833932:::::s::::::23:
fpr:::::::::3E50F6D3EC278FDEB655C8CA6494C6D6997C215E:
OUTPUT
allow(described_class).to receive(:apt_key).with(
['adv', '--no-tty', '--list-keys', '--with-colons', '--fingerprint', '--fixed-list-mode'],
).and_return(command_output)
end

it 'returns 1 expired resource' do
expect(described_class.instances.size).to eq(1)
expect(described_class.instances[0].expired).to be_truthy
end
end

Expand Down Expand Up @@ -174,7 +200,7 @@

context 'with key_line_hash function' do
it 'matches rsa' do
expect(described_class.key_line_hash('pub:-:1024:1:40976EAF437D05B5:1095016255:::-:::scESC:', 'fpr:::::::::630239CC130E1A7FD81A27B140976EAF437D05B5:')).to include(
expect(described_class.key_line_hash('pub:-:1024:1:40976EAF437D05B5:1095016255:::-:::scESC:', ['fpr:::::::::630239CC130E1A7FD81A27B140976EAF437D05B5:'])).to include(
key_expiry: nil,
key_fingerprint: '630239CC130E1A7FD81A27B140976EAF437D05B5',
key_long: '40976EAF437D05B5',
Expand All @@ -185,7 +211,7 @@
end

it 'matches dsa' do
expect(described_class.key_line_hash('pub:-:1024:17:40976EAF437D05B5:1095016255:::-:::scESC:', 'fpr:::::::::630239CC130E1A7FD81A27B140976EAF437D05B5:')).to include(
expect(described_class.key_line_hash('pub:-:1024:17:40976EAF437D05B5:1095016255:::-:::scESC:', ['fpr:::::::::630239CC130E1A7FD81A27B140976EAF437D05B5:'])).to include(
key_expiry: nil,
key_fingerprint: '630239CC130E1A7FD81A27B140976EAF437D05B5',
key_long: '40976EAF437D05B5',
Expand All @@ -196,7 +222,7 @@
end

it 'matches ecc' do
expect(described_class.key_line_hash('pub:-:1024:18:40976EAF437D05B5:1095016255:::-:::scESC:', 'fpr:::::::::630239CC130E1A7FD81A27B140976EAF437D05B5:')).to include(
expect(described_class.key_line_hash('pub:-:1024:18:40976EAF437D05B5:1095016255:::-:::scESC:', ['fpr:::::::::630239CC130E1A7FD81A27B140976EAF437D05B5:'])).to include(
key_expiry: nil,
key_fingerprint: '630239CC130E1A7FD81A27B140976EAF437D05B5',
key_long: '40976EAF437D05B5',
Expand All @@ -207,7 +233,7 @@
end

it 'matches ecdsa' do
expect(described_class.key_line_hash('pub:-:1024:19:40976EAF437D05B5:1095016255:::-:::scESC:', 'fpr:::::::::630239CC130E1A7FD81A27B140976EAF437D05B5:')).to include(
expect(described_class.key_line_hash('pub:-:1024:19:40976EAF437D05B5:1095016255:::-:::scESC:', ['fpr:::::::::630239CC130E1A7FD81A27B140976EAF437D05B5:'])).to include(
key_expiry: nil,
key_fingerprint: '630239CC130E1A7FD81A27B140976EAF437D05B5',
key_long: '40976EAF437D05B5',
Expand Down