Skip to content
Search Fedora

    Browse this documentation

    F45 signature enforcement: what the three paths actually do

    Fedora 45 changes the RPM default verification mode to enforcing signature checking (official ChangeSet: Enforcing signature checking by default). Which layer “enforced” lands on decides whether your third-party RPMs suddenly refuse to install — the same unsigned package takes three different paths to three different outcomes.

    Every terminal output on this page was produced by building an unsigned package with rpmbuild on F45 and running each path, not copied from the docs.

    You do not need to install anything to find out; two commands are enough:

    Terminal window
    rpm -K utest-1.0-1.noarch.rpm
    rpm -qpi utest-1.0-1.noarch.rpm | head -8

    For an unsigned package, rpm -K reports:

    /tmp/urpms/noarch/utest-1.0-1.noarch.rpm: digests SIGNATURES NOT OK

    SIGNATURES NOT OK is where the three paths start to diverge.

    Terminal window
    sudo rpm -i utest-1.0-1.noarch.rpm
    package utest-1.0-1.noarch does not verify: no signature

    The exit code is 1 and the package is not installed (rpm -q utest reports package utest is not installed). This is the biggest behavioural difference from F44: rpm 6.0.x on F44 installs it directly.

    Path two: dnf install ./x.rpm — warns and installs

    Section titled “Path two: dnf install ./x.rpm — warns and installs”
    Terminal window
    sudo dnf -y install ./utest-1.0-1.noarch.rpm
    [3/3] Installing utest-0:1.0-1.noarch 100% | 4.3 KiB/s | 268.0 B | 00m00s
    Warning: skipped OpenPGP checks for 1 package from repository: @commandline
    Complete!

    It installs. Note from repository: @commandline — dnf treats the local file as a temporary repository, and that repository carries no gpgcheck constraint, so it only warns.

    Path three: repository install (gpgcheck=1) — refused at transaction

    Section titled “Path three: repository install (gpgcheck=1) — refused at transaction”

    Put the unsigned package in a repository with gpgcheck=1 and install it by name:

    Terminal window
    sudo dnf -y install utest
    Running transaction
    Transaction failed: Signature verification failed.
    OpenPGP check for package "utest-1.0-1.noarch" (.../utest-1.0-1.noarch.rpm)
    from repo "unsigned-local" has failed: The package is not signed.

    The failure happens at the transaction stage: the package is already in the cache, but it is refused for writing into the system.

    Message you seePathOutcomeWhat to do
    does not verify: no signaturerpm -irefusedadd --nosignature or switch to a repository
    skipped OpenPGP checks … @commandlinednf install ./x.rpminstalleddo not use the local-file path if you want a hard block
    The package is not signeddnf repository installrefusedsign the package, or disable gpgcheck temporarily
    SIGNATURES NOT OKrpm -Kreport onlycheck where the package came from

    Each path has its own override, and picking the wrong one leaves the risk in the system:

    Terminal window
    # 1) Allow this one install (rpm layer)
    sudo rpm -i --nosignature utest-1.0-1.noarch.rpm
    # 2) Disable checking for one repository for this command only (writes no file)
    sudo dnf install --setopt=unsigned-local.gpgcheck=0 utest
    # 3) The proper route: sign the package and install from a repository
    rpm --addsign utest-1.0-1.noarch.rpm

    Teams usually end up running their own repository. The smallest reproducible flow:

    Terminal window
    # Generate a key (once)
    gpg --batch --gen-key <<'EOF'
    %no-protection
    Key-Type: RSA
    Key-Length: 4096
    Name-Real: Internal RPM Signing
    Name-Email: [email protected]
    Expire-Date: 2y
    %commit
    EOF
    # Export the public key for clients, import the private one into rpm
    gpg --export -a "Internal RPM Signing" > RPM-GPG-KEY-internal
    sudo rpm --import RPM-GPG-KEY-internal
    # Sign each package and rebuild metadata
    rpm --addsign *.rpm
    createrepo_c .

    Point the client repository file at the key with gpgkey= and gpgcheck=1, so unsigned packages are blocked at the transaction stage instead of being discovered after installation.

    Four steps to confirm you are not blocked:

    1. rpm -q rpm — confirm 6.1, not 6.0.x;
    2. rpm -K your-third-party.rpm — check the signature state;
    3. Run dnf install ./x.rpm once and confirm it only warns;
    4. Run it by repository name and confirm it is refused — being refused is the expected behaviour.