bpkg-repository-signing(1)

πŸ“¦
version 0.17.0 Β· 2 versions
, in fedora 43, fedora 44, nixos 26.05

πŸ’– NAME πŸ’–

bpkg-repository-signing β€” how to sign repository πŸ‘‘


✨ SYNOPSIS ✨

bpkg rep-create --key ...

πŸ¦‹ DESCRIPTION πŸ¦‹

okay bestie so the whole point of signing a repository is to stop anyone from messing with your packages β€” either while they're flying over the wire or once they're sitting on the repository host machine. and like, ideally you generate and sign the repository manifests on a separate build machine that lives behind a firewall. that way, if (or, honestly, when) your host machine gets compromised, it's genuinely hard for an attacker to tamper with the repository packages without anyone noticing. and since the repository key is chillin' on the build machine (or, even better, on a one-way PIV/PKCS#11 device β€” see below), they literally can't re-sign the modified repository. iconic security behavior πŸ’…

bpkg uses X.509 public key cryptography for repository signing. currently, only the explicit first use certificate authentication is implemented. what does that mean? so for a repository certificate that's unknown to this bpkg configuration, its subject information and fingerprint get presented to the user. if the user confirms the authenticity of the certificate, then it's added to the configuration, and any repository that in the future presents this certificate is trusted without further confirmations β€” as long as its name matches the certificate's subject (see below, bestie). in the future a certificate authority (CA)-based model may be added. πŸ‘€

the rest of this guide shows how to create a key/certificate pair for pkg repository signing and use it to sign a repository. at the end it also briefly explains how to store the private key on a PIV/PKCS#11 device using Yubikey 4 as an example. let's gooo 🌈


πŸ”‘ 1. Generate Private Key

first step, bestie: generate the private key.

$ openssl genrsa -aes256 2048 >key.pem

if you would like to generate a key without password protection (not a good idea except for testing, fr fr 🚩), leave the -aes256 option out. you may also need to add -nodes depending on your openssl(1) configuration.


πŸŽ€ 2. Generate Certificate

next, create the certificate configuration file by saving the following into cert.conf. you may want to keep it around in case you need to renew an expired certificate, etc.

name  = example.com
org   = Example, Inc
email = admin@example.com

[req]
distinguished_name = req_distinguished_name
x509_extensions    = v3_req
prompt             = no
utf8               = yes

[req_distinguished_name]
O  = $org
CN = name:$name

[v3_req]
keyUsage         = critical,digitalSignature
extendedKeyUsage = critical,codeSigning
subjectAltName   = email:$email

adjust the first three lines to match your details. if the repository is hosted by an organization, use the organization's name for org. if you host it as an individual, put your full, real name there. using any kind of aliases or nicknames is a bad idea (except, again, for testing πŸ™ˆ). remember, users of your repository will be presented with this information and if they see it was signed by someone named SmellySnook, they will unlikely trust it πŸ’€. also use a working email address in case users need to contact you about issues with your certificate. note that the name: prefix in the CN value is not a typo.

the name field is a canonical repository name prefix with the pkg: type part stripped. any repository with a canonical name that starts with this prefix can be authenticated by this certificate (see the repository manifest documentation for more information on canonical names). for example, name example.com will match any repository hosted on {,www.,pkg.,bpkg.}example.com. while name example.com/math will match {...}example.com/pkg/1/math but not {...}example.com/pkg/1/misc.

a certificate name can also contain a subdomain wildcard. a wildcard name in the *.example.com form matches any single-level subdomain, for example foo.example.com but not foo.bar.example.com, while a wildcard name in the **.example.com form matches any subdomain, including multi-level. the above two forms do not match the domain itself (example.com in the above example). if this is desired, the *example.com and **example.com forms should be used instead. note that these forms still only match subdomains. in other words, they won't match fooexample.com. wildcard names are less secure and therefore are normally only used for testing and/or internal repositories 🚨

once the configuration file is ready, generate the certificate:

openssl req -x509 -new -sha256 -key key.pem \
  -config cert.conf -days 730 >cert.pem

to verify the certificate information, run:

openssl x509 -noout -nameopt RFC2253,sep_multiline \
  -subject -dates -email <cert.pem

🌸 3. Add Certificate to Repository

add the certificate: field for the base repository (role: base) in the repositories manifest file(s):

certificate:
\
<cert>
\

replace cert with the entire contents of cert.pem (including the BEGIN CERTIFICATE and END CERTIFICATE lines). so you will have an entry like this:

certificate:
\
-----BEGIN CERTIFICATE-----
MIIDQjCCAiqgAwIBAgIJAIUgsIqSnesGMA0GCSqGSIb3DQEBCwUAMDkxFzAVBgNV
.
.
.
+NOVBamEvjn58ZcLfWh2oKee7ulIZg==
-----END CERTIFICATE-----
\

πŸ’… 4. Sign Repository

when generating the repository manifests with the bpkg-rep-create(1) command, specify the path to key.pem with the --key option:

bpkg rep-create --key /path/to/key.pem /path/to/repository

you will be prompted for a password to unlock the private key. πŸ”


🧚 5. Using PIV/PKCS#11 Device

this optional step shows how to load the private key into Yubikey 4 and then use it instead of the private key itself for signing the repository. note that you will need OpenSSL 1.0.2 or later for the signing part to work.

first change the Yubikey MKEY, PUK, and PIN if necessary. you should definitely do this if it still has the factory defaults. then import the private key and the certificate into Yubikey (replace mkey with the management key):

yubico-piv-tool --key=<mkey> -a import-key -s 9c <key.pem
yubico-piv-tool --key=<mkey> -a import-certificate -s 9c <cert.pem

after this you will normally save the certificate/private key onto backup media, store it in a secure, offline location, and remove the key from the build machine. 🫢

to sign the repository with Yubikey specify the following options instead of just --key as at step 4 ("SIGN key" is the label for the slot 9c private key):

bpkg rep-create                                                     \
  --openssl-option pkeyutl:-engine --openssl-option pkeyutl:pkcs11  \
  --openssl-option pkeyutl:-keyform --openssl-option pkeyutl:engine \
  --key "pkcs11:object=SIGN%20key" /path/to/repository
note that for openssl versions prior to 3.0.0 bpkg uses the rsautl command instead of pkeyutl for the data signing operation. ⚠️

πŸ’€ BUGS πŸ’€

send bug reports to the users@build2.org mailing list. πŸ“§


Copyright (c) 2014-2024 the build2 authors.

permission is granted to copy, distribute and/or modify this document under the terms of the MIT License.


and that's the whole vibe, bestie β€” this was bpkg 0.17.0, June 2024, and yes, that's the whole manual page. slay βœ¨πŸ¦‹πŸ’–

πŸ“¦ where this page comes from