3.2. Podman¶
This guide describes how to deploy a pair of QKDLite nodes as rootless
Podman containers, using the QKDLite Podman installer. Each node runs its
components as containers inside a single Podman pod.
For devices with no route to the QKDLite registry, see Delivering QKDLite in a air-gapped environment (Podman).
3.2.2. Prerequisites¶
Hosts
Two Linux hosts, one per QKDLite node, each able to run rootless Podman.
Validated on Rocky Linux 10.2 and on Ubuntu 24.04 LTS
Network reachability between the two hosts on TCP port
2222, used for key replication between nodesNote
Key replication uses a quantum-safe SSH connection between the two nodes on port
2222. How that connection is routed, for example directly across a LAN or through an existing VPN, is a network design decision and is not configured by the installer.
Tooling
Podman, as packaged by the distributions above. Validated with Podman 5.8.2 on Rocky Linux 10.2, and with the Podman package shipped by Ubuntu 24.04 LTS
OpenSSL
Security material
For a QKD KME deployment: the Root CA certificate, client certificate and client key issued by the KME operator. The client key must be unencrypted, as the QKDLite node runs unattended
Note
The certificates for the node’s own quantum-safe TLS front end are generated by the installer, so they are not required in advance.
Artifacts and access
Credentials for the QKDLite registry (
qkdlite.azurecr.io)The QKDLite Podman installer package, containing
install_qkdlite_podman.sh,remove_qkdlite_podman.sh,qkdlite.conf, theplace-kme-certs-heredirectory and thepqtls-nginxtemplates
3.2.3. Installing the dependencies¶
Install Podman and OpenSSL on both hosts.
$ sudo apt update && sudo apt install -y podman openssl
$ export PODMAN_USERNS=keep-id
On Rocky Linux, install the same packages with dnf.
$ sudo dnf install -y podman openssl
$ export PODMAN_USERNS=keep-id
3.2.4. Quick Installation¶
The installer is run on the main node first, then on the remote node. Every secret must be identical on both nodes, so the main node generates them and the remote node reuses the same values.
The installer can be re-run at any time. It skips every step that is already complete, so it is also the way to resume an interrupted installation.
3.2.4.1. Main node¶
Edit
qkdlite.confand set the topology values for this node. The file ships with the main node’s values as defaults.POD_NAME=pod-a1 REMOTE_POD_NAME=pod-b1 LOCAL_SAE_NAME=SAE_A REMOTE_SAE_NAME=SAE_B HSM_SLOT=1234 UPSTREAM_PREFIX=ALICE REMOTE_POD_IP="<remote node's IP address>" LOCAL_IP="<this node's own IP address>"
Note
The Security Application Entity (SAE) name is required for the proper labelling of quantum keys to be provided via the ETSI server. In this example the main node uses
SAE_Aas its local name andSAE_Bas the remote name. The remote node uses the two values the other way round.Make the installer executable and run it.
$ chmod +x install_qkdlite_podman.sh remove_qkdlite_podman.sh $ ./install_qkdlite_podman.sh
State whether this device can reach the registry, then enter the registry credentials used to pull the QKDLite images. Answer
Nfor a normal installation.==> Step 1: Container images Is this an offline (airgapped) install, with the images already loaded? [y/N]: N Container registry to pull images from [qkdlite.azurecr.io]: Registry token name: user Registry token/password: Login Succeeded
Note
Answer
yonly on a device with no route to the registry, where the images have already been loaded. See Delivering QKDLite in a air-gapped environment (Podman).Choose whether to save a copy of the images for transfer into an air-gapped environment. Answer
Nunless you are preparing such a transfer.Do you need an offline copy of the images to transfer into an airgapped environment? [y/N]:Answering
ysaves every image to~/qkdlite-imagesas it is pulled, ready to transfer to the air-gapped device.Confirm the topology values. Any value already set in
qkdlite.confis used without prompting.==> Step 2: Topology configuration This device's pod name (e.g. pod-a1): Remote peer's pod name (e.g. pod-b1): This device's local SAE name (e.g. SAE_A): Remote peer's SAE name (e.g. SAE_B): HSM slot number (e.g. 1234): Remote peer's real IP (leave blank if same host): This device's own real IP (for the 1-way TLS proxy cert): Upstream prefix for 1-way TLS (ALICE or BOB):
Choose the source of the quantum keys. Answer
vto generate keys from the QRNG source, orqto request keys from a QKD KME. This question is asked on every run, so a node can be switched later.QRNG or real QKD KME mode? [v/q]:For
q, see QKD KME mode below.Enter the secrets. On the main node, press Enter at each prompt to generate a value.
==> Step 3: Secrets Type 'first' for the first device being set up (usually Alice), or 'second' to pair with an existing one [first/second]: first SO_PIN [press Enter to auto-generate, or type your own]: Generated SO_PIN: 12345678
The installer asks the same for
USER_PIN,TRANSPORT_KEY_DATA,BOOTSTRAP_KEY_DATA,SSH_PASSPHRASEandTLS_CERT_PASSPHRASE.Note
The Transport Key and the Bootstrap Key must be identical on both nodes, otherwise key replication fails. The installer writes every generated value to
qkdlite_secrets.txtnext to the pod directory, for use when installing the remote node. Keep this file private.The installer creates the pod, pulls the images and starts the containers. It then displays this node’s public key for key replication.
==> Step 6: SSH cross-registration This device's public SSH key, copy this to the remote device: ecdsa-sha2-nistp384 AAAA... Paste the remote device's public SSH key here (you can find it in its qkdlite_secrets.txt) Or press Enter to skip, if not available yet:
Press Enter to skip for now: the remote node does not exist yet.
3.2.4.2. Remote node¶
Edit
qkdlite.confon the remote node, swapping the local and remote values.POD_NAME=pod-b1 REMOTE_POD_NAME=pod-a1 LOCAL_SAE_NAME=SAE_B REMOTE_SAE_NAME=SAE_A HSM_SLOT=5678 UPSTREAM_PREFIX=BOB REMOTE_POD_IP="<main node's IP address>" LOCAL_IP="<this node's own IP address>"
Copy the six secrets from the main node’s
qkdlite_secrets.txt(SO_PIN,USER_PIN,TRANSPORT_KEY_DATA,BOOTSTRAP_KEY_DATA,SSH_PASSPHRASEandTLS_CERT_PASSPHRASE) into this node’sqkdlite.conf.Run the installer and answer
secondat the device role prompt. With the secrets already in place, the installer reuses them instead of generating new ones.$ ./install_qkdlite_podman.shWhen prompted, paste the main node’s public SSH key.
3.2.4.3. Completing key replication¶
Run the installer once more on the main node and paste the remote node’s public
SSH key, which is shown in its output and in its qkdlite_secrets.txt. Both
nodes can then authenticate to each other, and key replication begins.
$ ./install_qkdlite_podman.sh
3.2.5. QKD KME mode¶
To request keys from a QKD KME instead of the QRNG source, answer q at the
key source prompt. The installer then asks for the KME URL and the peer SAE
name registered on that KME, which differ per node.
QRNG or real QKD KME mode? [v/q]: q
QKD KME URL (e.g. https://kme1.example.com:443):
This KME's registered peer SAE name (e.g. SAE_PEER):
Place the CA.crt, client.crt and client.key files provided by the
KME operator into the place-kme-certs-here directory next to the installer,
using those exact file names. The installer copies them into the node’s
configuration. If the files are missing, the installer stops and reports where
to put them.
Note
client.key must be an unencrypted PEM file, as the QKDLite node runs
unattended and cannot supply a passphrase. An encrypted key can be converted
with openssl pkey -in <encrypted key> -out client.key.
3.2.6. Boot-time auto-start¶
Every container is created with --restart=always, and the installer enables
podman-restart.service for the current user. One step requires sudo and
is therefore not performed by the installer. Run it once on each node.
$ sudo loginctl enable-linger <username>
Without this, the user’s services stop when the user logs out, and the containers do not start after a reboot.
3.2.7. Verifying the Installation¶
Confirm that every container in the pod is running.
$ podman ps -a --pod --format "table {{.PodName}}\t{{.Names}}\t{{.Status}}"
All containers should report
Up, and the engine and key-sync containers should reporthealthy.Confirm that keys are being created and replicated.
$ podman logs <engine container name>
The log reports the number of keys held locally, and reports success for each key created locally and replicated to the remote node.
Confirm that both nodes hold the same key. Request a key on the main node, then retrieve the same key by its identifier on the remote node.
$ curl -s http://127.0.0.1:8000/api/v1/keys/SAE_B/enc_keys
$ curl -s "http://127.0.0.1:8000/api/v1/keys/SAE_A/dec_keys?key_ID=<key_ID from the main node>"
Both nodes return the same
keyvalue. This check uses the loopback address on the node itself. See REST APIs for the key delivery interface offered to external Security Application Entities (SAEs).
Note
The pod publishes the ETSI server on 127.0.0.1:8000, the key
replication service on port 2222, and the quantum-safe TLS front end on
port 8443.
3.2.8. Upgrading¶
Re-run the installer to apply changes made in qkdlite.conf. Steps that are
already complete are skipped, and containers that already exist are left in
place.
$ ./install_qkdlite_podman.sh
3.2.9. Removing QKDLite¶
To remove the containers, the pod, its data directory and the local images, run the removal script. It asks for confirmation first.
$ ./remove_qkdlite_podman.sh