From a1a42fbda90aa8b02ef3443005e37e3caf997a14 Mon Sep 17 00:00:00 2001 From: Ken Dreyer Date: Aug 16 2021 11:39:01 +0000 Subject: doc: add signing documentation Explain RPM signatures, how Koji handles signatures, and how to generate repos with signed RPMs. --- diff --git a/docs/source/index.rst b/docs/source/index.rst index 5ce4df4..e9a4704 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -38,6 +38,7 @@ Contents runs_here server_bootstrap server_howto + signing database_howto kojid_conf using_the_koji_build_system diff --git a/docs/source/signing.rst b/docs/source/signing.rst new file mode 100644 index 0000000..ad2fed9 --- /dev/null +++ b/docs/source/signing.rst @@ -0,0 +1,181 @@ +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 is no "RSA/SHA256 Signature" header field on the RPM here. + +*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 + +Uploding 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 ``kojidev 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. + +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 + `_ hub plugin to automatically + export dist-repos for certain tags. + +3. Pungi can create signed repos ("composes"). + +See :doc:`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 `_ and `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.