205 lines
7.6 KiB
ReStructuredText
205 lines
7.6 KiB
ReStructuredText
RPM Signing with Koji
|
|
=====================
|
|
|
|
What is a GPG keypair?
|
|
----------------------
|
|
|
|
A GPG keypair has a public key that you can share with the world and a private key that you keep secret.
|
|
|
|
Here are some example commands for working with RPM and GPG.
|
|
|
|
Example of generating a GPG keypair for testing::
|
|
|
|
gpg --quick-generate-key security@example.com
|
|
# For testing, simply press "Enter" when prompted for a password.
|
|
|
|
Exporting your public key::
|
|
|
|
gpg --armor --export --output /path/to/my-signing-key.asc
|
|
|
|
Signing all RPMs in the current directory with this key::
|
|
|
|
rpmsign --define "_gpg_name security@example.com" --addsign *.rpm
|
|
|
|
Inspecting an RPM signature
|
|
---------------------------
|
|
|
|
In order to install a signed RPM on clients, each client must trust (import)
|
|
the public GPG key into their RPMDB::
|
|
|
|
rpm --import /path/to/my-signing-key.asc
|
|
|
|
*Example: No GPG signature at all (an unsigned RPM)*::
|
|
|
|
rpm -Kv python3-cherrypy-18.6.0-1.fc33.noarch.rpm
|
|
python3-cherrypy-18.6.0-1.fc33.noarch.rpm:
|
|
Header SHA256 digest: OK
|
|
Header SHA1 digest: OK
|
|
Payload SHA256 digest: OK
|
|
MD5 digest: OK
|
|
|
|
Note there are only "digest" fields here, no "Signature" fields since this RPM
|
|
is unsigned.
|
|
|
|
*Example: A GPG signature that rpmdb DOES trust*::
|
|
|
|
rpm -Kv python3-cherrypy-18.4.0-4.fc32.noarch.rpm
|
|
python3-cherrypy-18.4.0-4.fc32.noarch.rpm:
|
|
Header V3 RSA/SHA256 Signature, key ID 12c944d0: OK
|
|
Header SHA256 digest: OK
|
|
Header SHA1 digest: OK
|
|
Payload SHA256 digest: OK
|
|
V3 RSA/SHA256 Signature, key ID 12c944d0: OK
|
|
MD5 digest: OK
|
|
|
|
*Example: A GPG signature that rpmdb does NOT trust*::
|
|
|
|
rpm -Kv python-cherrypy-18.6.0-1.el8.src.rpm
|
|
python-cherrypy-18.6.0-1.el8.src.rpm:
|
|
Header V4 RSA/SHA256 Signature, key ID 782096ac: NOKEY
|
|
Header SHA256 digest: OK
|
|
Header SHA1 digest: OK
|
|
Payload SHA256 digest: OK
|
|
V4 RSA/SHA256 Signature, key ID 782096ac: NOKEY
|
|
MD5 digest: OK
|
|
|
|
Note the signature is syntatically valid here, but "NOKEY" here means RPMDB
|
|
does not trust the GPG key that signed this RPM.
|
|
|
|
A lower-level command that shows the signature on an RPM file (the
|
|
``RSAHEADER`` field piped through RPM's ``pgpsig`` formatter)::
|
|
|
|
rpm -q --qf '%{NAME} %{RSAHEADER:pgpsig}\n' -p python-routes-2.5.1-1.el8.src.rpm
|
|
|
|
Learn more about RPM signatures and digests in `RPM's reference manual
|
|
<https://rpm-software-management.github.io/rpm/manual/signatures_digests.html>`_.
|
|
|
|
Uploading signed RPMs to Koji
|
|
-----------------------------
|
|
|
|
Koji does not sign RPMs. Instead, Koji imports RPMs that are signed with a separate key.
|
|
|
|
To sign an RPM from Koji, you should make a copy of the file, sign it
|
|
with the appropriate rpm command, and import the signature. Note that you
|
|
should not simply sign the file directly under /mnt/koji, as this causes an
|
|
inconsistency between the filesystem and the database (hence the copy step).
|
|
|
|
In this example, we download an unsigned build from Koji, then sign it, and
|
|
then upload the signed copy with ``koji import-sig``::
|
|
|
|
koji download-build --debuginfo bash-5.0.17-2.fc32
|
|
rpmsign --define "_gpg_name security@example.com" --addsign *.rpm
|
|
koji import-sig *.rpm
|
|
|
|
The ``koji import-sig`` command uploads the signed RPM headers to the Koji
|
|
Hub, which stores the headers on disk alongside the main unsigned RPM.
|
|
It also writes out a full signed RPM.
|
|
|
|
Another variant is to import whole signed rpms (e.g. during :doc:`bootstrapping
|
|
<server_bootstrap>` via ``koji import`` command.) If such an imported rpm
|
|
contains an rpm signature, the import does not automatically write out a signed
|
|
copy for that signature (in contrast with ``import-sig``). The primary copy will
|
|
be the signed rpm, and the signature will be noted. If a signed copy is desired
|
|
(e.g. for generating :doc:`distrepos <exporting_repositories>`), you can use the
|
|
koji write-signed-rpm command.
|
|
|
|
Downloading a signed RPM from Koji
|
|
----------------------------------
|
|
|
|
Specify the ``--key`` option to ``koji download-build``::
|
|
|
|
koji download-build --key=3AF362BAB bash-5.0.17-2.fc32
|
|
|
|
Signing a build with multiple keys
|
|
----------------------------------
|
|
|
|
Currently RPM's file format only allows one single GPG signature per file.
|
|
|
|
Koji allows users to upload multiple GPG signatures for a single RPM. it
|
|
stores each signature alongside the RPM build and splices the signature
|
|
headers in to generate full signed RPMs. Here are some use-cases of this
|
|
feature:
|
|
|
|
- Sign a set of RPMs with a "beta" key, and later sign those same RPMs with a
|
|
"main" key.
|
|
|
|
- Sign the same Fedora RPMs with multiple keys, one per Fedora release.
|
|
|
|
- Sign the same CentOS RPMs with multiple keys, one per CentOS SIG.
|
|
|
|
- In Fedora, after the developers stop supporting a Fedora version like "30",
|
|
they can delete the full signed packages, which are many hundreds of GB, and
|
|
just keep the signatures, which are only a few bytes.
|
|
https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.org/message/RWILIHQJEKIQM5LAH7UJ7KMRPZEXCKQL/
|
|
|
|
Creating repos of signed RPMs
|
|
-----------------------------
|
|
|
|
You can put signed RPMs into Yum repos three different ways.
|
|
|
|
1. Create dist-repos manually with the ``koji dist-repo`` command, that takes
|
|
a GPG key argument.
|
|
|
|
2. Install and configure the `tag2distrepo
|
|
<https://pagure.io/releng/tag2distrepo>`_ hub plugin to automatically
|
|
export dist-repos for certain tags.
|
|
|
|
3. Pungi can create signed repos ("composes").
|
|
|
|
See :doc:`Exporting repositories <exporting_repositories>` for more
|
|
information.
|
|
|
|
How to automate signing?
|
|
------------------------
|
|
|
|
For a small testing environment, you can simply sign RPMs with a GPG key on a
|
|
workstation and run ``koji import-sig``. This is not secure and it does not
|
|
scale.
|
|
|
|
See the `Sigil <https://pagure.io/sigul>`_ and `Robosignatory
|
|
<https://pagure.io/robosignatory>`_ projects for more advanced workflows.
|
|
|
|
Koji cryptography best-practices
|
|
--------------------------------
|
|
|
|
- Use HTTPS everywhere (kojihub + kojiweb)
|
|
- Understand checksums (md5)
|
|
- Understand signatures (GPG)
|
|
|
|
How do RPM signatures relate to HTTPS?
|
|
--------------------------------------
|
|
|
|
HTTPS is transport-layer security. When you install a package over HTTPS you
|
|
verify that:
|
|
|
|
* The web server is who they say they are
|
|
* The information the web server sends is private
|
|
|
|
As soon as you download that build or copy it to another location, those
|
|
security guarantees are lost.
|
|
|
|
In a release pipeline, you end up copying builds to many locations, and while
|
|
it's important to use HTTPS for copying, it's even more important to have a
|
|
strong cryptographic signature follow each build.
|
|
|
|
This means that even if someone or some thing mirrors your build elsewhere,
|
|
that signature will go along with the build. In the case of RPMs, the GPG
|
|
signatures are actually embedded in the RPMs themselves that we deliver to
|
|
users.
|
|
|
|
Another reason this is important is for image-based artifacts that might use
|
|
many RPMs. If you think of cloud images or container images where you're
|
|
delivering an image with "preinstalled" RPMs, if you use signed RPMs in the
|
|
images you distribute, you're providing an extra layer of security.
|
|
|
|
How do RPM signatures relate to IMA signing?
|
|
--------------------------------------------
|
|
|
|
IMA stands for `"Integrity Measurement Architecture"
|
|
<https://www.redhat.com/en/blog/how-use-linux-kernels-integrity-measurement-architecture>`_.
|
|
It's a separate type of signature. RHEL-9 is the first release to have IMA
|
|
signing enabled. The change is still `under discussion
|
|
<https://fedoraproject.org/wiki/Changes/Signed_RPM_Contents>`_ for Fedora.
|
|
|
|
IMA does not replace RPM signing. RPM signing is orthogonal to IMA. Packages
|
|
can be both RPM-signed and IMA signed at the same time.
|