diff options
author | Rick Elrod <rick@elrod.me> | 2020-08-31 10:05:42 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-08-31 10:05:42 -0500 |
commit | 1fa2d5fd6b768120b76a77929e27302b06accc0c (patch) | |
tree | 7b1757c910a997cf0baefd0d056bbf3b07fed0cf | |
parent | 53be4cc03dca58d8e4a42af65e697d6b2c7d8845 (diff) | |
download | ansible-1fa2d5fd6b768120b76a77929e27302b06accc0c.tar.gz |
[dnf] ensure packages are gpg-verified (#71541)
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>
-rw-r--r-- | changelogs/fragments/dnf_gpg.yml | 2 | ||||
-rw-r--r-- | lib/ansible/modules/packaging/os/dnf.py | 22 | ||||
-rw-r--r-- | test/integration/targets/dnf/meta/main.yml | 1 | ||||
-rw-r--r-- | test/integration/targets/dnf/tasks/dnf.yml | 2 | ||||
-rw-r--r-- | test/integration/targets/dnf/tasks/gpg.yml | 72 | ||||
-rw-r--r-- | test/integration/targets/dnf/tasks/main.yml | 4 | ||||
-rw-r--r-- | test/integration/targets/dnf/tasks/repo.yml | 5 |
7 files changed, 108 insertions, 0 deletions
diff --git a/changelogs/fragments/dnf_gpg.yml b/changelogs/fragments/dnf_gpg.yml new file mode 100644 index 0000000000..2e156c509b --- /dev/null +++ b/changelogs/fragments/dnf_gpg.yml @@ -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) diff --git a/lib/ansible/modules/packaging/os/dnf.py b/lib/ansible/modules/packaging/os/dnf.py index 509ee76568..66912593fa 100644 --- a/lib/ansible/modules/packaging/os/dnf.py +++ b/lib/ansible/modules/packaging/os/dnf.py @@ -65,6 +65,8 @@ options: description: - Whether to disable the GPG checking of signatures of packages being 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 default: 'no' @@ -1203,6 +1205,26 @@ class DnfModule(YumDnf): 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=msg) + if self.download_only: for package in self.base.transaction.install_set: response['results'].append("Downloaded: {0}".format(package)) diff --git a/test/integration/targets/dnf/meta/main.yml b/test/integration/targets/dnf/meta/main.yml index 73076d3562..34d812616b 100644 --- a/test/integration/targets/dnf/meta/main.yml +++ b/test/integration/targets/dnf/meta/main.yml @@ -1,3 +1,4 @@ dependencies: - prepare_tests - setup_rpm_repo + - setup_remote_tmp_dir diff --git a/test/integration/targets/dnf/tasks/dnf.yml b/test/integration/targets/dnf/tasks/dnf.yml index abe7068d24..24e7b8073c 100644 --- a/test/integration/targets/dnf/tasks/dnf.yml +++ b/test/integration/targets/dnf/tasks/dnf.yml @@ -559,6 +559,7 @@ dnf: name: "/tmp/{{ pkg_name }}.rpm" state: present + disable_gpg_check: true register: dnf_result - name: verify installation @@ -588,6 +589,7 @@ dnf: name: "{{ pkg_url }}" state: present + disable_gpg_check: true register: dnf_result - name: verify installation diff --git a/test/integration/targets/dnf/tasks/gpg.yml b/test/integration/targets/dnf/tasks/gpg.yml new file mode 100644 index 0000000000..2b6f4079a9 --- /dev/null +++ b/test/integration/targets/dnf/tasks/gpg.yml @@ -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 diff --git a/test/integration/targets/dnf/tasks/main.yml b/test/integration/targets/dnf/tasks/main.yml index 9369b5b0cd..fbfddadf7b 100644 --- a/test/integration/targets/dnf/tasks/main.yml +++ b/test/integration/targets/dnf/tasks/main.yml @@ -23,6 +23,10 @@ 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: 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 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', '>=')) diff --git a/test/integration/targets/dnf/tasks/repo.yml b/test/integration/targets/dnf/tasks/repo.yml index 718eae10a5..b8a84f2dc7 100644 --- a/test/integration/targets/dnf/tasks/repo.yml +++ b/test/integration/targets/dnf/tasks/repo.yml @@ -88,6 +88,7 @@ name: "{{ repodir }}/dinginessentail-1.0-1.{{ ansible_architecture }}.rpm" state: present allow_downgrade: True + disable_gpg_check: True register: dnf_result - name: Check dinginessentail with rpm @@ -114,6 +115,7 @@ dnf: name: "{{ repodir }}/dinginessentail-1.0-1.{{ ansible_architecture }}.rpm" state: present + disable_gpg_check: True register: dnf_result - name: Check dinginessentail with rpm @@ -135,6 +137,7 @@ dnf: name: "{{ repodir }}/dinginessentail-1.0-1.{{ ansible_architecture }}.rpm" state: present + disable_gpg_check: True register: dnf_result - name: Check dinginessentail with rpm @@ -151,6 +154,7 @@ dnf: name: "{{ repodir }}/dinginessentail-1.0-2.{{ ansible_architecture }}.rpm" state: present + disable_gpg_check: True register: dnf_result - name: Check dinginessentail with rpm @@ -172,6 +176,7 @@ dnf: name: "{{ repodir }}/dinginessentail-1.0-2.{{ ansible_architecture }}.rpm" state: present + disable_gpg_check: True register: dnf_result - name: Check dinginessentail with rpm |