[dnf] ensure packages are gpg-verified (#71539)
Change: - By default the dnf API does not gpg-verify packages. This is a feature that is executed in its CLI code. It never made it into Ansible's usage of the API, so packages were previously not verified. - This fixes CVE-2020-14365. Test Plan: - New integration tests Signed-off-by: Rick Elrod <rick@elrod.me>
This commit is contained in:
parent
d3e0cb4320
commit
dc97027453
6 changed files with 107 additions and 0 deletions
2
changelogs/fragments/dnf_gpg.yml
Normal file
2
changelogs/fragments/dnf_gpg.yml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
security_fixes:
|
||||||
|
- dnf - Previously, regardless of the ``disable_gpg_check`` option, packages were not GPG validated. They are now. (CVE-2020-14365)
|
|
@ -62,6 +62,8 @@ options:
|
||||||
description:
|
description:
|
||||||
- Whether to disable the GPG checking of signatures of packages being
|
- Whether to disable the GPG checking of signatures of packages being
|
||||||
installed. Has an effect only if state is I(present) or I(latest).
|
installed. Has an effect only if state is I(present) or I(latest).
|
||||||
|
- This setting affects packages installed from a repository as well as
|
||||||
|
"local" packages installed from the filesystem or a URL.
|
||||||
type: bool
|
type: bool
|
||||||
default: 'no'
|
default: 'no'
|
||||||
|
|
||||||
|
@ -1165,6 +1167,26 @@ class DnfModule(YumDnf):
|
||||||
results=[],
|
results=[],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Validate GPG. This is NOT done in dnf.Base (it's done in the
|
||||||
|
# upstream CLI subclass of dnf.Base)
|
||||||
|
if not self.disable_gpg_check:
|
||||||
|
for package in self.base.transaction.install_set:
|
||||||
|
fail = False
|
||||||
|
gpgres, gpgerr = self.base._sig_check_pkg(package)
|
||||||
|
if gpgres == 0: # validated successfully
|
||||||
|
continue
|
||||||
|
elif gpgres == 1: # validation failed, install cert?
|
||||||
|
try:
|
||||||
|
self.base._get_key_for_package(package)
|
||||||
|
except dnf.exceptions.Error as e:
|
||||||
|
fail = True
|
||||||
|
else: # fatal error
|
||||||
|
fail = True
|
||||||
|
|
||||||
|
if fail:
|
||||||
|
msg = 'Failed to validate GPG signature for {0}'.format(package)
|
||||||
|
self.module.fail_json(msg)
|
||||||
|
|
||||||
if self.download_only:
|
if self.download_only:
|
||||||
for package in self.base.transaction.install_set:
|
for package in self.base.transaction.install_set:
|
||||||
response['results'].append("Downloaded: {0}".format(package))
|
response['results'].append("Downloaded: {0}".format(package))
|
||||||
|
|
|
@ -557,6 +557,7 @@
|
||||||
dnf:
|
dnf:
|
||||||
name: "/tmp/{{ pkg_name }}.rpm"
|
name: "/tmp/{{ pkg_name }}.rpm"
|
||||||
state: present
|
state: present
|
||||||
|
disable_gpg_check: true
|
||||||
register: dnf_result
|
register: dnf_result
|
||||||
|
|
||||||
- name: verify installation
|
- name: verify installation
|
||||||
|
@ -586,6 +587,7 @@
|
||||||
dnf:
|
dnf:
|
||||||
name: "{{ pkg_url }}"
|
name: "{{ pkg_url }}"
|
||||||
state: present
|
state: present
|
||||||
|
disable_gpg_check: true
|
||||||
register: dnf_result
|
register: dnf_result
|
||||||
|
|
||||||
- name: verify installation
|
- name: verify installation
|
||||||
|
|
72
test/integration/targets/dnf/tasks/gpg.yml
Normal file
72
test/integration/targets/dnf/tasks/gpg.yml
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
# Set up a repo of unsigned rpms
|
||||||
|
- block:
|
||||||
|
- name: Ensure our test package isn't already installed
|
||||||
|
dnf:
|
||||||
|
name:
|
||||||
|
- fpaste
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Install rpm-sign
|
||||||
|
dnf:
|
||||||
|
name:
|
||||||
|
- rpm-sign
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Create directory to use as local repo
|
||||||
|
file:
|
||||||
|
path: "{{ remote_tmp_dir }}/unsigned"
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: Download an RPM
|
||||||
|
get_url:
|
||||||
|
url: https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/dnf/fpaste-0.3.9.1-1.fc27.noarch.rpm
|
||||||
|
dest: "{{ remote_tmp_dir }}/unsigned/fpaste-0.3.9.1-1.fc27.noarch.rpm"
|
||||||
|
mode: 0644
|
||||||
|
|
||||||
|
- name: Unsign the RPM
|
||||||
|
command: rpmsign --delsign "{{ remote_tmp_dir }}/unsigned/fpaste-0.3.9.1-1.fc27.noarch.rpm"
|
||||||
|
|
||||||
|
- name: createrepo
|
||||||
|
command: createrepo .
|
||||||
|
args:
|
||||||
|
chdir: "{{ remote_tmp_dir }}/unsigned"
|
||||||
|
|
||||||
|
- name: Add the repo
|
||||||
|
yum_repository:
|
||||||
|
name: unsigned
|
||||||
|
description: unsigned rpms
|
||||||
|
baseurl: "file://{{ remote_tmp_dir }}/unsigned/"
|
||||||
|
# we want to ensure that signing is verified
|
||||||
|
gpgcheck: true
|
||||||
|
|
||||||
|
- name: Install fpaste from above
|
||||||
|
dnf:
|
||||||
|
name:
|
||||||
|
- fpaste
|
||||||
|
disablerepo: '*'
|
||||||
|
enablerepo: unsigned
|
||||||
|
register: res
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- res is failed
|
||||||
|
- "'Failed to validate GPG signature' in res.msg"
|
||||||
|
|
||||||
|
always:
|
||||||
|
- name: Remove rpm-sign (and fpaste if it got installed)
|
||||||
|
dnf:
|
||||||
|
name:
|
||||||
|
- rpm-sign
|
||||||
|
- fpaste
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Remove test repo
|
||||||
|
yum_repository:
|
||||||
|
name: unsigned
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Remove repo dir
|
||||||
|
file:
|
||||||
|
path: "{{ remote_tmp_dir }}/unsigned"
|
||||||
|
state: absent
|
|
@ -23,6 +23,10 @@
|
||||||
when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('23', '>=')) or
|
when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('23', '>=')) or
|
||||||
(ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>='))
|
(ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>='))
|
||||||
|
|
||||||
|
- include_tasks: gpg.yml
|
||||||
|
when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('23', '>=')) or
|
||||||
|
(ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>='))
|
||||||
|
|
||||||
- include_tasks: repo.yml
|
- include_tasks: repo.yml
|
||||||
when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('23', '>=')) or
|
when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('23', '>=')) or
|
||||||
(ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>='))
|
(ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>='))
|
||||||
|
|
|
@ -106,6 +106,7 @@
|
||||||
name: "{{ repodir }}/dinginessentail-1.0-1.{{ ansible_architecture }}.rpm"
|
name: "{{ repodir }}/dinginessentail-1.0-1.{{ ansible_architecture }}.rpm"
|
||||||
state: present
|
state: present
|
||||||
allow_downgrade: True
|
allow_downgrade: True
|
||||||
|
disable_gpg_check: True
|
||||||
register: dnf_result
|
register: dnf_result
|
||||||
|
|
||||||
- name: Check dinginessentail with rpm
|
- name: Check dinginessentail with rpm
|
||||||
|
@ -132,6 +133,7 @@
|
||||||
dnf:
|
dnf:
|
||||||
name: "{{ repodir }}/dinginessentail-1.0-1.{{ ansible_architecture }}.rpm"
|
name: "{{ repodir }}/dinginessentail-1.0-1.{{ ansible_architecture }}.rpm"
|
||||||
state: present
|
state: present
|
||||||
|
disable_gpg_check: True
|
||||||
register: dnf_result
|
register: dnf_result
|
||||||
|
|
||||||
- name: Check dinginessentail with rpm
|
- name: Check dinginessentail with rpm
|
||||||
|
@ -153,6 +155,7 @@
|
||||||
dnf:
|
dnf:
|
||||||
name: "{{ repodir }}/dinginessentail-1.0-1.{{ ansible_architecture }}.rpm"
|
name: "{{ repodir }}/dinginessentail-1.0-1.{{ ansible_architecture }}.rpm"
|
||||||
state: present
|
state: present
|
||||||
|
disable_gpg_check: True
|
||||||
register: dnf_result
|
register: dnf_result
|
||||||
|
|
||||||
- name: Check dinginessentail with rpm
|
- name: Check dinginessentail with rpm
|
||||||
|
@ -169,6 +172,7 @@
|
||||||
dnf:
|
dnf:
|
||||||
name: "{{ repodir }}/dinginessentail-1.0-2.{{ ansible_architecture }}.rpm"
|
name: "{{ repodir }}/dinginessentail-1.0-2.{{ ansible_architecture }}.rpm"
|
||||||
state: present
|
state: present
|
||||||
|
disable_gpg_check: True
|
||||||
register: dnf_result
|
register: dnf_result
|
||||||
|
|
||||||
- name: Check dinginessentail with rpm
|
- name: Check dinginessentail with rpm
|
||||||
|
@ -190,6 +194,7 @@
|
||||||
dnf:
|
dnf:
|
||||||
name: "{{ repodir }}/dinginessentail-1.0-2.{{ ansible_architecture }}.rpm"
|
name: "{{ repodir }}/dinginessentail-1.0-2.{{ ansible_architecture }}.rpm"
|
||||||
state: present
|
state: present
|
||||||
|
disable_gpg_check: True
|
||||||
register: dnf_result
|
register: dnf_result
|
||||||
|
|
||||||
- name: Check dinginessentail with rpm
|
- name: Check dinginessentail with rpm
|
||||||
|
|
Loading…
Reference in a new issue