From e6d82e7d35eb8240897af68b389f6f182c9430a9 Mon Sep 17 00:00:00 2001 From: Ramesh Sencha Date: Fri, 27 Oct 2023 14:29:12 +0530 Subject: [PATCH] Merge pull request #1128 from puppetlabs/modern_debian_keyrings Add support for modern keyrings --- Gemfile | 24 +++- README.md | 38 +++++ REFERENCE.md | 215 ++++++++++++++++++++++------ manifests/init.pp | 73 +++++----- manifests/keyring.pp | 74 ++++++++++ manifests/source.pp | 137 ++++++++++++------ spec/acceptance/apt_keyring_spec.rb | 28 ++++ spec/defines/keyring_spec.rb | 20 +++ 8 files changed, 482 insertions(+), 127 deletions(-) create mode 100644 manifests/keyring.pp create mode 100644 spec/acceptance/apt_keyring_spec.rb create mode 100644 spec/defines/keyring_spec.rb diff --git a/Gemfile b/Gemfile index 135373d02b..3b2257505f 100644 --- a/Gemfile +++ b/Gemfile @@ -17,14 +17,32 @@ ruby_version_segments = Gem::Version.new(RUBY_VERSION.dup).segments minor_version = ruby_version_segments[0..1].join('.') group :development do - gem "json", '= 2.0.4', require: false if Gem::Requirement.create('~> 2.4.2').satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) - gem "json", '= 2.1.0', require: false if Gem::Requirement.create(['>= 2.5.0', '< 2.7.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) - gem "json", '= 2.3.0', require: false if Gem::Requirement.create(['>= 2.7.0', '< 2.8.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) gem "puppet-module-posix-default-r#{minor_version}", '~> 1.0', require: false, platforms: [:ruby] gem "puppet-module-posix-dev-r#{minor_version}", '~> 1.0', require: false, platforms: [:ruby] gem "puppet-module-win-default-r#{minor_version}", '~> 1.0', require: false, platforms: [:mswin, :mingw, :x64_mingw] gem "puppet-module-win-dev-r#{minor_version}", '~> 1.0', require: false, platforms: [:mswin, :mingw, :x64_mingw] gem "github_changelog_generator", require: false + gem "json", '= 2.0.4', require: false if Gem::Requirement.create('~> 2.4.2').satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "json", '= 2.1.0', require: false if Gem::Requirement.create(['>= 2.5.0', '< 2.7.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "json", '= 2.3.0', require: false if Gem::Requirement.create(['>= 2.7.0', '< 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "json", '= 2.5.1', require: false if Gem::Requirement.create(['>= 3.0.0', '< 3.0.5']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "json", '= 2.6.1', require: false if Gem::Requirement.create(['>= 3.1.0', '< 3.1.3']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "json", '= 2.6.3', require: false if Gem::Requirement.create(['>= 3.2.0', '< 4.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "voxpupuli-puppet-lint-plugins", '~> 4.0', require: false + gem "facterdb", '~> 1.18', require: false + gem "metadata-json-lint", '~> 3.0', require: false + gem "puppetlabs_spec_helper", '~> 7.0', require: false + gem "rspec-puppet-facts", '~> 2.0', require: false + gem "codecov", '~> 0.2', require: false + gem "dependency_checker", '~> 1.0.0', require: false + gem "parallel_tests", '= 3.12.1', require: false + gem "pry", '~> 0.10', require: false + gem "simplecov-console", '~> 0.5', require: false + gem "puppet-debugger", '~> 1.0', require: false + gem "rubocop", '= 1.48.1', require: false + gem "rubocop-performance", '= 1.16.0', require: false + gem "rubocop-rspec", '= 2.19.0', require: false + gem "rb-readline", '= 0.5.5', require: false, platforms: [:mswin, :mingw, :x64_mingw] end group :system_tests do gem "puppet-module-posix-system-r#{minor_version}", '~> 1.0', require: false, platforms: [:ruby] diff --git a/README.md b/README.md index c37101f07b..87c17e4322 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,28 @@ include apt ### Add GPG keys +You can fetch GPG keys via HTTP, Puppet URI, or local filesystem. The key can be in GPG binary format, or ASCII armored, but the filename should have the appropriate extension (`.gpg` for keys in binary format; or `.asc` for ASCII armored keys). + +#### Fetch via HTTP + +```puppet +apt::keyring { 'puppetlabs-keyring.gpg': + source => 'https://apt.puppetlabs.com/keyring.gpg', +} +``` + +#### Fetch via Puppet URI + +```puppet +apt::keyring { 'puppetlabs-keyring.gpg': + source => 'puppet:///modules/my_module/local_puppetlabs-keyring.gpg', +} +``` + +Alternatively `apt::key` can be used. + +**Warning** `apt::key` is deprecated in the latest Debian and Ubuntu releases. Please use apt::keyring instead. + **Warning:** Using short key IDs presents a serious security issue, potentially leaving you open to collision attacks. We recommend you always use full fingerprints to identify your GPG keys. This module allows short keys, but issues a security warning if you use them. Declare the `apt::key` defined type: @@ -184,6 +206,22 @@ apt::source { 'puppetlabs': } ``` +### Adding name and source to the key parameter of apt::source, which then manages modern apt gpg keyrings + +The `name` parameter of key hash should contain the filename with extension (such as `puppetlabs.gpg`). + +```puppet +apt::source { 'puppetlabs': + comment => 'Puppet8', + location => 'https://apt.puppetlabs.com/', + repos => 'puppet8', + key => { + 'name' => 'puppetlabs.gpg', + 'source' => 'https://apt.puppetlabs.com/keyring.gpg', + }, +} +``` + ### Configure Apt from Hiera diff --git a/REFERENCE.md b/REFERENCE.md index 674ec22f05..3a92a7b0ae 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -18,13 +18,14 @@ ### Defined types -* [`apt::conf`](#aptconf): Specifies a custom Apt configuration file. -* [`apt::key`](#aptkey): Manages the GPG keys that Apt uses to authenticate packages. -* [`apt::mark`](#aptmark): Manages apt-mark settings -* [`apt::pin`](#aptpin): Manages Apt pins. Does not trigger an apt-get update run. -* [`apt::ppa`](#aptppa): Manages PPA repositories using `add-apt-repository`. Not supported on Debian. -* [`apt::setting`](#aptsetting): Manages Apt configuration files. -* [`apt::source`](#aptsource): Manages the Apt sources in /etc/apt/sources.list.d/. +* [`apt::conf`](#apt--conf): Specifies a custom Apt configuration file. +* [`apt::key`](#apt--key): Manages the GPG keys that Apt uses to authenticate packages. +* [`apt::keyring`](#apt--keyring): Manage GPG keyrings for apt repositories +* [`apt::mark`](#apt--mark): Manages apt-mark settings +* [`apt::pin`](#apt--pin): Manages Apt pins. Does not trigger an apt-get update run. +* [`apt::ppa`](#apt--ppa): Manages PPA repositories using `add-apt-repository`. Not supported on Debian. +* [`apt::setting`](#apt--setting): Manages Apt configuration files. +* [`apt::source`](#apt--source): Manages the Apt sources in /etc/apt/sources.list.d/. ### Resource types @@ -61,38 +62,39 @@ Main class, includes all other classes. The following parameters are available in the `apt` class: -* [`provider`](#provider) -* [`keyserver`](#keyserver) -* [`key_options`](#key_options) -* [`ppa_options`](#ppa_options) -* [`ppa_package`](#ppa_package) -* [`backports`](#backports) -* [`confs`](#confs) -* [`update`](#update) -* [`purge`](#purge) -* [`proxy`](#proxy) -* [`sources`](#sources) -* [`keys`](#keys) -* [`ppas`](#ppas) -* [`pins`](#pins) -* [`settings`](#settings) -* [`manage_auth_conf`](#manage_auth_conf) -* [`auth_conf_entries`](#auth_conf_entries) -* [`auth_conf_owner`](#auth_conf_owner) -* [`root`](#root) -* [`sources_list`](#sources_list) -* [`sources_list_d`](#sources_list_d) -* [`conf_d`](#conf_d) -* [`preferences`](#preferences) -* [`preferences_d`](#preferences_d) -* [`config_files`](#config_files) -* [`sources_list_force`](#sources_list_force) -* [`update_defaults`](#update_defaults) -* [`purge_defaults`](#purge_defaults) -* [`proxy_defaults`](#proxy_defaults) -* [`include_defaults`](#include_defaults) -* [`apt_conf_d`](#apt_conf_d) -* [`source_key_defaults`](#source_key_defaults) +* [`provider`](#-apt--provider) +* [`keyserver`](#-apt--keyserver) +* [`key_options`](#-apt--key_options) +* [`ppa_options`](#-apt--ppa_options) +* [`ppa_package`](#-apt--ppa_package) +* [`backports`](#-apt--backports) +* [`confs`](#-apt--confs) +* [`update`](#-apt--update) +* [`update_defaults`](#-apt--update_defaults) +* [`purge`](#-apt--purge) +* [`purge_defaults`](#-apt--purge_defaults) +* [`proxy`](#-apt--proxy) +* [`proxy_defaults`](#-apt--proxy_defaults) +* [`sources`](#-apt--sources) +* [`keys`](#-apt--keys) +* [`keyrings`](#-apt--keyrings) +* [`ppas`](#-apt--ppas) +* [`pins`](#-apt--pins) +* [`settings`](#-apt--settings) +* [`manage_auth_conf`](#-apt--manage_auth_conf) +* [`auth_conf_entries`](#-apt--auth_conf_entries) +* [`auth_conf_owner`](#-apt--auth_conf_owner) +* [`root`](#-apt--root) +* [`sources_list`](#-apt--sources_list) +* [`sources_list_d`](#-apt--sources_list_d) +* [`conf_d`](#-apt--conf_d) +* [`preferences`](#-apt--preferences) +* [`preferences_d`](#-apt--preferences_d) +* [`config_files`](#-apt--config_files) +* [`sources_list_force`](#-apt--sources_list_force) +* [`include_defaults`](#-apt--include_defaults) +* [`apt_conf_d`](#-apt--apt_conf_d) +* [`source_key_defaults`](#-apt--source_key_defaults) ##### `provider` @@ -214,7 +216,15 @@ Creates new `apt::key` resources. Valid options: a hash to be passed to the crea Default value: `$apt::params::keys` -##### `ppas` +##### `keyrings` + +Data type: `Hash` + +Hash of `apt::keyring` resources. + +Default value: `{}` + +##### `ppas` Data type: `Hash` @@ -619,7 +629,102 @@ Passes additional options to `apt-key adv --keyserver-options`. Default value: `$::apt::key_options` -### `apt::mark` +### `apt::keyring` + +Manage GPG keyrings for apt repositories + +#### Examples + +##### Download the puppetlabs apt keyring + +```puppet +apt::keyring { 'puppetlabs-keyring.gpg': + source => 'https://apt.puppetlabs.com/keyring.gpg', +} +``` + +##### Deploy the apt source and associated keyring file + +```puppet +apt::source { 'puppet8-release': + location => 'http://apt.puppetlabs.com', + repos => 'puppet8', + key => { + name => 'puppetlabs-keyring.gpg', + source => 'https://apt.puppetlabs.com/keyring.gpg' + } +} +``` + +#### Parameters + +The following parameters are available in the `apt::keyring` defined type: + +* [`keyring_dir`](#-apt--keyring--keyring_dir) +* [`keyring_filename`](#-apt--keyring--keyring_filename) +* [`keyring_file`](#-apt--keyring--keyring_file) +* [`keyring_file_mode`](#-apt--keyring--keyring_file_mode) +* [`source`](#-apt--keyring--source) +* [`content`](#-apt--keyring--content) +* [`ensure`](#-apt--keyring--ensure) + +##### `keyring_dir` + +Data type: `Stdlib::Absolutepath` + +Path to the directory where the keyring will be stored. + +Default value: `'/etc/apt/keyrings'` + +##### `keyring_filename` + +Data type: `String[1]` + +Optional filename for the keyring. It should also contain extension along with the filename. + +Default value: `$name` + +##### `keyring_file` + +Data type: `Stdlib::Absolutepath` + +File path of the keyring. + +Default value: `"${keyring_dir}/${keyring_filename}"` + +##### `keyring_file_mode` + +Data type: `Stdlib::Filemode` + +File permissions of the keyring. + +Default value: `'0644'` + +##### `source` + +Data type: `Optional[Stdlib::Filesource]` + +Source of the keyring file. Mutually exclusive with 'content'. + +Default value: `undef` + +##### `content` + +Data type: `Optional[String[1]]` + +Content of the keyring file. Mutually exclusive with 'source'. + +Default value: `undef` + +##### `ensure` + +Data type: `Enum['present','absent']` + +Ensure presence or absence of the resource. + +Default value: `'present'` + +### `apt::mark` Manages apt-mark settings @@ -920,6 +1025,20 @@ apt::source { 'puppetlabs': } ``` +##### Download key behaviour to handle modern apt gpg keyrings. The `name` parameter in the key hash should be given with + +```puppet +extension. Absence of extension will result in file formation with just name and no extension. +apt::source { 'puppetlabs': + location => 'http://apt.puppetlabs.com', + comment => 'Puppet8', + key => { + 'name' => 'puppetlabs.gpg', + 'source' => 'https://apt.puppetlabs.com/keyring.gpg', + }, +} +``` + #### Parameters The following parameters are available in the `apt::source` defined type: @@ -994,9 +1113,12 @@ Default value: `{}` Data type: `Optional[Variant[String, Hash]]` -Creates a declaration of the apt::key defined type. Valid options: a string to be passed to the `id` parameter of the `apt::key` -defined type, or a hash of `parameter => value` pairs to be passed to `apt::key`'s `id`, `server`, `content`, `source`, `weak_ssl`, -and/or `options` parameters. +Creates an `apt::keyring` in `/etc/apt/keyrings` (or anywhere on disk given `filename`) Valid options: + * a hash of `parameter => value` pairs to be passed to `file`: `name` (title), `content`, `source`, `filename` + +The following inputs are valid for the (deprecated) `apt::key` defined type. Valid options: + * a string to be passed to the `id` parameter of the `apt::key` defined type + * a hash of `parameter => value` pairs to be passed to `apt::key`: `id`, `server`, `content`, `source`, `weak_ssl`, `options` Default value: ``undef`` @@ -1005,6 +1127,7 @@ Default value: ``undef`` Data type: `Optional[Stdlib::AbsolutePath]` Absolute path to a file containing the PGP keyring used to sign this repository. Value is used to set signed-by on the source entry. +This is not necessary if the key is installed with `key` param above. See https://wiki.debian.org/DebianRepository/UseThirdParty for details. Default value: ``undef`` @@ -1023,8 +1146,8 @@ Default value: ``undef`` Data type: `Optional[String]` Tells Apt to only download information for specified architectures. Valid options: a string containing one or more architecture names, -separated by commas (e.g., 'i386' or 'i386,alpha,powerpc'). Default: undef (if unspecified, Apt downloads information for all architectures -defined in the Apt::Architectures option). +separated by commas (e.g., 'i386' or 'i386,alpha,powerpc'). +(if unspecified, Apt downloads information for all architectures defined in the Apt::Architectures option) Default value: ``undef`` diff --git a/manifests/init.pp b/manifests/init.pp index 49f93093ce..ef3d51d1fc 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -75,6 +75,9 @@ # @param keys # Creates new `apt::key` resources. Valid options: a hash to be passed to the create_resources function linked above. # +# @param keyrings +# Hash of `apt::keyring` resources. +# # @param ppas # Creates new `apt::ppa` resources. Valid options: a hash to be passed to the create_resources function linked above. # @@ -122,38 +125,38 @@ # Specifies whether to perform force purge or delete. Default false. # class apt ( - Hash $update_defaults = $apt::params::update_defaults, - Hash $purge_defaults = $apt::params::purge_defaults, - Hash $proxy_defaults = $apt::params::proxy_defaults, - Hash $include_defaults = $apt::params::include_defaults, - String $provider = $apt::params::provider, - String $keyserver = $apt::params::keyserver, - Optional[String] $key_options = $apt::params::key_options, - Optional[String] $ppa_options = $apt::params::ppa_options, - Optional[String] $ppa_package = $apt::params::ppa_package, - Optional[Hash] $backports = $apt::params::backports, - Hash $confs = $apt::params::confs, - Hash $update = $apt::params::update, - Hash $purge = $apt::params::purge, - Apt::Proxy $proxy = $apt::params::proxy, - Hash $sources = $apt::params::sources, - Hash $keys = $apt::params::keys, - Hash $ppas = $apt::params::ppas, - Hash $pins = $apt::params::pins, - Hash $settings = $apt::params::settings, - Boolean $manage_auth_conf = $apt::params::manage_auth_conf, - Array[Apt::Auth_conf_entry] - $auth_conf_entries = $apt::params::auth_conf_entries, - String $auth_conf_owner = $apt::params::auth_conf_owner, - String $root = $apt::params::root, - String $sources_list = $apt::params::sources_list, - String $sources_list_d = $apt::params::sources_list_d, - String $conf_d = $apt::params::conf_d, - String $preferences = $apt::params::preferences, - String $preferences_d = $apt::params::preferences_d, - String $apt_conf_d = $apt::params::apt_conf_d, - Hash $config_files = $apt::params::config_files, - Boolean $sources_list_force = $apt::params::sources_list_force, + Hash $update_defaults = $apt::params::update_defaults, + Hash $purge_defaults = $apt::params::purge_defaults, + Hash $proxy_defaults = $apt::params::proxy_defaults, + Hash $include_defaults = $apt::params::include_defaults, + String $provider = $apt::params::provider, + String $keyserver = $apt::params::keyserver, + Optional[String] $key_options = $apt::params::key_options, + Optional[Array[String]] $ppa_options = $apt::params::ppa_options, + Optional[String] $ppa_package = $apt::params::ppa_package, + Optional[Hash] $backports = $apt::params::backports, + Hash $confs = $apt::params::confs, + Hash $update = $apt::params::update, + Hash $purge = $apt::params::purge, + Apt::Proxy $proxy = $apt::params::proxy, + Hash $sources = $apt::params::sources, + Hash $keys = $apt::params::keys, + Hash $keyrings = {}, + Hash $ppas = $apt::params::ppas, + Hash $pins = $apt::params::pins, + Hash $settings = $apt::params::settings, + Boolean $manage_auth_conf = $apt::params::manage_auth_conf, + Array[Apt::Auth_conf_entry] $auth_conf_entries = $apt::params::auth_conf_entries, + String $auth_conf_owner = $apt::params::auth_conf_owner, + String $root = $apt::params::root, + String $sources_list = $apt::params::sources_list, + String $sources_list_d = $apt::params::sources_list_d, + String $conf_d = $apt::params::conf_d, + String $preferences = $apt::params::preferences, + String $preferences_d = $apt::params::preferences_d, + String $apt_conf_d = $apt::params::apt_conf_d, + Hash $config_files = $apt::params::config_files, + Boolean $sources_list_force = $apt::params::sources_list_force, Hash $source_key_defaults = { 'server' => $keyserver, @@ -336,6 +339,12 @@ if $keys { create_resources('apt::key', $keys) } + # manage keyrings if present + $keyrings.each |$key, $data| { + apt::keyring { $key: + * => $data, + } + } # manage ppas if present if $ppas { create_resources('apt::ppa', $ppas) diff --git a/manifests/keyring.pp b/manifests/keyring.pp new file mode 100644 index 0000000000..83af558257 --- /dev/null +++ b/manifests/keyring.pp @@ -0,0 +1,74 @@ +# @summary Manage GPG keyrings for apt repositories +# +# @example Download the puppetlabs apt keyring +# apt::keyring { 'puppetlabs-keyring.gpg': +# source => 'https://apt.puppetlabs.com/keyring.gpg', +# } +# @example Deploy the apt source and associated keyring file +# apt::source { 'puppet8-release': +# location => 'http://apt.puppetlabs.com', +# repos => 'puppet8', +# key => { +# name => 'puppetlabs-keyring.gpg', +# source => 'https://apt.puppetlabs.com/keyring.gpg' +# } +# } +# +# @param keyring_dir +# Path to the directory where the keyring will be stored. +# +# @param keyring_filename +# Optional filename for the keyring. It should also contain extension along with the filename. +# +# @param keyring_file +# File path of the keyring. +# +# @param keyring_file_mode +# File permissions of the keyring. +# +# @param source +# Source of the keyring file. Mutually exclusive with 'content'. +# +# @param content +# Content of the keyring file. Mutually exclusive with 'source'. +# +# @param ensure +# Ensure presence or absence of the resource. +# +define apt::keyring ( + Stdlib::Absolutepath $keyring_dir = '/etc/apt/keyrings', + String[1] $keyring_filename = $name, + Stdlib::Absolutepath $keyring_file = "${keyring_dir}/${keyring_filename}", + Stdlib::Filemode $keyring_file_mode = '0644', + Optional[Stdlib::Filesource] $source = undef, + Optional[String[1]] $content = undef, + Enum['present','absent'] $ensure = 'present', +) { + ensure_resource('file', $keyring_dir, { ensure => 'directory', mode => '0755', }) + if $source and $content { + fail("Parameters 'source' and 'content' are mutually exclusive") + } elsif ! $source and ! $content { + fail("One of 'source' or 'content' parameters are required") + } + + case $ensure { + 'present': { + file { $keyring_file: + ensure => 'file', + mode => $keyring_file_mode, + owner => 'root', + group => 'root', + source => $source, + content => $content, + } + } + 'absent': { + file { $keyring_file: + ensure => $ensure, + } + } + default: { + fail("Invalid 'ensure' value '${ensure}' for apt::keyring") + } + } +} diff --git a/manifests/source.pp b/manifests/source.pp index 4e14d8a0a5..f76812b463 100644 --- a/manifests/source.pp +++ b/manifests/source.pp @@ -10,6 +10,17 @@ # }, # } # +# @example Download key behaviour to handle modern apt gpg keyrings. The `name` parameter in the key hash should be given with +# extension. Absence of extension will result in file formation with just name and no extension. +# apt::source { 'puppetlabs': +# location => 'http://apt.puppetlabs.com', +# comment => 'Puppet8', +# key => { +# 'name' => 'puppetlabs.gpg', +# 'source' => 'https://apt.puppetlabs.com/keyring.gpg', +# }, +# } +# # @param location # Required, unless ensure is set to 'absent'. Specifies an Apt repository. Valid options: a string containing a repository URL. # @@ -35,12 +46,16 @@ # Specifies whether to request the distribution's uncompiled source code. Default false. # # @param key -# Creates a declaration of the apt::key defined type. Valid options: a string to be passed to the `id` parameter of the `apt::key` -# defined type, or a hash of `parameter => value` pairs to be passed to `apt::key`'s `id`, `server`, `content`, `source`, `weak_ssl`, -# and/or `options` parameters. +# Creates an `apt::keyring` in `/etc/apt/keyrings` (or anywhere on disk given `filename`) Valid options: +# * a hash of `parameter => value` pairs to be passed to `file`: `name` (title), `content`, `source`, `filename` +# +# The following inputs are valid for the (deprecated) `apt::key` defined type. Valid options: +# * a string to be passed to the `id` parameter of the `apt::key` defined type +# * a hash of `parameter => value` pairs to be passed to `apt::key`: `id`, `server`, `content`, `source`, `weak_ssl`, `options` # # @param keyring # Absolute path to a file containing the PGP keyring used to sign this repository. Value is used to set signed-by on the source entry. +# This is not necessary if the key is installed with `key` param above. # See https://wiki.debian.org/DebianRepository/UseThirdParty for details. # # @param pin @@ -49,8 +64,8 @@ # # @param architecture # Tells Apt to only download information for specified architectures. Valid options: a string containing one or more architecture names, -# separated by commas (e.g., 'i386' or 'i386,alpha,powerpc'). Default: undef (if unspecified, Apt downloads information for all architectures -# defined in the Apt::Architectures option). +# separated by commas (e.g., 'i386' or 'i386,alpha,powerpc'). +# (if unspecified, Apt downloads information for all architectures defined in the Apt::Architectures option) # # @param allow_unsigned # Specifies whether to authenticate packages from this release, even if the Release file is not signed or the signature can't be checked. @@ -108,35 +123,87 @@ $includes = merge($::apt::include_defaults, $include) - if $key and $keyring { - fail("parameters key and keyring are mutualy exclusive") - } - - if $key { + if $keyring { + if $key { + fail('parameters key and keyring are mutually exclusive') + } else { + $_list_keyring = $keyring + } + } elsif $key { if $key =~ Hash { - unless $key['id'] { - fail('key hash must contain at least an id entry') + unless $key['name'] or $key['id'] { + fail('key hash must contain a key name (for apt::keyring) or an id (for apt::key)') + } + if $key['id'] { + # defaults like keyserver are only relevant to apt::key + $_key = merge($apt::source_key_defaults, $key) + } else { + $_key = $key } - $_key = merge($::apt::source_key_defaults, $key) } else { $_key = { 'id' => assert_type(String[1], $key) } } + if $_key['ensure'] { + $_key_ensure = $_key['ensure'] + } else { + $_key_ensure = $ensure + } + + # Old keyserver keys handled by apt-key + if $_key =~ Hash and $_key['id'] { + # We do not want to remove keys when the source is absent. + if $ensure == 'present' { + apt::key { "Add key: ${$_key['id']} from Apt::Source ${title}": + ensure => $_key_ensure, + id => $_key['id'], + server => $_key['server'], + content => $_key['content'], + source => $_key['source'], + options => $_key['options'], + weak_ssl => $_key['weak_ssl'], + before => $_before, + } + } + $_list_keyring = undef + } + # Modern apt keyrings + elsif $_key =~ Hash and $_key['name'] { + apt::keyring { $_key['name']: + ensure => $_key_ensure, + content => $_key['content'], + source => $_key['source'], + keyring_filename => $_key['filename'], + before => $_before, + } + # TODO replace this block with a reference to the apt::keyring's final filename/full_path + if $_key['filename'] { + $_list_keyring = $_key['filename'] + } else { + $_list_keyring = "/etc/apt/keyrings/${_key['name']}" + } + } + } else { + # No `key` nor `keyring` provided + $_list_keyring = undef } $header = epp('apt/_header.epp') $sourcelist = epp('apt/source.list.epp', { - 'comment' => $comment, - 'includes' => $includes, - 'options' => delete_undef_values({ - 'arch' => $architecture, - 'trusted' => $allow_unsigned ? {true => "yes", false => undef}, - 'signed-by' => $keyring, - }), - 'location' => $_location, - 'release' => $_release, - 'repos' => $repos, - }) + 'comment' => $comment, + 'includes' => $includes, + 'options' => delete_undef_values({ + 'arch' => $_architecture, + 'trusted' => $allow_unsigned ? { true => 'yes', false => undef }, + 'allow-insecure' => $allow_insecure ? { true => 'yes', false => undef }, + 'signed-by' => $_list_keyring, + 'check-valid-until' => $check_valid_until? { true => undef, false => 'false' }, + }, + ), + 'location' => $_location, + 'components' => $_components, + } + ) apt::setting { "list-${name}": ensure => $ensure, @@ -161,26 +228,4 @@ } create_resources('apt::pin', { "${name}" => $_pin }) } - - # We do not want to remove keys when the source is absent. - if $key and ($ensure == 'present') { - if $_key =~ Hash { - if $_key['ensure'] != undef { - $_ensure = $_key['ensure'] - } else { - $_ensure = $ensure - } - - apt::key { "Add key: ${$_key['id']} from Apt::Source ${title}": - ensure => $_ensure, - id => $_key['id'], - server => $_key['server'], - content => $_key['content'], - source => $_key['source'], - options => $_key['options'], - weak_ssl => $_key['weak_ssl'], - before => $_before, - } - } - } } diff --git a/spec/acceptance/apt_keyring_spec.rb b/spec/acceptance/apt_keyring_spec.rb new file mode 100644 index 0000000000..0104464ff4 --- /dev/null +++ b/spec/acceptance/apt_keyring_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'spec_helper_acceptance' + +PUPPETLABS_KEYRING_CHECK_COMMAND = 'gpg --import /etc/apt/keyrings/puppetlabs-keyring.gpg && gpg --list-keys | grep -F -A 1 \'pub rsa4096 2019-04-08 [SC] [expires: 2025-04-06]\'' \ +'| grep \'D6811ED3ADEEB8441AF5AA8F4528B6CD9E61EF26\'' + +describe 'apt::keyring' do + context 'when using default values and source specified explicitly' do + keyring_pp = <<-MANIFEST + apt::keyring { 'puppetlabs-keyring.gpg': + source => 'https://apt.puppetlabs.com/keyring.gpg', + } + MANIFEST + + it 'applies idempotently' do + retry_on_error_matching do + idempotent_apply(keyring_pp) + end + end + + it 'expects file content to be present and correct' do + retry_on_error_matching do + run_shell(PUPPETLABS_KEYRING_CHECK_COMMAND.to_s) + end + end + end +end diff --git a/spec/defines/keyring_spec.rb b/spec/defines/keyring_spec.rb new file mode 100644 index 0000000000..6b3c65e1ef --- /dev/null +++ b/spec/defines/keyring_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'apt::keyring' do + let(:title) { 'namevar' } + let(:params) do + { + source: 'http://apt.puppetlabs.com/pubkey.gpg', + } + end + + on_supported_os.each do |os, os_facts| + context "on #{os}" do + let(:facts) { os_facts } + + it { is_expected.to compile } + end + end +end