Secure Payloads ================ .. warning:: This page is still under development and not complete. It will be so until this warning is removed. Secure payloads offer the ability to provision encrypted data to an enrolled node. This encrypted data can be used to deliver secrets needed by the node such as keys, passwords, certificate roots of trust, etc. Secure payloads are for anything which requires strong confidentiality and integrity to bootstrap your system. The payload itself is encrypted and sent via the Keylime Tenant CLI (or rest API) to the Keylime Agent. The Agent also sends part of the key needed to decrypt the payload, a key share, called the `u_key` or user key. Only when the Agent has passed its enrolment criteria (including any `tpm_policy` or IMA allowlist), will the other key share of the decryption key, called the `v_key` or verification key, be passed to the Agent by the Keylime Verifier to decrypt the payload. .. note:: An alternative to secure payloads is to deliver the encrypted data to the node through some other mechanism like `cloud-init` or pre-embedded in a disk image. The Keylime protocol described above will still run to derive the decryption key for this data, but the data itself will never been seen or transported by Keylime. This guide does not discuss this method. Keylime offers two modes for sending secure payloads: single file encryption and certificate package mode. In the following sections we describe each. If you're interested in using the more advanced certificate package mode, we recommend you also read the Single File Encryption section as it contains configuration options and other information that both modes share. Single File Encryption ---------------------- In this mode, a file you specify to the `keylime_tenant` application with the `-f` option will be encrypted by the Tenant using the bootstrap key and securely delivered to the Agent. Once the Keylime protocol with the Tenant and Verifier has completed, the Keylime Agent will decrypt this file and place it in `/var/lib/keylime/secure/decrypted_payload` This is the default file name, but you can adjust the name of this file using the `dec_payload_file` option in `agent.conf`. You can also optionally specify a zip file as the file to be securely delivered. If the `extract_payload_zip` option in `agent.conf` is set (which it is by default), then Keylime will automatically extract the zip file to `/var/lib/keylime/secure/unzipped`. Finally, Keylime can also execute a script contained in the zip file once it has been unzipped. You can think of this as a very simple form of `cloud-init `_. By default this script is called `autorun.sh`. You can override this default with a different script name by adjusting the `payload_script` option in `agent.conf`. Note also that this script must be contained in the encrypted zip file, from which it will be extraced and then placed in `/var/lib/keylime/secure/unzipped`. Because the keys that Keylime uses to decrypt the data and the decrypted data itself are very sensitive, Keylime will only write those files to the memory-backed (and therefore non-persistent) `/var/lib/keylime/secure` directory. This is a bind-mounted tmpfs partition. As such, depending on how large your payload is, you may need to increase the size of this mounted partition by adjusting the `secure_size` option in `agent.conf`. This simple mode of operation is suitable for many situations where the secrets and other bootstrapping information are basic. However, there are several features that Keylime supports like revocation and certificate management that do not work in this mode. For those, you'll need the next mode: Certificate Package Mode. Certificate Package Mode ------------------------ This mode of Keylime automates many common actions that tenants will want to take when provisioning their Agents. First, Keylime can create an X509 certificate authority (CA) using `keylime_ca -d listen` and then issue certificates and the corresponding private keys to each provisioned node. This CA lives on the same host where the tenant issues the `keylime_ca` command and can be used to bootstrap many other security solutions like mutual TLS or SSH. To use this mode, pass the `--cert` option and a directory where the CA is located as the parameter to this option. Keylime will then create a certificate for the node (with the common name set to the Agent's UUID) and then create a zip file containing the newly generated X509 certificates, trust roots, and private keys. It then uses the same process for single file encryption as described above to securely deliver all the keys to the Agent. Optionally, the user can specify with the `--include` option a directory of additional files to be put into the certification package zip and securely delivered to the Agent. This mode of operation also natively supports certificate revocation. If the Keylime Verifier detects an Agent that no longer satisfies its integrity policy (e.g., it booted an unauthorized kernel or ran an unauthorized binary not on the IMA allowlist), it will create a signed revocation notification. These revocation notifications are signed by a special certificate/private key called the RevocationNotifier. Keylime will automatically create this certificate and pass it to the verifier when you add a new Agent to the verifier. Keylime will also include the public certificate for this key in the zip it sends to the Agent. This way Agents can validate the revocation notifications they receive from the verifier. By default all Keylime Agents listen for these revocation notifications (see the `listen_notifications` option in `agent.conf`). Using the keys in the unzipped certificate package, Agents can check that the revocations are valid. Keylime Agents can also take actions in response to a valid revocation. You can configure these actions by putting additional files into the delivered zip file using `--include`. Revocation actions are small Python scripts that will run on an Agent when a valid revocation is received. They should contain an `execute` function that takes one argument. This argument is a Python dictionary of metadata that can be used to tailor what the revocation action does. In the cert package mode, Keylime will specify the certificate serial number and common name (aka UUID) of the node that has failed its integrity check inside this metadata passed to the revocation action. For example, you can use this info to revoke the the offending X509 certificate. One subtlety to revocation actions is that they are not intended for the Agent that has been revoked. If an Agent has failed its integrity check, then we really can't trust that it won't ignore the revocations and do arbitrarily malicious things. So, revocation actions are for other well-behaving Agents in the system to take action against the revoked Agent. For example, by revoking its certificate as described above or firewalling it from the network, etc. There are some conventions to specifying revocation actions. As described above, their names must start with `local_action` to be executed. They also must be listed (without `.py` extensions) in a comma separated list in a file called `action_list` in the zip file. For example to run `local_action_a.py` and `local_action_b.py` the `action_list` file should contain `local_action_a,local_action_b`. So far we've described all the details of this in fine detail, but much of this automation will happen by default. Certificate Package Example --------------------------- Let's put all of the above together with an example. For the following example, we will provision some SSH keys onto the Agent. 1. Create a directory to host the files and `autorun.sh` script. For this example, we will use the directory `payload` 2. Create an `autorun.sh` script in the `payload` directory: .. sourcecode:: bash #!/bin/bash # this will make it easier for us to find our own cert ln -s `ls *-cert.crt | grep -v Revocation` mycert.crt mkdir -p /root/.ssh/ cp payload_id_rsa* /root/.ssh/ chmod 600 /root/.ssh/payload_id_rsa* 3. Copy the files you wish to provision into the `payload` directory. .. sourcecode:: console $ ls payload/ autorun.sh payload_id_rsa.pub payload_id_rsa Send the files using the Keylime Tenant tool: .. sourcecode:: console keylime_tenant -t --cert myca --include payload Recall that the `--cert` option tells Keylime to create a certificate authority at the default location `/var/lib/keylime/ca` and give this machine an X509 identity with its UUID. Keylime will also create a revocation notifier certificate for this CA and make it available to the verifier. Finally, the `--include` option tells Keylime to securely deliver the files in the specified directory (`payload` in our case) along with the X509 certs to the targeted Agent machine. If the enrolment was been successful, you will be able to see the contents of the `payload` directory in `/var/lib/keylime/secure/unzipped` along with the certs and included files. You should also see the SSH keys we included made in `/root/.ssh` directory from where the autorun.sh script was ran. Now, let's extend this example with revocation. In this example, we're going to execute a simple revocation action on the node that was revoked. It is also possible to configure scripts for execution should a node fail any given criteria (IMA measurements, for example). To configure this, we will use our `payload` directory again. First create a Python script with the preface of `local_action` For example `local_action_rm_ssh.py` Within this script create an `execute` function: .. sourcecode:: python import os import json import keylime.ca_util as ca_util import keylime.secure_mount as secure_mount async def execute(event): if event['type'] != 'revocation': return json_meta = json.loads(event['meta_data']) serial = json_meta['cert_serial'] # load up my own cert secdir = secure_mount.mount() mycert = ca_util.load_cert_by_path(f'{secdir}/unzipped/mycert.crt') # is this revocation meant for me? if serial == mycert.serial_number: os.remove("/root/.ssh/payload_id_rsa") os.remove("/root/.ssh/payload_id_rsa.pub") Next, in the `payload` directory create the `action_list` file containing `local_action_rm_ssh` (remember not to put the `.py` extension). .. warning:: In the above example, the node that fails its integrity check is the same one that we're expecting to run the revocation action to delete the key. Since the node is potentially compromised, we really can't expect that it will actually do this and not just ignore the revocation. A more realistic scenario for SSH keys is to provision one node with the SSH key generated as above, then provision a second server and add `payload_id_rsa.pub` to `.ssh/authorized_keys` using an autorun script. At this point, you can SSH from the first server to the second one. Should the first machine fail its integrity, then an revocation action on the second server can remove the compromised first machine from its list of Secure machines in `.ssh/authorized_keys` Many actions can be executed based on CA revocation. For more details and examples, please refer to the :doc:`/user_guide/revocation` page.