Package Signing
For security reasons the packages built by the build server can be signed. Whilst the signature is no guarantee the built package does not contain any malware it still verifies the package was built on your build server. This is useful when you host your repository without a TLS certificate (which is generally discouraged) or when there are mirrors which redistribute packages from your build server.
To enable package signing you'll have to provide the server container with a private key. A suitable key must have a minimum length of 2048 bits. There are two methods to provide a private key to your server, either via keyfile, or via a running gpg-agent. It's highly recommended to use a new gpg key for the package signing which isn't used anywhere else. Putting a private key and it's password onto a publicly accessible server poses a risk of identity theft!
Also note that if the signing is enabled after the initial setup of the server only new package builds will be signed. Old packages need to be rebuilt in order to get signed.
Using the signatures in Pacman
If you have successfully set up package signing, you'll still have to make pacman verify it.
When you are setting up a new host, the onboarding procedure of the CLI will walk you through setting up signatures automatically, if they are enabled on your server.
If you are already using Serene on a host, you can also use the CLI to guide you through importing the server's key on your machine. Make sure you don't forget to also update the SigLevel in your pacman config.
However, it can also easily be done manually done in three simple steps:
- Obtain the public key. You can do this via
serene manage keyif you have the CLI installed, or you can download it directly from the/keyapi endpoint. - Import the key into your
pacmankeyring. See the corresponding arch wiki section. - Tighten the
SigLevelin your pacman config. Remove the lineSigLevel = Optional TrustAllfrom the pacman config if you have added it during setup. See the arch wiki section for more information about it.
Setup with a Keyfile
The easiest way to configure signing is by providing the container with a private keyfile. When not using any hardware devices to provide the secret key, this is the recommended setup.
A gpg private key can be exported like this (again, make sure to have a seperate key just for Serene):
# export private key into `private-key.asc` in armored ascii format
gpg --armor --output private-key.asc --export-secret-key <key-id>Now, when deploying you'll have to add another bind mount to the docker-compose.yml:
# docker-compose.yml
services:
serene:
# ... remaining service definition
volumes:
- /path/to/private-key.asc:/app/sign_key.asc
# ... other volumes
environment:
# if your key requires a password, add the following to your envs
- SIGN_KEY_PASSWORD: <key-password>
# ... remaining configSetup with gpg-agent
If you want to store your private key in a more secure way, for example in a hardware device, you can use the gpg-agent as your keystore and let the server use the key from there.
To use this, you'll need to have a gpg-agent running and accessible to the server. The agent will then be exposed to the server inside the container via a bind mount to its socket. It is recommended to start your gpg-agent via your init system or something similar.
Managing the agent with systemd
Create a systemd socket and service for your gpg-agent:
# /etc/systemd/system/serene-gpg-agent.socket
[Unit]
Description=GnuPG cryptographic agent and passphrase cache for serene
[Socket]
ListenStream=/etc/serene/gnupg/S.gpg-agent
FileDescriptorName=std
SocketMode=0600
DirectoryMode=0700
[Install]
RequiredBy=docker.service# /etc/systemd/system/serene-gpg-agent.service
[Unit]
Description=GnuPG cryptographic agent and passphrase cache for serene
Requires=serene-gpg-agent.socket
[Service]
ExecStart=/usr/bin/gpg-agent --homedir /etc/serene/gnupg --supervised
ExecReload=/usr/bin/gpgconf --homedir /etc/serene/gnupg --reload gpg-agentEnable the service and test whether your devices are accessible:
# enable the socket
sudo systemctl enable serene-gpg-agent.socket
# test if your hardware key is detected
sudo GNUPGHOME=/etc/serene/gnupg gpg-connect-agent "LEARN --sendinfo" /byeThe gpg-agent only handles the secret key and the actual signing process. You still need to provide the server with the public key that matches the private key you intend to use. The server will then search for the corresponding private key in the agent. If there are multiple suitable keypairs, the first one found will be used.
NOTE
If using a PIN-protected hardware device, the server will refuse to use if only one PIN attempt remains. This is to prevent accidental lockouts or key destruction. If you encounter this, perform some action that requires a PIN (e.g., sign something manually with GPG) to reset the PIN retry counter or reset it using the admin PIN.
Now, when deploying you'll have to mount the socket and public key in the docker-compose.yml. We assume the gpg-agent socket is in /etc/serene/gnupg/S.gpg-agent:
# docker-compose.yml
services:
serene:
# ... remaining service definition
volumes:
- /path/to/public-key.asc:/app/sign_key.asc
- /etc/serene/gnupg/S.gpg-agent:/app/S.gpg-agent
# ... other volumes
environment:
# if your key requires a pin/password, add the following to your envs
- SIGN_KEY_PASSWORD: <key-password>
# ... remaining config