Introduction
This code is used to produce the files needed to update citadel. The citadel-fetch command is installed in citadel and may be run manually to check for updates.
N.B. This update framework is at the moment not production ready. It defends against arbitrary installation and rollback attacks. It partially defends against endless data attacks. It does not defend against fast-forward or indefinite freeze attacks. This system is vulnerable to key compromise and does not provide a method to update the root key through the update mechanism. Further work needs to be done to prevent these issues. Consider using libraries from the Update Framework (TUF).
Usage
- Clone this repo at the top level of the citadel directory for the automagic create-signed-file subcommand to work.
- Run
cargo run generate-update-keysto generate an update keypair. This keypair is used to sign the version manifest file (version.cbor). - Replace the existing public key file by moving update_server_key.pub to meta-citadel/recipes-citadel/citadel-config/files/citadel-fetch/update_server_key.pub .
- Set a channel name at: meta-citadel/conf/distro/citadel-distro.conf . If you use the dev channel, you may skip the generation of update keys as the keys are hard-coded in the citadel-tools code.
- Run
cargo run generate-image-keysto generate the image signing keypair for the channel defined inmeta-citadel/conf/distro/citadel-distro.conf. This command will place the public key (<channel>.pub) and private key (<channel>.priv) inmeta-citadel/recipes-citadel/citadel-keys/files/. - Build the OS.
- Run
cargo run create-signed-file. A version.cbor file will be generated. - Finally, the files generated (version.cbor and images) must be uploaded/ Run
cargo run upload-to-serverto automatically upload all the files to the server.
To perform the upload manually, put this version.cbor file to the server at: {UPDATE_SERVER_NAME}/{CLIENT}/{CHANNEL}/. For example: https://update.subgraph.com/public/dev/ . Upload the image files to {UPDATE_SERVER_NAME}/{CLIENT}/{CHANNEL}/. For example: https://update.subgraph.com/public/dev/rootfs_1.0.0.img
More information is available in the help menu.
Basic structure
We host the files used by this program as a file server. The current hostname is update.subgraph.com.
An installed citadel server will automatically check for updates by reading the file at https://update.subgraph.com/{CLIENT}/{CHANNEL}/version.cbor
{CLIENT} will default to "public" but is read from the currently running system.
{CHANNEL} will default to "stable" but is read from the currently running system.
The version.cbor file is signed with Subgraph-controlled keys or equivalent client-controlled keys. The corresponding public key will be embedded in the rootfs image during build. The public key is called update_server_key.pub and is stored in /etc/citadel/ .
Image Signing Keys
To ensure the integrity and authenticity of OS images during the boot process, each image is signed with a channel-specific key pair.
- Generating Keys: Use the
cargo run generate-image-keyscommand to generate a new key pair for the channel specified inmeta-citadel/conf/distro/citadel-distro.conf(e.g.,CITADEL_CHANNEL = "test"). - Key Locations:
- The public key (
<channel>.pub) is placed inmeta-citadel/recipes-citadel/citadel-keys/files/and is embedded into the OS image during the build. This public key is used bycitadel-booton the target system to verify the image header's signature. - The private key (
<channel>.priv) is also placed inmeta-citadel/recipes-citadel/citadel-keys/files/. It is crucial to keep this private key secret and local to your build environment. It is used by thecitadel-mkimagetool during the image creation process to sign the image header.
- The public key (
- Verification: During boot,
citadel-bootreads the embedded public key and verifies the signature of the image header. If the signature is invalid, the boot process will fail.
Update File Structure
version.cbor file
The version.cbor file is the only file read during update and contains all information required for the user's system to decide to update or not as well as where the update files are located on the server. This file is merely a container which provides a cryptographic guarantee that the serialized_citadel_version struct (which contains the actual information we need to disseminate) is authentic.
- serialized_citadel_version (CitadelVersionStruct): the serialized data containing the actionable information we need to read to make decisions on the update
- signature: the ed25519 signature of the above serialized data
- signatory: the name of the org or person who produced the version.cbor file
CitadelVersionStruct
- client: the Subgraph client making the request
- channel: whether this is a production release or other
- component_version (Vec): a vector containing structures called ComponentVersion which contain the information on each component's version number and the location of the download file of the component
- publisher: the name of the org or person who released this update
ComponentVersion
- component: the name of the image we may update. Either the rootfs, the kernel or the extra image
- version: a string which contains the semver describing the version of the component
- file_path: where on the update server can the file be downloaded from. This is relative to the domain name we are currently fetching from
Channel list
Finally, we place a channel list in the directory of every client directory called channels.cbor. Create a json file with structure:
{ "channels": ["stable", "dev", "beta"] }
You can run the json-to-cbor function subcommand in the update-generator tool to create the cbor file.