Making .deb files
Notes and other various tutorials on how to package .deb files for Debian based distributions
Notes on repackaging the HomeSeer Linux tarfile
Recently, I decided to repack the latest HomeSeer 4 Linux distribution file into a .deb package and then create a repository to share with others to download it.
The inspiration for this comes from the now defunct SavageAutomate's original .deb repack, repo and helper scripts to install HS4 on Debian/Ubuntu systems.
You can find the repack at https://homeseer.digiflux.org. This will redirect you to the github repo where the .deb and repository files are located.
Prerequisites
- You need a Debian/Ubuntu Linux system to build the packages and create the repo on.
- You need to have a GPG key that you will use for signing packages in the repo.
- You should have a place to serve the repo for public access. I am using Github pages with a custom domain for the HomeSeer Linux repack.
- You should have the prerequisites installed on your system to test the package. Ideally a virtual machine you can do snapshots on works best.
Since I am doing this for HomeSeer v4 Linux, I will install some packages so I can run HomeSeer and test the .deb pacakge I create:
apt-get install --yes mono-complete mono-devel flite chromium aha ffmpeg alsa-utils tmux curl wget nano sudo install
apt-get install --yes binutils cpp cpio dpkg-dev file gcc make patch dh-make debhelper devscripts fakeroot lintian aptly
apt-get install --yes debian-policy developers-reference man-db debsigs debsigs debsig-verify manpages reportbug
You can also try to install the mono-vbnc package. It may be missing from your system, which is ok since there is a helper script I created/package to download, build, and install it.
apt-get install --yes mono-vbnc
Create GPG Public Key
If you don't already have a GPG key to sign packages, you can generate one with:
gpg --full-generate-key
When prompted, choose options 9 and 1 to generate the key (should be the defaults).
Note that Redhat has a good walkthrough on generating new GPG keys and exporting the public part for distribution: https://www.redhat.com/en/blog/creating-gpg-keypairs
Doing the steps above, this is the key that was generate to sign the HomeSeer Repak Github Repo (https://homeseer.digiflux.org).
gpg --export --armor 888F492DFBCB1C4D4C5B6077FA324674BB6F3B03 > /etc/apt/keyrings/homeseer_key.asc
-----BEGIN PGP PUBLIC KEY BLOCK-----
mDMEaMZUcRYJKwYBBAHaRw8BAQdAi8/Bt66wmJKcj0d4RC3jj6hDQ3TIxj71tVys
E1Tb9sG0SVN0YWN5IE9saXZhcyAoR1BHIEtleSBPbmx5IHRvIHNpZ24gSG9tZVNl
ZXIgLmRlYiBmaWxlcykgPGtnN3FpbkBhcnJsLm5ldD6IlgQTFgoAPhYhBIiPSS37
yxxNTFtgd/oyRnS7bzsDBQJoxlRxAhsDBQkSzAMABQsJCAcCBhUKCQgLAgQWAgMB
Ah4BAheAAAoJEPoyRnS7bzsDA74BAPSlHZ+tMbGlQBusMvtOMe7h7STtbK+STv7i
3XkrgkyNAQD3EmK8zgFYWoAyATi+Fl19OUY8qsXtVMrXDSYvmZchAbg4BGjGVHES
CisGAQQBl1UBBQEBB0AleQey27FKEkqEpAkLfcWrhGlwBNlgnyB5DNvSo43PZAMB
CAeIfgQYFgoAJhYhBIiPSS37yxxNTFtgd/oyRnS7bzsDBQJoxlRxAhsMBQkSzAMA
AAoJEPoyRnS7bzsDsBMA/RPZwHIIIjbhAwjCuFGMqG3YdU/dbm2K7nuJ5z3DKt40
AQCBtaXI5Lm/0WfRJcG4jEkbnOCipjZN+XWOGWs1XV38AQ==
=A3Z+
-----END PGP PUBLIC KEY BLOCK-----
Add GPG public key to system APT keyring for testing
gpg --yes -o /etc/apt/keyrings/homeseer-key.gpg --dearmor /etc/apt/keyrings/homeseer-key.asc
rm /etc/apt/keyrings/homeseer-key.asc
chown root:root /etc/apt/keyrings/homeseer-key.gpg
chmod ugo+r /etc/apt/keyrings/homeseer-key.gpg
chmod go-w /etc/apt/keyrings/homeseer-key.gpg
Add test repo to system for testing
Note: You will do your initial testing with aptly serve. You can change this after setting up your public facing distribution point.
Add create a file called /etc/apt/sources.list.d/homeseer-repak.list:
deb [signed-by=/etc/apt/keyrings/homeseer_key.gpg] http://<build machine IP>:8080/public stable main
Prepare build environment
- I've downloaded the HS4 4.2.44.4 tarfile and exracted it to a directory called homeseer-4.2.22-4_all. The general format for a .deb package build directory is <program>-<version>_<architecture>. In this instance, the _all signifies that it is for both 32 and 64 bit systems.
- Inside the homeseer-4.2.22-4_all directory I create the following directory structure:
DEBIAN
etc/systemd/system
opt/HomeSeer
- The DEBIAN directory hold the files needed to tell dpkg-deb how to build the .deb file.
- The etc and opt directories contain the filestructure with the files to be extracted/installed on the target system when the .deb file is installed. In our case there is a systemd .service file in etc that is placed in /etc/systemd/system and HomeSeer is put in /opt/HomeSeer.
DEBIAN directory files
Inside the DEBIAN directory resides 3 files:
control
postinst
prerm
- control is the file that contains information for dpkg-deb to build the package file. In the case of the .deb we are building, the control file contains:
Package: homeseer
Version: 4.2.22.4
Architecture: all
Essential: no
Maintainer: Only Packaged for Linux by kg7qin@arrl.net
Depends: mono-complete,mono-devel,flite,aha,ffmpeg,alsa-utils,tmux,curl,wget,nano,sudo
Recommends: chromium
Section: misc
Priority: optional
Homepage: https://homeseer.digiflux.org
Description: HomeSeer Home Automation Server (Linux Debian Package)
- postinst is the script file that is run and contains any setup/items needed after the .deb file is installed.
#!/bin/sh
/usr/sbin/useradd -m homeseer
/usr/sbin/usermod -a -G sudo homeseer
/usr/sbin/usermod -a -G dialout homeseer
echo "homeseer ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/homeseer
/usr/bin/chown -R homeseer:homeseer /opt/HomeSeer
/usr/bin/systemctl enable homeseer
echo " "
echo "HomeSeer install directory: /opt/HomeSeer"
echo "Additional files located in /opt"
echo " "
echo "*******************************************"
echo "* HomeSeer for Linux Setup Instructions *"
echo "* ------------------------------------- *"
echo "* For systems that do not have the *"
echo "* mono-vbnc package, you will need to *"
echo "* run the /opt/install-mono-basic.sh *"
echo "* script to download, compile, and *"
echo "* install this components of mono before *"
echo "* you can start HomeSeer. *"
echo "*******************************************"
echo " "
echo "* HomeSeer service installed"
echo " "
echo "Note: HomeSeer is run under the homeseer"
echo "user instead of root and uses sudo."
echo " "
echo "Refer to the README and README-CEPSTRAL files"
echo "located in /opt for more information."
echo " "
Note: The sudoers.d file should be more restrictive. This is the first iteration of this package and this was done to make it easier to test. Subsequent releases will have sudo locked down to specific commands to make it more secure. Adjust as necessary.
- prerm is the script file that is run and contains any items needed before the .deb package is removed.
#!/bin/sh
echo "Uninstalling HomeSeer..."
/usr/bin/systemctl disable homeseer
/usr/bin/systemctl stop --force homeseer
rm /etc/systemctl/system/homeseer.service
/usr/bin/systemctl daemon-reload
etc directory files
The /etc/systemd/system directory contains the homeseer.service file to allow homeseer to run as a service with systemd:
[Unit]
Description=HomeSeer Server
Documentation=https://homeseer.com
After=network-online.target remote-fs.target time-sync.target
Before=multi-user.target
[Service]
User=homeseer
WorkingDirectory=/opt/HomeSeer
ExecStart=/usr/bin/mono /opt/HomeSeer/HSConsole.exe --log
SyslogIdentifier=HS4
StandardOutput=null
StandardError=syslog
Restart=on-failure
RestartSec=30
KillMode=none
TimeoutStopSec=60
ExecStop=/opt/HomeSeer/hsstop.sh
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
opt directory files
- The HS4 Linux distrubution tarfile is extracted to /opt/HomeSeer.
- This directory also contains other files such as the README, README-CEPSTRAL, client.conf, puseaudio.service, and install-mono-basic.sh. These are additional files distributed with this package for users to install the missing mono-basic package (mono-vbnc), provides general information on the package, and also how to setup the Cepstral voices for use with HomeSeer.
Building .deb package
One you have all the files in the homeseer-4.2.22-4_all directory, you are then ready to build the .deb package file:
dpkg-deb --build --root-owner-group homeseer-4.2.22-4_all
If all goes well, you will have a .deb file called homeseer-4.2.22-4_all.deb created.
Creating .deb repo
We use the aptly utility to create the .deb repo and package/sign our .deb file for distribution.
aptly configuration
The defaults for aptly need to be changed to make it easier to publish the repo created.
- Place the following configuration file in /etc/aptly.conf
# Aptly Configuration File
###########################
# vim: : filetype=yaml
# aptly 1.6.0 supports yaml configuraiton files with inline documentation and examples.
# Legacy json config files are still supported, and may be converted to yaml with `aptly config show -yaml`
# Root directory for:
# - downloaded packages (`rootDir`/pool)
# - database (`rootDir`/db)
# - published repositories (`rootDir`/public)
root_dir: /srv/repos
# Log Level
# * debug
# * info
# * warning
# * error
log_level: info
# Log Format
# * default (text)
# * json
log_format: default
# Number of attempts to open database if it's locked by other instance
# * -1 (no retry)
database_open_attempts: -1
# Default Architectures
# empty list defaults to all available architectures
architectures:
# - amd64
# OBSOLETE
# in aptly up to version 1.0.0, package files were stored in internal package pool
# with MD5-dervied path, since 1.1.0 package pool layout was changed;
# if option is enabled, aptly stops checking for legacy paths;
# by default option is enabled for new aptly installations and disabled when
# upgrading from older versions
skip_legacy_pool: true
# Dependency following
#######################
# Follow contents of `Suggests:` field when processing dependencies for the package
dep_follow_suggests: false
# Follow contents of `Recommends:` field when processing dependencies for the package
dep_follow_recommends: false
# When dependency looks like `package-a | package-b`, follow both variants always
dep_follow_allvariants: false
# Follow dependency from binary package to source package
dep_follow_source: false
# Log additional details while resolving dependencies (useful for debugging)
dep_verbose_resolve: false
# PPA
######
# Specify paramaters for short PPA url expansion
# empty defaults to output of `lsb_release` command
ppa_distributor_id: ubuntu
# Codename for short PPA url expansion
ppa_codename: ""
# Aptly Server
###############
# Serve published repos as well as API
serve_in_api_mode: false
# Enable metrics for Prometheus client
enable_metrics_endpoint: false
# Not implemented in this version.
# Enable API documentation on /docs
#enable_swagger_endpoint: false
# OBSOLETE: use via url param ?_async=true
async_api: false
# Database
###########
# Database backend
# Type must be one of:
# * leveldb (default)
# * etcd
database_backend:
type: leveldb
# Path to leveldb files
# empty dbPath defaults to `rootDir`/db
db_path: ""
# type: etcd
# # URL to db server
# url: "127.0.0.1:2379"
# Mirroring
############
# Downloader
# * "default"
# * "grab" (more robust)
downloader: default
# Number of parallel download threads to use when downloading packages
download_concurrency: 4
# Limit in kbytes/sec on download speed while mirroring remote repositories
download_limit: 0
# Number of retries for download attempts
download_retries: 0
# Download source packages per default
download_sourcepackages: false
# Signing
##########
# GPG Provider
# * "internal" (Go internal implementation)
# * "gpg" (External `gpg` utility)
gpg_provider: gpg
# Disable signing of published repositories
gpg_disable_sign: false
# Disable signature verification of remote repositories
gpg_disable_verify: false
# Publishing
#############
# Do not publish Contents files
skip_contents_publishing: false
# Do not create bz2 files
skip_bz2_publishing: false
# Storage
##########
# Filesystem publishing endpoints
#
# aptly defaults to publish to a single publish directory under `rootDir`/public. For
# a more advanced publishing strategy, you can define one or more filesystem endpoints in the
# `FileSystemPublishEndpoints` list of the aptly configuration file. Each endpoint has a name
# and the following associated settings.
#
# In order to publish to such an endpoint, specify the endpoint as `filesystem:endpoint-name`
# with `endpoint-name` as the name given in the aptly configuration file. For example:
#
# `aptly publish snapshot wheezy-main filesystem:test1:wheezy/daily`
#
filesystem_publish_endpoints:
# # Endpoint Name
# test1:
# # Directory for publishing
# root_dir: /opt/srv/aptly_public
# # File Link Method for linking files from the internal pool to the published directory
# # * hardlink
# # * symlink
# # * copy
# link_method: hardlink
# # File Copare Method for comparing existing links from the internal pool to the published directory
# # Only used when "linkMethod" is set to "copy"
# # * md5 (default: compare md5 sum)
# # * size (compare file size)
# verify_method: md5
# S3 Endpoint Support
#
# cloud storage). First, publishing
# endpoints should be described in aptly configuration file. Each endpoint has name
# and associated settings.
#
# In order to publish to S3, specify endpoint as `s3:endpoint-name:` before
# publishing prefix on the command line, e.g.:
#
# `aptly publish snapshot wheezy-main s3:test:`
#
s3_publish_endpoints:
# # Endpoint Name
# test:
# # Amazon region for S3 bucket
# region: us-east-1
# # Bucket name
# bucket: test-bucket
# # Prefix (optional)
# # publishing under specified prefix in the bucket, defaults to
# # no prefix (bucket root)
# prefix: ""
# # Default ACLs (optional)
# # assign ACL to published files:
# # * private (default, for use with apt S3 transport)
# # * public-read (public repository)
# # * none (don't set ACL)
# acl: private
# # Credentials (optional)
# # Amazon credentials to access S3 bucket. If not supplied, environment variables
# # `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_SESSION_TOKEN` are used
# access_key_id: ""
# secret_access_key: ""
# session_token: ""
# # Endpoint (optional)
# # When using S3-compatible cloud storage, specify hostname of service endpoint here,
# # region is ignored if endpoint is set (set region to some human-readable name)
# # (should be left blank for real Amazon S3)
# endpoint: ""
# # Storage Class (optional)
# # Amazon S3 storage class, defaults to `STANDARD`. Other values
# # available: `REDUCED_REDUNDANCY` (lower price, lower redundancy)
# storage_class: STANDARD
# # Encryption Method (optional)
# # Server-side encryption method, defaults to none. Currently
# # the only available encryption method is `AES256`
# encryption_method: ""
# # Plus Workaround (optional)
# # Workaround misbehavior in apt and Amazon S3 for files with `+` in filename by
# # creating two copies of package files with `+` in filename: one original
# # and another one with spaces instead of plus signs
# # With `plusWorkaround` enabled, package files with plus sign
# # would be stored twice. aptly might not cleanup files with spaces when published
# # repository is dropped or updated (switched) to new version of repository (snapshot)
# plus_workaround: false
# # Disable MultiDel (optional)
# # For S3-compatible cloud storages which do not support `MultiDel` S3 API,
# # enable this setting (file deletion would be slower with this setting enabled)
# disable_multidel: false
# # Force Signature v2 (optional)
# # Disable Signature V4 support, useful with non-AWS S3-compatible object stores
# # which do not support SigV4, shouldn't be enabled for AWS
# force_sigv2: false
# # Force VirtualHosted Style (optional)
# # Disable path style visit, useful with non-AWS S3-compatible object stores
# # which only support virtual hosted style
# force_virtualhosted_style: false
# # Debug (optional)
# # Enables detailed request/response dump for each S3 operation
# debug: false
# Swift Endpoint Support
#
# aptly can publish a repository directly to OpenStack Swift.
# Each endpoint has name and associated settings.
#
# In order to publish to Swift, specify endpoint as `swift:endpoint-name:` before
# publishing prefix on the command line, e.g.:
#
# `aptly publish snapshot jessie-main swift:test:`
#
swift_publish_endpoints:
# # Endpoint Name
# test:
# # Container Name
# container: taylor1
# # Prefix (optional)
# # Publish under specified prefix in the container, defaults to no prefix (container root)
# prefix: ""
# # Credentials (optional)
# # OpenStack credentials to access Keystone. If not supplied, environment variables `OS_USERNAME` and `OS_PASSWORD` are used
# username: ""
# password: ""
# # Domain (optional)
# # OpenStack domain
# domain: ""
# domain_id: ""
# # Tenant (optional)
# # OpenStack tenant (in order to use v2 authentication)
# tenant: ""
# tenant_id: ""
# tenant_domain: ""
# tenant_domain_id: ""
# # Auth URL (optional)
# # Full url of Keystone server (including port, and version).
# # Example `http://identity.example.com:5000/v2.0`
# auth_url: ""
# Azure Endpoint Support
#
# aptly can be configured to publish repositories directly to Microsoft Azure Blob
# Storage. First, publishing endpoints should be described in the aptly
# configuration file. Each endpoint has its name and associated settings.
azure_publish_endpoints:
# # Endpoint Name
# test:
# # Container Name
# container: container1
# # Prefix (optional)
# # Publishing under specified prefix in the container, defaults to no prefix (container root)
# prefix: ""
# # Credentials
# # Azure storage account access key to access blob storage
# account_name: ""
# account_key: ""
# # Endpoint URL
# # See: Azure documentation https://docs.microsoft.com/en-us/azure/storage/common/storage-configure-connection-string
# # defaults to "https://<accountName>.blob.core.windows.net"
# endpoint: ""
# Package Pool
#
# Location for storing downloaded packages
# Type must be one of:
# * local
# * azure
packagepool_storage:
# Local Pool
type: local
# Local Pool Path
# empty path defaults to `rootDir`/pool
path:
# # Azure Azure Blob Storage Pool
# type: azure
# # Container Name
# container: pool1
# # Prefix (optional)
# # Publishing under specified prefix in the container, defaults to no prefix (container root)
# prefix: ""
# # Credentials
# # Azure storage account access key to access blob storage
# account_name: ""
# account_key: ""
# # Endpoint URL
# # See: Azure documentation https://docs.microsoft.com/en-us/azure/storage/common/storage-configure-connection-string
# # defaults to "https://<accountName>.blob.core.windows.net"
# endpoint: "
You make note of the location of the repo that is created by aptly and adjust as needed. We are creating our repo in /srv/repos.
Create repo
Create the repository with the following aptly commands
- Create the repo
aptly repo create -distribution=stable -component=main binary-all
- Add the .deb file we built to the repo
aptly repo add binary-all homeseer-4.2.22-4_all.deb
- Publish the .deb file we added to the repo (note you will be prompted for your GPG key's password)
aptly publish repo -architectures="i386,amd64" binary-all
Finished product
- When completed you will have your repository in the directory specified in your aptly.conf file. In our case it resides in /srv/repos
db
pool
public
- db and pool are used by aptly to track the files added to the repo
- public is the directory you will want to make available for public access. The dists and pool directory are where apt/apt-get will go to download files from your repo. When serving the contents of public, make sure the first directory of the repo is called public, otherwise you will need to alter your /etc/apt/sources.list.d/homeseer-repak.list file to contain the path of the .deb files in dists and pool.
Congratulations! You have successfully created your first .deb repository. Now test out the installation of the .deb file and removal
- Start test server
aptly serve
- Install the .deb file
apt-get update
apt-get install homeseer
- Start the systemd service
systemctl start homeseer
systemctl status homeseer
- Attempt to login to the homeseer web interface -- it should prompt you for a username and password (default/default)
- Ensure the HS4 registration/serial page is displayed
- Uninstall the .deb file
apt-get remove homeseer
Debian Packaging Tutorial by Gürkan Myczko
Debian Packaging [1]
- pretty much the same with Ubuntu
- 2003-2020 Gürkan Myczko <gurkan@phys.ethz.ch>
Introduction
We will package mguesser for Debian. Knowledge about Makefiles, configure and shell scripting or C programming can be very helpful. Man and the manpages are also very useful. Also have a look at help2man or pod2man. I want to guide you packaging mguesser from the beginning to the end.
What you will need
A development environment, some utilities and debhelper which we will install like this:
# apt-get install binutils cpp cpio dpkg-dev file gcc make patch \ dh-make debhelper devscripts fakeroot lintian \ debian-policy developers-reference \ man-db manpages reportbug
- (or build-essential lintian debhelper dh-make devscripts fakeroot)
Setting up the environment, see www.linuks.mine.nu/conf/. Of course you fill in your email address and your full name and choose your favourite editor, these settings is what I use:
$ export DEBEMAIL="gurkan@phys.ethz.ch" $ export DEBFULLNAME="Gürkan Myczko" $ export EDITOR=mcedit
Choosing software you want to package for Debian
For this course I have chosen mguesser, as I packaged it already and it is not too hard (single binary) to get you started. This should give you an idea what it is like. For Debian the package
- must be DFSG compliant
- must have some public download place (usually http/https, or ftp)
- naming should be software-maj.min.tar.gz and unpacks into software-maj.min/
Build systems
There are plenty of build systems. The most classical ones are
make; make install
./configure; make; make install
mk (Plan 9)
cmake (ccmake is handy sometimes)
scons
qmake (for qt based software)
gnustep-make
bazel
- Plenty more to check out at https://en.wikipedia.org/wiki/List_of_build_automation_software
Packaging the software
First you need to get the source of the software. Then put it in some directory. There you unpack the software, usually
$ tar xzvf software-2.1.tar.gz $ ln -s software-2.1.tar.gz software_2.1.orig.tar.gz $ cd software-2.1/ $ dh_make
dh_make will make a directory called debian/ with a few files in it. Now your task is to fill them. I usually start with control, then edit copyright. There is also changelog, rules, dirs and more. In rules you will find some dh commands, they are all from debhelper. Each of them has a manpage, please refer to man debhelper for details, as well as the Debian New Maintainers' Guide. Read more about the Debian Policy Manual. Updating the debian/changelog is done with dch or debchange (symlink).
When you think the thing is ready, you can start building the package
The most important files are probably debian/changelog, debian/control, debian/copyright, debian/rules and the following ones are helpful
- debian/clean files to be removed that get generated at build time (make clean)
- debian/install files to be installed
- debian/manpages manpage(s) to be installed
You will find licensecheck -r . | grep -v UNKNOWN useful during the writing of debian/copyright.
$ debuild
When things are built you should check the package using
$ lintian -vi mguesser_0.2-1_i386.changes
If that says all is right (no Warnings or Errors). You can install it with
$ sudo dpkg -i mguesser_0.2-1_i386.deb
You must check if the program works. Also make sure the manpage is working. From what I experienced I will most likely to have go through this several times.
Never forget reportbug, it will be your friend. When you package something from http://www.debian.org/devel/wnpp/requested you can close the bug also with an e-mail to <number>-done@bugs.debian.org. If you don't know what to package, check orphaned packages.
It can be helpful to look at how other packages are packaged
$ apt-get source package
And being in touch with upstream is important, contact them somehow. My debian/ directory for mguesser. Here is my Makefile changes.
Ensuring completeness of Build-Depends Use `sbuild` to ensure build-depends are complete:
apt-get install sbuild mkdir -p /srv/chroot/sid sbuild-createchroot --include=eatmydata,ccache,gnupg unstable /srv/chroot/sid http://deb.debian.org/debian
Now test your packages with:
sbuild -d sid yourpackage_version-rev.dsc
After Work
- Adding a screenshot to the package is recommended: http://screenshots.debian.net/
- Tagging the package is also a good idea: https://debtags.debian.org/
- Check Lintian output: https://lintian.debian.org/
- I did not go into detail with debian/watch.
- Make sure it builds on as many architectures as possible: https://buildd.debian.org/
- If your software works with data files, that can be detected with the file(5) utility, send patches for its detection.
Links
A good source for free software https://github.com All Debian project participants www.debian.org/devel/people
Other packaging systems (ports, pkg, rpm)
- Freebsd: https://www.freebsd.org/doc/en/books/porters-handbook/
- Solaris: https://docs.oracle.com/cd/E26505_01/html/E28550/ch2buildpkg-17051.html
- RedHat: https://access.redhat.com/sites/default/files/attachments/rpm_building_practice_10082013.pdf
- macOS: https://guide.macports.org/#project.contributing
- Spack: https://github.com/spack/spack
- flatpak/flathub: https://flatpak.org/ / https://flathub.org/
- Ubuntu Snap: https://snapcraft.io/
Checkinstall
checkinstall [2] keeps track of all the files created or modified by your installation script, builds a standard binary package (.deb, .rpm, .tgz) and installs it in your system giving you the ability to uninstall it with your distribution's standard package management utilities.
You have an archive with software to build.
- Normal install
./configure make make install make clean
- Build and use checkinstall to install and build the .deb file
./configure make checkinstall
- Build and use checkinstall to create the .deb file without installing it
./configure make checkinstall --install=no
Refer to the man page for more information
Simple method #1
From https://www.internalpointers.com/post/build-binary-deb-package-practical-guide[3] A deb file is an archive that contains data. Marked with the .deb extension, it is used to easily distribute and install programs for Linux Debian and derivatives. Deb files are handy when your app needs to take care of additional dependencies, integrate itself with the desktop, run pre and post install scripts and so on.
In this quick tutorial I want to show you how to generate a deb package from scratch that will install a binary executable in the target system. Let's start off with a bit of theoretical background.
Anatomy of a deb package
A deb is a standard Unix ar archive that contains your application and other utility files. The most important one is the control file, which stores the information about the deb package and the program it installs.
Internally, a deb package contains a collection of folders that mimics a typical Linux file system, such as /usr, /usr/bin, /opt and so on. A file put in one of those directories will be copied to the same location in the actual file system during installation. So, for example a binary file put into <.deb>/usr/local/bin/binaryfile will be installed to /usr/local/bin/binaryfile.
On the outside instead, all deb package files follow a specific naming convention:
<name>_<version>-<revision>_<architecture>.deb
That is:
<name> – the name of your application; <version> – the version number of your application; <revision> – the version number of the current deb package; <architecture> – the hardware architecture your program will be run on.
For example, suppose you want to release your program called hello, version 1.0, built for 64-bit ARM processors. Your deb file name would look something like hello_1.0-1_arm64.deb.
Making the deb package
We are now ready to generate the package. Make sure you have the dpkg-deb program installed in your system: this will be used later on to generate the final archive
Create working directory
Create a temporary working directory to make your package in. Follow the same naming convention we have seen before. For example:
mkdir hello_1.0-1_arm64
Create internal structure
Put your program files where they should be installed to on the target system. For example, suppose you want your program to be installed to /usr/local/bin:
mkdir -p hello_1.0-1_arm64/usr/local/bin
The -p flag to the mkdir command will create nested directories. Then copy the executable file in there:
cp ~/YourProjects/Hello/hello hello_1.0-1_arm64/usr/local/bin
Create control file
The control file lives inside the DEBIAN directory. Mind the uppercase: a similar directory named debian (lowecase) is used to store source code for the so-called source packages. This tutorial is about binary packages, so we don't need it.
Let's create the DEBIAN folder first:
mkdir helloworld_1.0-1_arm64/DEBIAN
And then create the empty control file:
touch helloworld_1.0-1_arm64/DEBIAN/control
Fill in control file
Open the file previously created with your text editor of choice. The control file is just a list of data fields. For binary packages there is a minimum set of mandatory ones:
Package – the name of your program; Version – the version of your program; Architecture – the target architecture; Maintainer – the name and the email address of the person in charge of the package maintenance; Description – a brief description of the program.
For example:
Package: hello Version: 1.0 Architecture: arm64 Maintainer: Internal Pointers <info@internalpointers.com> Description: A program that greets you. You can add a longer description here. Mind the space at the beginning of this paragraph.
The control file may contain additional useful fields such as the section it belongs to or the dependency list. The latter is extremely important in case your program relies on external libraries to work correctly. You can fill it manually if you wish, but there are helper tools to ease the burden. I will show you how in the next few paragraphs.
Build .deb package
This is the last step. Invoke dpkg-deb as following:
dpkg-deb --build --root-owner-group <package-dir>
So in our example:
dpkg-deb --build --root-owner-group <helloworld_1.0-1_arm64>
The --root-owner-group flag makes all deb package content owned by the root user, which is the standard way to go. Without such flag, all files and folders would be owned by your user, which might not exist in the system the deb package would be installed to.
The command above will generate a nice .deb file alongside the working directory or print an error if something is wrong or missing inside the package.
Test .deb package
It's a good idea to test your deb package once created. You can install it like any other regular deb package:
sudo dpkg -i <package>
Make sure it can be also uninstalled easily. You can just remove the package:
sudo dpkg -r <appname>
or remove it along with the configuration files (if any):
sudo dpkg -P <appname>
Make sure the application has been removed correctly by issuing:
dpkg -l | grep <appname>
The dpkg -l command lists all the packages installed, while grep searches for <appname>. The output should be blank if the app has been uninstalled correctly.
Sometimes the deb install goes wrong
Especially when you are dealing with pre/post install or removal scripts that fail at some point. This is a typical error message by dpkg:
Package is in a very bad inconsistent state - you should reinstall it before attempting a removal.
which prevents any progress. The trick is to move all references to your broken package somewhere safe (e.g. the /tmp directory) and then force remove it, like so:
sudo mv /var/lib/dpkg/info/<packagename>.* /tmp/ sudo dpkg --remove --force-remove-reinstreq <packagename>
Taking care of external dependencies
You can generate them automatically with dpkg-shlibdeps. It will parse your binary and look for external symbols. At the time of writing, that tool doesn't seem to work out of the box. For some unknown (to me) reasons it wants the debian/control file to be present in the working directory – that's for source packages, remember? The workaround here is to create it, then move into the working folder and run:
dpkg-shlibdeps -O path/to/binary/file
The -O flag will print dependencies on the standard output. Copy the output and paste it in the Depends section of your DEBIAN/control file. You can get rid of the debian/control file once done. I'm pretty sure there are better ways for this, though: I will update the article once I get additional information on this step.
Run scripts before or after package installation and removal
Four files: postinst, preinst, postrm, and prerm are called maintainer scripts. Such files live inside the DEBIAN directory and, as their names suggest, preinst and postinst are run before and after installation, while prerm and postrm are run before and after removal. They must be marked as executables. Also, remember to set permissions: must be between 0555 and 0775.
Simple method #2
- Configure your paths and filenames
SOURCEBINPATH=~ SOURCEBIN=myscript.sh DEBFOLDER=~/somescripts DEBVERSION=0.1 DEBFOLDERNAME=$DEBFOLDER-$DEBVERSION
- Create your scripts source dir
mkdir $DEBFOLDERNAME
- Copy your script to the source dir
cp $SOURCEBINPATH/$SOURCEBIN $DEBFOLDERNAME cd $DEBFOLDERNAME
- Create the packaging skeleton (debian/*)
dh_make -s --indep --createorig
- Remove make calls
grep -v makefile debian/rules > debian/rules.new mv debian/rules.new debian/rules
- debian/install must contain the list of scripts to install as well as the target directory
echo $SOURCEBIN usr/bin > debian/install
- Remove the example files
rm debian/*.ex
- Build the package. You will get a lot of warnings and ../somescripts_0.1-1_i386.deb
debuild
GPG sign and verify deb packages and APT repositories
From https://packagecloud.io/blog/how-to-gpg-sign-and-verify-deb-packages-and-apt-repositories/ [4]
TL;DR
GPG can be used to create a digital signature for both Debian package files and for APT repository metadata.
Many Debian-based Linux distributions (e.g., Ubuntu) have GPG signature verification of Debian package files (.deb) disabled by default and instead choose to verify GPG signatures of repository metadata and source packages (.dsc). The setting which enables GPG signature checking of the individual .deb packages can be found in /etc/dpkg/dpkg.cfg and is set to no-debsig, but there are important caveats to enabling this option explained below.
Further, most official Debian package files from the publicly accessible repositories do not have GPG signatures. The official repository metadata is GPG signed, as are the source packages, but the .deb packages themselves are not.
If you publish a Debian package and GPG sign the package yourself before distributing it to users, those users’ systems will, in most cases, not verify the signature of the package unless they have done a considerable amount of configuration. However, their system will, in most cases, automatically verify repository metadata.
This post explains how Debian package GPG signatures are implemented, how to enable GPG signature checking of packages on your system, and how to GPG sign packages you create.
This post will also explain how APT repository GPG signatures are implemented and how to sign your APT repository metadata
Debian package GPG signatures
Signing data with a GPG key enables the recipient of the data to verify that no modifications occurred after the data was signed (assuming the recipient has a copy of the sender’s public GPG key).
Debian package files (.deb files), Debian source packages (.dsc files), and Debian changes files (.changes files) can all be signed with GPG.
GPG signing Debian source packages (.dsc files) and changes files
Debian source packages and Debian changes files are text-based files which can be used to rebuild a Debian package from source code. Theses files can be signed with a GPG key by using a program called debsign.
debsign will add a GPG signature to .dsc and .changes files themselves. You can use debsign like this:
$ debsign -k E732A79A test_1.0-7.dsc
This command specifies the GPG key ID to use when signing the Debian source file test_1.0-7.dsc. The signature is embedded in the file test_1.0-7.dsc and can be verified by running:
$ gpg --verify test_1.0-7.dsc
The Debian source package file and changes file both contain SHA1, SHA256, and MD5 checksums of all source files that comprise the source package. Since this data is signed, any modifications to the source file checksums, file size, or other data in the dsc or changes files would cause signature verification to fail.
As long as a recipient who wishes to build the deb package from a dsc file verifies the checksums of each file in the source package and verifies the signature of the source package itself, the recipient knows their build uses precisely the same files that the packager used when building.
apt-get source packagename will automatically check the GPG signature of the source package file and the checksums of the source files listed in the source package.
GPG signing Debian package files (.deb files)
A Debian package file itself is actually an AR archive that contains at least three entries:
- debian-binary: a file describing the deb format version
- control: an archive containing package metadata such as pre-install or post-install scripts
- data: an archive containing the actual installable files
The control and data archives are typically tar, tar.gz, tar.bz2, tar.xz, tar.lzma, or another archive format.
When a Debian package is signed with a GPG key, the signature is also included in the archive and is named based on its type.
Debian package signatures are typically associated with a role. Some commonly used roles include:
- origin: the signature of the organization which distributes the .deb file
- maint: the signature of the package maintainer
- archive: the signature of the archive providing the package to reassure users that the package is the actually provided by the archive
In order to sign a Debian package, you can use the program debsigs:
$ debsigs --sign=origin -k E732A79A test_1.0-7_amd64.deb
In the above example, the Debian file is signed using the GPG key on my system with the ID E732A79A. The signature type is origin and thus the deb package will be modified to include a file called _gpgorigin that contains the GPG signature of the debian-binary file, control archive, and data archive from the deb package concatenated together.
I used the type origin because I am person distributing the deb package test_1.0-7_amd64.deb.
Verifying signatures on Debian .deb package files is a bit more involved than the other verification methods.
Verifying GPG signatures of .deb package files
n order to verify .deb package files, you must have the program debsig-verify installed, import the public GPG keys you will use to verify packages to the debsig-verify keyrings, and you must also create an XML policy document for signature verification.
First, create a keyring directory for the public key and import the public key to the debsig-verify GPG keyring:
$ mkdir /usr/share/debsig/keyrings/DDDF2F4CE732A79A/
$ gpg --no-default-keyring \ --keyring /usr/share/debsig/keyrings/DDDF2F4CE732A79A/debsig.gpg \ --import my-public-key
In the above example, the keyring subdirectory, DDDF2F4CE732A79A, is the fingerprint of the GPG key which will be imported. Note that the name of the keyring debsig.gpg will be used in the XML policy document created next. You can use any filename you like.
Now that the public key is imported, the XML policy document can be constructed.
First, create a directory for the policy document. It must have the GPG key fingerprint in the path:
$ mkdir /etc/debsig/policies/DDDF2F4CE732A79A/
Next create the XML document. The debsig-verify man page indicates that the filename is irrelevant so long as the filename ends with .pol and resides in a directory named after the GPG key fingerpint.
You can find more information regarding how the build the policy document in /usr/share/doc/debsig-verify/policy-syntax.txt and some examples in /usr/share/doc/debsig-verify/examples. Here’s an example XML policy document:
<?xml version="1.0"?>
<!DOCTYPE Policy SYSTEM "http://www.debian.org/debsig/1.0/policy.dtd">
<Policy xmlns="http://www.debian.org/debsig/1.0/">
<Origin Name="test" id="DDDF2F4CE732A79A" Description="Test package"/>
<Selection>
<Required Type="origin" File="debsig.gpg" id="DDDF2F4CE732A79A"/>
</Selection>
<Verification MinOptional="0">
<Required Type="origin" File="debsig.gpg" id="DDDF2F4CE732A79A"/>
</Verification>
</Policy>
Once this document is constructed, you can verify the GPG signature of the Debian .deb package:
$ debsig-verify test_1.0-7_amd64.deb debsig: Verified package from `Test package' (test)
dpkg support for debsig-verify
dpkg has support for verifying GPG signatures of Debian package files, but this verification is disabled by default. This means that when a package is installed on a Debian-based system, the signature checking for individual packages is disabled.
In order to enable it, the file /etc/dpkg/dpkg.cfg will need to be modified to remove the no-debsig option from the config file.
CAUTION: Doing this may cause dpkg to reject installation of unsigned Debian files. If you plan to enable this, it is strongly recommended you try this first in a testing environment. You’ll also need to ensure you have the correct XML policy files created, as explained above.
Another Debian package signing tool: dpkg-sig
here is a another tool available for signing and verifying Debian package files called dpkg-sig. Signatures created by this tool are not compatible with debsigs and the tools cannot understand or verify each other signatures.
dpkg-sig generates a Debian control file with several userful fields:
- Version of dpkg-sig which generated the signature
- Gpg key signer information
- Role
- Files section with checksums, file sizes, and filenames of the control, data, and debian-binary files in the Debian package
This file is named based on the role selected, which is defaulted to builder if none is specified. So, signing a Debian package file with no role would result in a file named _gpgbuilder being created and added to the package file. This file is signed with GPG and the signature is embedded as plain text (a “clearsign” GPG signature).
The result of this scheme is that the GPG signature can be verified using commonly available software. The Debian package can be extracted with ar and the signature of the control file can be checked with just gpg --verify.
Signing a Debian file with dpkg-sig is straight forward:
$ dpkg-sig -k E732A79A --sign builder test_1.0-7_amd64.deb
The signature can also be checked by using dpkg-sig directly, if desired:
$ dpkg-sig --verify test_1.0-7_amd64.deb Processing test_1.0-7_amd64.deb... GOODSIG _gpgbuilder 284E8BE753AE45DFF8D82748DDDF2F4CE732A79A 1414371553
Additionally, no XML policy document is required for signature verification, which makes signature verification much simpler to integrate into a workflow.
GPG signatures for APT repository metadata
It is also possible to generate GPG signatures for APT repositories in addition to generating signatures for Debian packages themselves.
Generating signatures of the repository metadata allows you to verify that the repository metadata has not been tampered with since it was generated by the repository host.
This is critical to preventing a Man-in-the-Middle attack attack where a malicious actor plants repository metadata causing a user’s package manager to install potentially malicious packages.
Luckily, generating GPG sigantures for APT repositories is straightforward with reprepro.
Assuming you have a GPG key you’d like to use to sign the repository metadata, you can simply modify your reprepro repository config to add your GPG key.
Using the same GPG key ID used in the earlier examples, the conf/distributions config file can be modified to add the field:
SignWith: E732A79A
This will cause reprepro to generate GPG signatures of the repository metadata. reprepro will generate a signature of the apt Release file and store the signature in the file Release.gpg. This is called a “detached GPG signature.”
Newer version of reprepro will generate an embedded text GPG signature (a “clearsign” GPG signature) in a file named InRelease.
The apt client will download the GPG Release file(s) it understands and will automatically attempt to verify the GPG signature of the repository.
Users of the repository will need to have the GPG key installed on the system. This can be done by end users running the command: apt-key:
$ sudo bash -c 'wget -O - https://packagecloud.io/gpg.key | apt-key add -'
The above command downloads a GPG key and adds the GPG key to the system using apt-key
Conclusion
GPG signing of Debian package files, source packages, and repository metadata is critical to establishing the authenticity of the package objects and repository metadata.
Knowing that a package being installed on your system is the same package that was generated by a vendor, and that you were able to retrieve that package from the repository generated by the vendor is the only way to ensure that you are safely installing software in your infrastructure.
Unfortunately, signing and checking the signatures of Debian packages is not as streamlined and integrated as it could be, so vendors currently signing their Debian package files may be surprised to learn that the majority of their customers are not automatically checking the GPG signature of their packages. At the very least, vendors should be signing their repository metadata. Also signing their Debian package files and providing instructions for how users can verify those signatures would be a great way to help improve their end user’s security.
packagecloud.io signs repository metadata it generates using a GPG key so that users of our repositories know, without a doubt, that the repository metadata their package manager downloaded was generated by packagecloud.io.