====== 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 :doc:`installation_podman_airgap`. .. toctree:: :hidden: installation_podman_airgap 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 nodes .. note:: 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``, the ``place-kme-certs-here`` directory and the ``pqtls-nginx`` templates Installing the dependencies =========================== Install Podman and OpenSSL on both hosts. .. code-block:: console :caption: console on both QKDLite nodes $ sudo apt update && sudo apt install -y podman openssl $ export PODMAN_USERNS=keep-id On Rocky Linux, install the same packages with ``dnf``. .. code-block:: console :caption: console on both QKDLite nodes $ sudo dnf install -y podman openssl $ export PODMAN_USERNS=keep-id 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. Main node --------- #. Edit ``qkdlite.conf`` and set the topology values for this node. The file ships with the main node's values as defaults. .. code-block:: ini :caption: qkdlite.conf on QKDLite main node 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="" LOCAL_IP="" .. 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_A`` as its local name and ``SAE_B`` as the remote name. The remote node uses the two values the other way round. #. Make the installer executable and run it. .. code-block:: console :caption: console on QKDLite main node $ 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 ``N`` for a normal installation. .. code-block:: console :caption: console on QKDLite main node ==> 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 ``y`` only on a device with no route to the registry, where the images have already been loaded. See :doc:`installation_podman_airgap`. #. Choose whether to save a copy of the images for transfer into an air-gapped environment. Answer ``N`` unless you are preparing such a transfer. .. code-block:: console :caption: console on QKDLite main node Do you need an offline copy of the images to transfer into an airgapped environment? [y/N]: Answering ``y`` saves every image to ``~/qkdlite-images`` as it is pulled, ready to transfer to the air-gapped device. #. Confirm the topology values. Any value already set in ``qkdlite.conf`` is used without prompting. .. code-block:: console :caption: console on QKDLite main node ==> 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 ``v`` to generate keys from the QRNG source, or ``q`` to request keys from a QKD KME. This question is asked on every run, so a node can be switched later. .. code-block:: console :caption: console on QKDLite main node QRNG or real QKD KME mode? [v/q]: For ``q``, see :ref:`podman-qkd-kme-mode` below. #. Enter the secrets. On the main node, press Enter at each prompt to generate a value. .. code-block:: console :caption: console on QKDLite main node ==> 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_PASSPHRASE`` and ``TLS_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.txt`` next 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. .. code-block:: console :caption: console on QKDLite main node ==> 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. Remote node ----------- #. Edit ``qkdlite.conf`` on the remote node, swapping the local and remote values. .. code-block:: ini :caption: qkdlite.conf on QKDLite remote node 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="
" LOCAL_IP="" #. Copy the six secrets from the main node's ``qkdlite_secrets.txt`` (``SO_PIN``, ``USER_PIN``, ``TRANSPORT_KEY_DATA``, ``BOOTSTRAP_KEY_DATA``, ``SSH_PASSPHRASE`` and ``TLS_CERT_PASSPHRASE``) into this node's ``qkdlite.conf``. #. Run the installer and answer ``second`` at the device role prompt. With the secrets already in place, the installer reuses them instead of generating new ones. .. code-block:: console :caption: console on QKDLite remote node $ ./install_qkdlite_podman.sh #. When prompted, paste the main node's public SSH key. 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. .. code-block:: console :caption: console on QKDLite main node $ ./install_qkdlite_podman.sh .. _podman-qkd-kme-mode: 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. .. code-block:: console :caption: console on QKDLite 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 -out client.key``. 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. .. code-block:: console :caption: console on both QKDLite nodes $ sudo loginctl enable-linger Without this, the user's services stop when the user logs out, and the containers do not start after a reboot. Verifying the Installation ========================== #. Confirm that every container in the pod is running. .. code-block:: console :caption: console on both QKDLite nodes $ podman ps -a --pod --format "table {{.PodName}}\t{{.Names}}\t{{.Status}}" All containers should report ``Up``, and the engine and key-sync containers should report ``healthy``. #. Confirm that keys are being created and replicated. .. code-block:: console :caption: console on QKDLite main node $ podman logs 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. .. code-block:: console :caption: console on QKDLite main node $ curl -s http://127.0.0.1:8000/api/v1/keys/SAE_B/enc_keys .. code-block:: console :caption: console on QKDLite remote node $ curl -s "http://127.0.0.1:8000/api/v1/keys/SAE_A/dec_keys?key_ID=" Both nodes return the same ``key`` value. This check uses the loopback address on the node itself. See :doc:`rest_api` 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``. 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. .. code-block:: console :caption: console on QKDLite node $ ./install_qkdlite_podman.sh Removing QKDLite ================ To remove the containers, the pod, its data directory and the local images, run the removal script. It asks for confirmation first. .. code-block:: console :caption: console on QKDLite node $ ./remove_qkdlite_podman.sh