Dnf module mangles package name of downloaded RPM

I’ve got the following code to download and install an RPM package on a Linux server:

- name: Install Foreman repository
  become: true
  ansible.builtin.dnf:
  ¦ name: "https://yum.theforeman.org/releases/latest/el{{ osrel | trim }}/x86_64/foreman-release.rpm"
  ¦ state: present

It works as far as downloading the package, but the installation fails with a GPG check error:

FAILED! => {"changed": false, "msg": "Failed to validate GPG signature for foreman-release-3.10.0-1.el8.noarch: Public key for foreman-releaseyvnup2j9.rpm is not installed"}

I don’t know where foreman-releaseyvnup2j9.rpm is coming from, but it’s different with each run. For example, running it again fails to find a key for foreman-releasetimtkhr2.rpm.

Running dnf from the command line outside of Ansible works as expected.

I could just work around the problem by adding disable_gpg_check: true, but I’d rather not do that. Can anyone tell me why this is not using the correct package name when doing the GPG check?

The issue has nothing to do with mangling of the filename. You’ll want to review this issue comment for the reason disparity between dnf module and dnf os command · Issue #80720 · ansible/ansible · GitHub

or ensure you’ve installed the gpg key used to sign that package so it can be verified

2 Likes

That particular package contains the GPG key, so it’s a bit of chicken and egg (you could install it via other means, sure).
But (with my Foreman hat on), pulling that RPM via an HTTPS connection should be fine w/o checking GPG.
That’s also what we’re doing in the official collection:

Thanks. That’s what I’ll do. Not that it really matters, but I’m still just curious though where the foreman-releaseyvnup2j9.rpm stuff is coming from.

You could install the key directly first, and then install the release rpm. The mangled filename might be incidental, but if you want to make sure the signature is good, then install the key first like so:

- name: Import Foreman key
  ansible.builtin.rpm_key:
    state: present
    key: "https://yum.theforeman.org/releases/{{ foreman_repositories_version }}/RPM-GPG-KEY-foreman"
1 Like

I guess Ansible first downloads that file to some temp location (so it can compare “is it installed already” and properly report changed), and during that it also gives it a “temp” name. Given rpm/yum/dnf will use the name from the RPM header, the filename doesn’t matter much anyway.

How would that improve the security of the “obtain the key” operation, given you’re still fetching the same key from the same host (well, CDN, but whatever)?

The signature will then be checked when you actually install packages from that repository.

2 Likes

I suppose that if the rpm is compromised by the same signing key found in the repo, the security is moot. If specific parts of the supply chain are somehow compromised, but also somehow the key is not, then we at least move the chicken and egg problem around to catch that. :man_shrugging:

Including the fingerprint from a trusted source would solve this, but I’m not sure of a reliable/programmatic way of doing that.

There’s these: Index of /static/keys (theforeman.org), but you would have to find exactly which one you need.

Here’s a table of which fingerprint keys are for which release: Foreman :: Security (theforeman.org)

Edit: So something like:

- name: Import Foreman key
  vars:
    foreman_repositories_version: "3.10"
    foreman_repositories_version_fingerprint: "63B3 8BE5 1B2D DDAF F7EF 7EC9 0A8F 8D40 93DD 1D0C"
  ansible.builtin.rpm_key:
    state: present
    key: "https://yum.theforeman.org/releases/{{ foreman_repositories_version }}/RPM-GPG-KEY-foreman"
    fingerprint: "{{ foreman_repositories_version_fingerprint }}"

1 Like

For situations like this I have the checksum of the key saved so that there is an error if it differs.

For example see this vars/mail.yml file and the task that downloads the GPG key.

This is in addition to having GPG fingerprints saved and checking the fingerprints.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.