summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Smith <tsmith@chef.io>2020-06-05 09:59:48 -0700
committerGitHub <noreply@github.com>2020-06-05 09:59:48 -0700
commit2e4f66d2588e6b87e82d4d94d0f51cb09d1ae39f (patch)
treef377f9169e3a32c059012f237c51e3e9059f1975
parent62a36c04a7b99494b84ae6f30c5a4e1c8cd13ce3 (diff)
parent41687b7de9f5c3d022bfc6f8293e33e80ce143db (diff)
downloadchef-2e4f66d2588e6b87e82d4d94d0f51cb09d1ae39f.tar.gz
Merge pull request #9956 from chef/zypper_repository
Fix zypper_repository key handling on SLES 15+
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/_zypper.rb13
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/linux.rb1
-rw-r--r--lib/chef/provider/zypper_repository.rb40
-rw-r--r--spec/unit/provider/zypper_repository_spec.rb70
4 files changed, 104 insertions, 20 deletions
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_zypper.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_zypper.rb
new file mode 100644
index 0000000000..0acca8e271
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/_zypper.rb
@@ -0,0 +1,13 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: _zypper
+#
+# Copyright:: Copyright (c) Chef Software Inc.
+#
+
+zypper_repository "nginx repo" do
+ baseurl "https://nginx.org/packages/sles/15"
+ gpgkey "https://nginx.org/keys/nginx_signing.key"
+end
+
+zypper_package "nginx"
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb b/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb
index b66edb14dd..f2ea241a14 100644
--- a/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb
@@ -108,6 +108,7 @@ locale "set system locale" do
end
include_recipe "::_apt" if platform_family?("debian")
+include_recipe "::_zypper" if suse?
include_recipe "::_chef-vault" unless includes_recipe?("end_to_end::chef-vault")
include_recipe "::_sudo"
include_recipe "::_sysctl"
diff --git a/lib/chef/provider/zypper_repository.rb b/lib/chef/provider/zypper_repository.rb
index 5dc5c999a4..4602d804b9 100644
--- a/lib/chef/provider/zypper_repository.rb
+++ b/lib/chef/provider/zypper_repository.rb
@@ -115,28 +115,48 @@ class Chef
end
end
+ # the version of gpg installed on the system
+ #
+ # @return [Gem::Version] the version of GPG
+ def gpg_version
+ so = shell_out!("gpg --version")
+ # matches 2.0 and 2.2 versions from SLES 12 and 15: https://rubular.com/r/e6D0WfGK6SXvUp
+ version = /gpg \(GnuPG\)\s*(.*)/.match(so.stdout)[1]
+ logger.trace("GPG package version is #{version}")
+ Gem::Version.new(version)
+ end
+
# is the provided key already installed
# @param [String] key_path the path to the key on the local filesystem
#
# @return [boolean] is the key already known by rpm
def key_installed?(key_path)
- so = shell_out("rpm -qa gpg-pubkey*")
+ so = shell_out("/bin/rpm -qa gpg-pubkey*")
# expected output & match: http://rubular.com/r/RdF7EcXEtb
- status = /gpg-pubkey-#{key_fingerprint(key_path)}/.match(so.stdout)
+ status = /gpg-pubkey-#{short_key_id(key_path)}/.match(so.stdout)
logger.trace("GPG key at #{key_path} is known by rpm? #{status ? "true" : "false"}")
status
end
- # extract the gpg key fingerprint from a local file
+ # extract the gpg key's short key id from a local file. Learning moment: This 8 hex value ID
+ # is sometimes incorrectly called the fingerprint. The fingerprint is the full length value
+ # and googling for that will just result in sad times.
+ #
# @param [String] key_path the path to the key on the local filesystem
#
- # @return [String] the fingerprint of the key
- def key_fingerprint(key_path)
- so = shell_out!("gpg --with-fingerprint #{key_path}")
- # expected output and match: http://rubular.com/r/BpfMjxySQM
- fingerprint = %r{pub\s*\S*/(\S*)}.match(so.stdout)[1].downcase
- logger.trace("GPG fingerprint of key at #{key_path} is #{fingerprint}")
- fingerprint
+ # @return [String] the short key id of the key
+ def short_key_id(key_path)
+ if gpg_version >= Gem::Version.new("2.2") # SLES 15+
+ so = shell_out!("gpg --import-options import-show --dry-run --import --with-colons #{key_path}")
+ # expected output and match: https://rubular.com/r/uXWJo3yfkli1qA
+ short_key_id = /fpr:*\h*(\h{8}):/.match(so.stdout)[1].downcase
+ else # SLES 12 and earlier
+ so = shell_out!("gpg --with-fingerprint #{key_path}")
+ # expected output and match: http://rubular.com/r/BpfMjxySQM
+ short_key_id = %r{pub\s*\S*/(\S*)}.match(so.stdout)[1].downcase
+ end
+ logger.trace("GPG short key ID of key at #{key_path} is #{short_key_id}")
+ short_key_id
end
# install the provided gpg key
diff --git a/spec/unit/provider/zypper_repository_spec.rb b/spec/unit/provider/zypper_repository_spec.rb
index 83ed83d297..f0686874e6 100644
--- a/spec/unit/provider/zypper_repository_spec.rb
+++ b/spec/unit/provider/zypper_repository_spec.rb
@@ -28,12 +28,40 @@ describe Chef::Provider::ZypperRepository do
# Output of the command:
# => gpg --with-fingerprint [FILE]
- ZYPPER_GPG_FINGER = <<~EOF.freeze
+ ZYPPER_GPG_20 = <<~EOF.freeze
pub 2048R/3DBDC284 2011-08-19 [expires: 2024-06-14]
Key fingerprint = 573B FD6B 3D8F BC64 1079 A6AB ABF5 BD82 7BD9 BF62
uid nginx signing key <signing-key@nginx.com>
EOF
+ # Output of the command:
+ # => gpg --import-options import-show --dry-run --import --with-colons [FILE]
+ ZYPPER_GPG_22 = <<~EOF.freeze
+ pub:-:2048:1:ABF5BD827BD9BF62:1313747554:1718374819::-:::scSC::::::23::0:
+ fpr:::::::::573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62:
+ uid:-::::1466086904::F18C4DBBFCB45099ABB59088DB6B252FA7E9FB41::nginx signing key <signing-key@nginx.com>::::::::::0:
+ gpg: Total number processed: 1
+ EOF
+
+ # Output of the command:
+ # -> gpg --version
+ ZYPPER_GPG_VERSION = <<~EOF.freeze
+ gpg (GnuPG) 2.2.20
+ libgcrypt 1.8.5
+ Copyright (C) 2020 Free Software Foundation, Inc.
+ License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
+ This is free software: you are free to change and redistribute it.
+ There is NO WARRANTY, to the extent permitted by law.
+
+ Home: /Users/tsmith/.gnupg
+ Supported algorithms:
+ Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
+ Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
+ CAMELLIA128, CAMELLIA192, CAMELLIA256
+ Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
+ Compression: Uncompressed, ZIP, ZLIB, BZIP2
+ EOF
+
let(:new_resource) { Chef::Resource::ZypperRepository.new("Nginx Repository") }
let(:logger) { double("Mixlib::Log::Child").as_null_object }
let(:provider) do
@@ -48,8 +76,16 @@ describe Chef::Provider::ZypperRepository do
double("shell_out", stdout: ZYPPER_RPM_KEYS, exitstatus: 0, error?: false)
end
- let(:gpg_finger) do
- double("shell_out", stdout: ZYPPER_GPG_FINGER, exitstatus: 0, error?: false)
+ let(:gpg_20) do
+ double("shell_out", stdout: ZYPPER_GPG_20, exitstatus: 0, error?: false)
+ end
+
+ let(:gpg_22) do
+ double("shell_out", stdout: ZYPPER_GPG_22, exitstatus: 0, error?: false)
+ end
+
+ let(:gpg_ver) do
+ double("shell_out", stdout: ZYPPER_GPG_VERSION, exitstatus: 0, error?: false)
end
it "responds to load_current_resource" do
@@ -96,24 +132,38 @@ describe Chef::Provider::ZypperRepository do
describe "#key_installed?" do
before do
- expect(provider).to receive(:shell_out).with("rpm -qa gpg-pubkey*").and_return(rpm_key_finger)
+ expect(provider).to receive(:shell_out).with("/bin/rpm -qa gpg-pubkey*").and_return(rpm_key_finger)
end
it "returns true if the key is installed" do
- expect(provider).to receive(:key_fingerprint).and_return("3dbdc284")
+ expect(provider).to receive(:short_key_id).and_return("3dbdc284")
expect(provider.key_installed?("/foo/nginx.key")).to be_truthy
end
it "returns false if the key is not installed" do
- expect(provider).to receive(:key_fingerprint).and_return("BOGUS")
+ expect(provider).to receive(:short_key_id).and_return("BOGUS")
expect(provider.key_installed?("/foo/nginx.key")).to be_falsey
end
end
- describe "#key_fingerprint" do
- it "returns the key's fingerprint" do
- expect(provider).to receive(:shell_out!).with("gpg --with-fingerprint /foo/nginx.key").and_return(gpg_finger)
- expect(provider.key_fingerprint("/foo/nginx.key")).to eq("3dbdc284")
+ describe "#gpg_version" do
+ it "returns the gpg version by shelling out to gpg" do
+ expect(provider).to receive(:shell_out!).with("gpg --version").and_return(gpg_ver)
+ expect(provider.gpg_version).to eq(Gem::Version.new("2.2.20"))
+ end
+ end
+
+ describe "#short_key_id" do
+ it "returns the short key ID via running a dry-run import on gpg 2.2+" do
+ expect(provider).to receive(:gpg_version).and_return(Gem::Version.new("2.2"))
+ expect(provider).to receive(:shell_out!).with("gpg --import-options import-show --dry-run --import --with-colons /foo/nginx.key").and_return(gpg_22)
+ expect(provider.short_key_id("/foo/nginx.key")).to eq("7bd9bf62")
+ end
+
+ it "returns the short key ID via --with-fingerpint on gpg < 2.2" do
+ expect(provider).to receive(:gpg_version).and_return(Gem::Version.new("2.0"))
+ expect(provider).to receive(:shell_out!).with("gpg --with-fingerprint /foo/nginx.key").and_return(gpg_20)
+ expect(provider.short_key_id("/foo/nginx.key")).to eq("3dbdc284")
end
end