Motivation
Signing is a powerful mechanism to guarantee that only a Mender Artifact from an authorized source can be installed onto a device. If the signature cannot be verified, the artifact is discarded and the device state is not modified.The signing can happen either during artifact creation or as a subsequent step. Both of those flows are provided by the mender-artifact tool.
The resulting challenges are key management respectively pipeline creation. In a classic setting, the key used to sign the artifact needs to be present at the invocation site of mender-artifact. This requires that either the computer and user are permanently trusted, having access to the key.Or the intermediate, unsigned artifact needs to be moved to a trusted signing instance and retrieved afterward. Both of these flows are possible but suffer substantial drawbacks.
A more secure approach is to use a server based solution for signing, such as SignServer by Keyfactor. This approach offers considerable benefits, notably:
- The key for signing never leaves the server.
- Users and permissions are managed on the server.
The drawback is obviously:
- The SignServer instance requires setup and maintenance.
In this tutorial, we will cover the upcoming support for using SignServer directly from the mender-artifact tool to streamline the artifact creation and signing process in a sustainable method.
Prerequisites
SignServer
A SignServer instance needs to be provisioned and configured. The required settings are:
Property | Value |
---|---|
ACCEPTED_HASH_DIGEST_ALGORITHMS | SHA-256, SHA-384, SHA-512 |
AUTHTYPE | NOAUTH |
CRYPTOTOKEN | CryptoTokenP12 |
DEFAULTKEY | signer00003 |
DISABLEKEYUSAGECOUNTER | true |
DO_LOGREQUEST_DIGEST | |
IMPLEMENTATION_CLASS | org.signserver.module.cmssigner.PlainSigner |
LOGREQUEST_DIGESTALGORITHM | |
NAME | MenderPlainSigner |
SIGNATUREALGORITHM | NONEwithRSA |
TYPE | PROCESSABLE |
CLIENTSIDEHASHING | true |
If you do not have an existing SignServer instance, then please follow the documentation by Keyfactor to set one up. This might include an EJBCA instance for creating the required certificates. To get started, see the following tutorials:
- EJBCA set up: Start EJBCA Docker container, ephemeral instance - EJBCA, also https://youtu.be/x-kEqPrz1g
- EJBCA certificate creation: https://youtu.be/wMD1GgSF-JE
- SignServer set up: https://youtu.be/wMqPWKi3ukE
Using this instance, you need to prepare the following assets:
- Hostname of the SignServer instance (optionally, the IP address)
- Name of the configured worker
- Certificate for the instance root CA
- Client certificate for the signing process
Signature verification key
The Mender Client checks the resulting signature on a deployed artifact using the public key of the signing certificate. To facilitate this, the public key must be provisioned to the device before deploying the first signed update.
To prepare the public key for provisioning, it can be extracted from the certificate that has been used for the SignServer setup. If the certificate is directly accessible, the key can be saved directly using OpenSSL with the following command:
openssl x509 -pubkey -noout -in MenderPlainSigner.cer > MenderPlainSignerPubkey.pem
If the certificate is not available, export it from the SignServer administrative web frontend by first selecting the signer worker that is configured for the Mender signing process. Open the “Status Properties” tab, and click the “Details” action link next to the “Signer Certificate” row item. At the bottom of this page, click “Export Certificate” and save it to a suitable location. After that, you can use the above OpenSSL command to save the public key.
In order to configure the client only to accept signed artifacts, set the following configuration option in /etc/mender/mender.conf:
ArtifactVerifyKey: “/path/to/your/key”
For more details, please see the Mender configuration documentation.
Depending on your image generation process, there are several ways to provision the key to a device. For a golden image-based approach, the key could be copied to /etc/mender/, the configuration file adjusted, and then, the image regenerated. For a build-time image generation process, such as Yocto, you need to package the key and include it in the build process. For Yocto specifically, you can use the MENDER_ARITFACT_VERIFY_KEY
variable.
Signing process
Operations on Mender Artifacts are facilitated by the mender-artifact tool. The relevant commands for signing operations are
- write: creates an artifact, optionally signing it
- sign: signs an existing artifact
- validate: checks integrity of artifact and signature
- read: prints information about an artifact
Obtaining the SignServer-enabled mender-artifact
tool
Support for SignServer has been added to the mender-artifact
main implementation (GitHub).
Creating and signing an artifact using SignServer
mender-artifact directly interacts with the SignServer instance to sign the artifact. The required information needs to be provided as environment variables. These need to be set:
SIGNSERVER_CA_CERT_PATH
: path to root CA certificate for the SignServer instanceSIGNSERVER_CLIENT_CERT_PATH
: path to client certificate for the SignServer instanceSIGNSERVER_HOSTNAME
: hostname or IP address of the SignServer instance
Example:
export SIGNSERVER_CA_CERT_PATH=$(pwd)/MenderSignServer00RootCA.cer
export SIGNSERVER_CLIENT_CERT_PATH=$(pwd)/MenderSignServer00.pem
export SIGNSERVER_HOSTNAME=xx.xx.xx.xx
For verifying the resulting signature on a Mender artifact, the public key that corresponds to the client certificate is required. This public key ultimately also needs to be available on the device.
In this tutorial, we will show the operations on the demo image, which is included in the mender-artifact repository first to outline the concept.
Option 1: Create and sign in one operation
This flow is the recommended approach for creating signed artifacts for ease of use.
./mender-artifact write rootfs-image -t beaglebone -n release-1 -f cli/mender_test.img -o artifact.mender --keyfactor-signserver-worker YOUR_WORKER
Inspect the artifact:
./mender-artifact read artifact.mender
```
Mender artifact:
Name: release-1
Format: mender
Version: 3
Signature: signed but no key for verification provided; please use `-k` option for providing verification key
```
Validate the signature:
./mender-artifact validate artifact.mender --keyfactor-signserver-worker YOUR_WORKER
Option 2: Create and sign in two steps
Some build setups require that the artifact is signed by an instance that does not create it, or the artifact creation process is designed to be disconnected from the network for reproducibility reasons. In such a workflow, you can use mender-artifact to sign an existing artifact.
./mender-artifact write rootfs-image -t beaglebone -n release-1 -f cli/mender_test.img -o artifact.mender
./mender-artifact sign -o artifact-signed.mender --keyfactor-signserver-worker YOUR_WORKER artifact.mender
Now you can inspect the artifact-signed.mender file just as in Option 1.
Deploying the artifact
An artifact can be deployed through either the Mender management backend or locally, invoking the Mender Client directly with mender install.
In either case, if the artifact is signed as described above, the signature will be locally checked on the device using the configured verification keys without relying on a network connection. If the verification fails, the artifact is rejected, therefore assuring that only genuine software is run on the device.
For further hardening of the device, the verification key can be placed in hardware assistant mechanisms such as a TPM. The Mender client can use PKCS#11-compliant providers for this, making it impossible to extract the key from an eventually accessible root file system.