Build and Sign RPM package and repo

By Published On: January 21, 20218.8 min readViews: 387

Hi there!

Welcome to geekcoding101.com!

I have two decades years of working experiences on Linux. There are many things I have come across, but I want to say, building package for Linux is something you couldn’t avoid at all in your work or study!

I have summarized the steps/tricks in this article, hope you will find it useful!

Enjoy!

Create unsigned rpm

I will first demonstrate how to create unsigned rpm.

Create Folder Structure

First step is creating folder structure.

If you don’t specify top_dir in ~/.rpmmacros (It’s a config file), then it will use ~/rpmbuild by default

cd ~ 
mkdir rpmbuild 
cd rpmbuild 
mkdir BUILD BUILDROOT SOURCES SRPMS RPMS SPECS

Create SPEC file for unsigned rpm

Now we can work on the spec file SPECS/rpm-no-sig.spec:

# cat SPECS/rpm-no-sig.spec
Name:       rpm-no-sig
Version:    1.0
Release:    1
Summary:    This is a unsigned rpm.

Vendor:     Geekcoding101
License:    Copyright (c) 2021

BuildArch:  noarch
BuildRoot:  %{_tmppath}/%{name}-%{version}

Packager:   Geekcoding101

Source0:    %{name}-%{version}.tar.gz
%define INSTALL_DIR /usr/lib/unsigned-rpm/
%define INSTALL_FILE rpm-helper-unsigned.py

%description
%{Summary}
This package provides rpm gpgcheck demo.

%prep
%setup -q

%install
rm -rf %{buildroot}
install --directory %{buildroot}/%{INSTALL_DIR}
install -m 0755 %{INSTALL_FILE} %{buildroot}/%{INSTALL_DIR}

%clean
rm -rf %{buildroot}

%files
%defattr(-,root,root,-)
%{INSTALL_DIR}
%{INSTALL_DIR}/%{INSTALL_FILE}
%exclude %{INSTALL_DIR}/*.pyc
%exclude %{INSTALL_DIR}/*.pyo

%doc

%changelog
* Sun Jan 21 2021 - Geekcoding101
- Initial commit.

%post

Create a dummy source file for unsigned rpm

Use a dummy py file to be packed into the rpm: rpm-helper-unsigned.py:

#!/usr/bin/env python

import sys
import os
import re

def main():
    print("This is from unsigned rpm.")


if __name__ == '__main__':
    main()

Create a folder: mkdir <rpm-name>-<version>

For example:

cd ~
mkdir rpm-no-sig-1.0

Then put rpm-helper-unsigned.py under it.

Then make gz file for the folder:

tar cf rpm-no-sig-1.0.tar rpm-no-sig-1.0
gzip rpm-no-sig-1.0.tar

You will get file rpm-no-sig-1.0.tar.gz.

Move it to SOURCES folder.

When building rpm, it will recognize this gz file and extract it automatically.

Build rpm-no-sig.rpm

Run command: rpmbuild -ba SPECS/rpm-no-sig.spec

Example:

# rpmbuild -ba SPECS/rpm-no-sig.spec
warning: bogus date in %changelog: Sun Jan 21 2021 - Geekcoding101
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.ut6Q5z
+ umask 022
+ cd /root/rpmbuild/BUILD
+ cd /root/rpmbuild/BUILD
+ rm -rf rpm-no-sig-1.0
+ /usr/bin/gzip -dc /root/rpmbuild/SOURCES/rpm-no-sig-1.0.tar.gz
+ /usr/bin/tar -xf -
+ STATUS=0
+ '[' 0 -ne 0 ']'
+ cd rpm-no-sig-1.0
+ /usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .
+ exit 0
Executing(%install): /bin/sh -e /var/tmp/rpm-tmp.UOxKuK
+ umask 022
+ cd /root/rpmbuild/BUILD
+ '[' /root/rpmbuild/BUILDROOT/rpm-no-sig-1.0-1.x86_64 '!=' / ']'
+ rm -rf /root/rpmbuild/BUILDROOT/rpm-no-sig-1.0-1.x86_64
++ dirname /root/rpmbuild/BUILDROOT/rpm-no-sig-1.0-1.x86_64
+ mkdir -p /root/rpmbuild/BUILDROOT
+ mkdir /root/rpmbuild/BUILDROOT/rpm-no-sig-1.0-1.x86_64
+ cd rpm-no-sig-1.0
+ rm -rf /root/rpmbuild/BUILDROOT/rpm-no-sig-1.0-1.x86_64
+ install --directory /root/rpmbuild/BUILDROOT/rpm-no-sig-1.0-1.x86_64//usr/lib/unsigned-rpm/
+ install -m 0755 rpm-helper-unsigned.py /root/rpmbuild/BUILDROOT/rpm-no-sig-1.0-1.x86_64//usr/lib/unsigned-rpm/
+ '[' noarch = noarch ']'
+ case "${QA_CHECK_RPATHS:-}" in
+ /usr/lib/rpm/check-buildroot
+ /usr/lib/rpm/redhat/brp-compress
+ /usr/lib/rpm/redhat/brp-strip /usr/bin/strip
+ /usr/lib/rpm/redhat/brp-strip-comment-note /usr/bin/strip /usr/bin/objdump
+ /usr/lib/rpm/redhat/brp-strip-static-archive /usr/bin/strip
+ /usr/lib/rpm/brp-python-bytecompile /usr/bin/python 1
+ /usr/lib/rpm/redhat/brp-python-hardlink
+ /usr/lib/rpm/redhat/brp-java-repack-jars
Processing files: rpm-no-sig-1.0-1.noarch
warning: File listed twice: /usr/lib/unsigned-rpm/rpm-helper-unsigned.py
Provides: rpm-no-sig = 1.0-1
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PartialHardlinkSets) <= 4.0.4-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires: /usr/bin/env
Checking for unpackaged file(s): /usr/lib/rpm/check-files /root/rpmbuild/BUILDROOT/rpm-no-sig-1.0-1.x86_64
Wrote: /root/rpmbuild/SRPMS/rpm-no-sig-1.0-1.src.rpm
Wrote: /root/rpmbuild/RPMS/noarch/rpm-no-sig-1.0-1.noarch.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.w4KHSg
+ umask 022
+ cd /root/rpmbuild/BUILD
+ cd rpm-no-sig-1.0
+ rm -rf /root/rpmbuild/BUILDROOT/rpm-no-sig-1.0-1.x86_64
+ exit 0

You will get RPMS/noarch/rpm-no-sig-1.0-1.noarch.rpm

Check MD5: rpm -Kv <rpm file>

Example:

# rpm -Kv RPMS/noarch/rpm-no-sig-1.0-1.noarch.rpm
RPMS/noarch/rpm-no-sig-1.0-1.noarch.rpm:
    Header SHA1 digest: OK (84bd6662874a27ccd5cd3247ef7a4107c1919f54)
    MD5 digest: OK (198ab02bd5765c383c57dbe113551af0)

Backup this rpm to somewhere else.

Create signed rpm

Create SPEC file for signed rpm

The spec file SPECS/rpm-with-sig.spec:

# cat SPECS/rpm-with-sig.spec
Name:       rpm-with-sig
Version:    1.0
Release:    1
Summary:    This is a signed rpm.

Vendor:     Geekcoding101
License:    Copyright (c) 2021

BuildArch:  noarch
BuildRoot:  %{_tmppath}/%{name}-%{version}

Packager:   Geekcoding101

Source0:    %{name}-%{version}.tar.gz
%define INSTALL_DIR /usr/lib/signed-rpm/
%define INSTALL_FILE rpm-helper-signed.py

%description
%{Summary}
This package provides rpm gpgcheck demo.

%prep
%setup -q

%install
rm -rf %{buildroot}
install --directory %{buildroot}/%{INSTALL_DIR}
install -m 0755 %{INSTALL_FILE} %{buildroot}/%{INSTALL_DIR}

%clean
rm -rf %{buildroot}

%files
%defattr(-,root,root,-)
%{INSTALL_DIR}
%{INSTALL_DIR}/%{INSTALL_FILE}
%exclude %{INSTALL_DIR}/*.pyc
%exclude %{INSTALL_DIR}/*.pyo

%doc

%changelog
* Sun Jan 21 2021 - Geekcoding101
- Initial commit.

%post

Create a dummy source file for signed rpm

Use a dummy py file to be packed into the rpm: rpm-helper-signed.py. You can just reuse the one in above and change the print message accordingly.

Create a folder: cd ~ && mkdir <rpm-name>-<version>

Example: cd ~ && mkdir rpm-with-sig-1.0

Move rpm-helper-signed.py into the folder.

Also create the gz file with same process.

Example:

tar cf rpm-with-sig-1.0.tar rpm-with-sig-1.0
gzip rpm-with-sig-1.0.tar

You will get file rpm-with-sig-1.0.tar.gz.

Remove rpm-no-sig-1.0.tar.gz from SOURCES folder. Move rpm-with-sig-1.0.tar.gz into SOURCES.

Generate GPG key and Build rpm-with-sig.rpm

  1. Generate a gpg key for signing the rpm: gpg --gen-key
  2. Check the new keys on your system:
gpg --fingerprint
gpg --list-keys
  1. Update ~/.rpmmacros to specify which key to be used for signing: %_gpg_name <secret key's last 8 digits>
  2. Build the rpm: rpmbuild -ba SPECS/rpm-with-sig.spec
  3. You will get RPMS/noarch/rpm-with-sig-1.0-1.noarch.rpm
  4. Sign the rpm: rpm --addsign RPMS/noarch/rpm-with-sig-1.0-1.noarch.rpm
  5. Now you can check MD5 again: rpm -Kv <rpm file> Example:
# rpm -Kv ../rpm-with-sig-1.0-1.noarch.rpm
../rpm-with-sig-1.0-1.noarch.rpm:
    Header V4 RSA/SHA1 Signature, key ID 1ddb39c6: NOKEY
    Header SHA1 digest: OK (64fc89cf3eb3054e6316a77f4b22c183221ab13d)
    V4 RSA/SHA1 Signature, key ID 1ddb39c6: NOKEY
    MD5 digest: OK (fadae5f41bb0c939edbe865a974fce4c)
  1. You might see “NOKEY” in above output, because we didn’t import the key into OS.
  2. Export your key: gpg --export -a <last_8_dig_of_your_pub_key> > PUB_KEY_SIGNING_RPM
  3. Import it into RPM’s database: rpm --import PUB_KEY_SIGNING_RPM
  4. Now you can check MD5 again: rpm -Kv rpm-with-sig-1.0-1.noarch.rpm Example:
# rpm -Kv ../rpm-with-sig-1.0-1.noarch.rpm
../rpm-with-sig-1.0-1.noarch.rpm:
    Header V4 RSA/SHA1 Signature, key ID 1ddb39c6: OK
    Header SHA1 digest: OK (64fc89cf3eb3054e6316a77f4b22c183221ab13d)
    V4 RSA/SHA1 Signature, key ID 1ddb39c6: OK
    MD5 digest: OK (fadae5f41bb0c939edbe865a974fce4c)

Creating repo database/conf for unsigned rpm

Create repo database for the rpm-no-sig rpm

cd ~
mkdir unsigned_repo_with_rpm_no_sig
cp rpm-no-sig-1.0-1.noarch.rpm unsigned_repo_with_rpm_no_sig
createrepo --database unsigned_repo_with_rpm_no_sig/

Example:

# createrepo --database unsigned_repo_with_rpm_no_sig/
Spawning worker 0 with 1 pkgs
Spawning worker 1 with 0 pkgs
Spawning worker 2 with 0 pkgs
Spawning worker 3 with 0 pkgs
Workers Finished
Saving Primary metadata
Saving file lists metadata
Saving other metadata
Generating sqlite DBs
Sqlite DBs complete

Create repo conf file for unsigned repo

# cat /etc/yum.repos.d/rpm-no-sig.repo
[RPM-NO-SIG]
name=rpm no sig repository
baseurl=file:///root/unsigned_repo_with_rpm_no_sig
enabled=1
gpgcheck=0
localpkg_gpgcheck=0
repo_gpgcheck=0
skip_if_unavailable=1

Creating signed repo database/conf/gpg for signed rpm

Generate GPG key and Create repo for the rpm-with-sig rpm

cd ~
mkdir signed_repo_with_rpm_with_sig
cp rpm-with-sig-1.0-1.noarch.rpm signed_repo_with_rpm_with_sig
createrepo --database signed_repo_with_rpm_with_sig/

Generate a new gpg key for signing the repo: gpg --gen-key Create asc file:

gpg --detach-sign --armor -r Ox<secret key's last 8 digits fingerprint> signed_repo_with_rpm_with_sig/repodata/repomd.xml

It will generate signed_repo_with_rpm_with_sig/repodata/repomd.xml.asc.

Create repo conf file for signed repo

# cat /etc/yum.repos.d/rpm-with-sig.repo
[RPM-WITH-SIG]
name=rpm with sig repository
baseurl=file:///root/signed_repo_with_rpm_with_sig
enabled=1
gpgcheck=1
localpkg_gpgcheck=1
repo_gpgcheck=1
skip_if_unavailable=1

RPM/YUM relevant GPG knowlege

There are two types of GPG keyrings used on RPM-based systems:

  1. RPM’s GPG keyring. This keyring is used for verifying signatures on RPM packages. This can be check by gpg without specifying homedir.
  2. YUM’s GPG keyring. This keyring is used for verifying signatures on repository metadata. There is one keyring per repository on the system. Once scanned the repo by yum repolist, you could find the gpg folder like this: /var/lib/yum/repos/x86_64/7/<your_repo_name>/gpgdir. You could use gpg command ADD/LIST/DELETE keys as below:
sudo gpg --homedir /var/lib/yum/repos/x86_64/7/<your_repo_name>/gpgdir --delete-key <keyid>

YUM clean up

yum clean all will not remove everything. In order to do a real clean, you could try this:

yum clean all
rm -fr /var/lib/yum/repos/x86_64/7/<your_repo_name>
rm -fr /var/cache/yum/x86_64/7/<your_repo_name>

Yum commands references

yum install <rpm name>
yum install <path of the rpm>
yum clean all
yum clean metadata
yum-config-manager
yum-config-manager <rpm name>

Q/A

Can’t remove keys from RPM due to duplicate entries

You might hit problem that there are duplicate entries in rpm -qa gpg-pub* with same fingerprints.

rpm -e gpg-pubkey-xxxx can’t remove any.

You should use rpm -e --all-matches gpg-pubkey-xxxx

All right!
That’s all for my sharing today!
Hope you find it useful!
Bye!

Share it:

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments