Keylime Documentation¶
Warning
This documentation is still under development and not complete. It will be so until this warning is removed.
Welcome to the Keylime Documentation site!
Keylime is a TPM-based highly scalable remote boot attestation and runtime integrity measurement solution. Keylime enables cloud users to monitor remote nodes using a hardware based cryptographic root of trust.
Keylime was originally born out of the security research team in MIT’s Lincoln Laboratory and is now developed and maintained by the Keylime community.
This Documentation site contains guides to install, use and administer keylime as well as guides to enable developers to make contributions to keylime or develop services against Keylime’s Rest API(s).
Installation¶
There are three current methods for installing Keylime: the Ansible role, the Keylime installer or a manual installation.
Ansible Keylime Roles¶
An Ansible role to deploy Keylime , alongside the Keylime Rust agent
This role deploys Keylime for use with a Hardware TPM.
Should you wish to deploy Keylime with a software TPM emulator for development or getting your feet wet, use the Ansible Keylime Soft TPM role instead.
Usage¶
Download or clone Ansible Keylime from its repository and follow the usage section.
Run the example playbook against your target remote host(s):
ansible-playbook -i your_hosts playbook.yml
TPM Version Control (Software TPM)¶
Ansible Keylime Soft TPM provides a role type for 2.0 TPM versions.
TPM 2.0 support can be configured by simply adding the role in the playbook.yml file here
For TPM 2.0 use:
- ansible-keylime-tpm20
This rule uses the TPM 2.0 Emulator (IBM software TPM).
Vagrant¶
If you prefer, a Vagrantfile is available for provisioning.
Clone the repository and then simply run:
vagrant up --provider <provider> --provision
For example, using libvirt:
vagrant up --provider libvirt --provision
For example, using VirtualBox:
vagrant up --provider virtualbox --provision
Once the VM is started, vagrant ssh into the VM and run sudo su - to become root.
You can then start the various components using commands:
keylime_verifier
keylime_registrar
keylime_agent
Rust agent¶
Note
The Rust agent is the official agent for Keylime and replaces the Python implementation. For the rust agent a different configuration file is used (by default /etc/keylime/agent.conf) which is not interchangeable with the old Python configuration.
Installation instructions can be found in the README.md for the Rust agent.
Keylime Bash installer¶
Keylime requires Python 3.6 or greater.
Installation can be performed via an automated shell script, installer.sh. The following command line options are available:
Usage: ./installer.sh [option...]
Options:
-k Download Keylime (stub installer mode)
-m Use modern TPM 2.0 libraries; this is the default
-s Install & use a Software TPM emulator (development only)
-p PATH Use PATH as Keylime path
-h This help info
Docker - Deployment¶
The verifier, registrar and tenant can also be deployed using Docker images. Keylime’s official images can be found here. Those are automatically generated for every commit and release.
For building those images locally see here.
Docker - Development¶
Python Keylime and with a TPM emulator can also be deployed using Docker. Since this docker configuration uses a TPM emulator, it should only be used for development or testing and NOT in production.
Please see either the Dockerfiles here or our local CI script here which will automate the build and pull of Keylime.
Manual¶
Keylime requires Python 3.6 or greater.
Python-based prerequisites¶
The following Python packages are required:
cryptography>=3.3.2
tornado>=5.0.2
m2crypto>=0.21.1
pyzmq>=14.4
pyyaml>=3.11
simplejson>=3.8
requests>=2.6
sqlalchemy>=1.3
alembic>=1.1.0
python-gnupg>=0.4.6
packaging>=16.0
psutil>=5.4.2
lark>=1.0.0
pyasn1>=0.4.2
pyasn1-modules>=0.2.1
jinja2>=3.0.0
The current list of required packages can be found here.
All of them should be available as distro packages. See installer.sh for more information if you want to install them this way. You can also let Keylime’s setup.py install them via PyPI.
TPM 2.0 Support¶
Keylime uses the Intel TPM2 software set to provide TPM 2.0 support. You will need to install the tpm2-tss software stack (available here) and tpm2-tools utilities available here. See README.md in these projects for detailed instructions on how to build and install.
The brief synopsis of a quick build/install (after installing dependencies) is:
# tpm2-tss
git clone https://github.com/tpm2-software/tpm2-tss.git tpm2-tss
pushd tpm2-tss
./bootstrap
./configure --prefix=/usr
make
sudo make install
popd
# tpm2-tools
git clone https://github.com/tpm2-software/tpm2-tools.git tpm2-tools
pushd tpm2-tools
./bootstrap
./configure --prefix=/usr/local
make
sudo make install
To ensure that you have the recent version installed ensure that you have the tpm2_checkquote utility in your path.
Note
Keylime by default (all versions after 6.2.0) uses the kernel TPM resource manager. For kernel versions older than 4.12 we recommend to use the tpm2-abrmd resource manager (available here).
How the TPM is accessed by tpm2-tools can be set using the TPM2TOOLS_TCTI environment variable. More information about that can be found here.
Talk to the swtpm emulator directly:
export TPM2TOOLS_TCTI="mssim:port=2321"
To talk to the TPM directly (not recommended):
export TPM2TOOLS_TCTI="device:/dev/tpm0"
Install Keylime¶
You’re finally ready to install Keylime:
sudo python setup.py install
Database support¶
Keylime supports the following databases:
SQLite
PostgreSQL
MySQL
MariaDB
SQLite is configured as default (database_url = sqlite) where the databases are stored under /var/lib/keylime.
Starting with Keylime version 6.4.0 only supports SQLAlchemy’s URL format to allow a more flexible configuration. The format for the supported databases can be found in the SQLAlchemy engine configuration documentation.
User Guide¶
User Selected PCR Monitoring¶
Warning
This page is still under development and not complete. It will be so until this warning is removed.
Using use the tpm_policy feature in Keylime, it is possible to monitor a remote machine for any given PCR.
This can be used for Trusted Boot checks for both the rhboot shim loader and Trusted Grub 2.
Note
On larger deployments the PCR values might be insufficient. In this case use the UEFI event log for measured boot: Use Measured Boot.
How to use¶
Select which PCRs you would like Keylime to measure, by using the tpm2_pcrread from the tpm2-tools tool.
Now you can set the PCR values as a JSON data structure in either the keylime.conf file:
tpm_policy = {"22":["0000000000000000000000000000000000000001","0000000000000000000000000000000000000000000000000000000000000001","000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","ffffffffffffffffffffffffffffffffffffffff","ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"],"15":["0000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"]}
Or you can add a node to using keylime_tenant:
keylime_tenant -v 127.0.0.1 -t 127.0.0.1 -f /root/excludes.txt \
--uuid D432FBB3-D2F1-4A97-9EF7-75BD81C00000 \
--allowlist /root/allowlist.txt \
--exclude /root/exclude.txt \
--tpm_policy {"22":["0000000000000000000000000000000000000001","0000000000000000000000000000000000000000000000000000000000000001","000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","ffffffffffffffffffffffffffffffffffffffff","ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"],"15":["0000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"]} \
-c add
rhboot shim-loader¶
The following is sourced from the rhboot shim repository please visit the upstream README to ensure information is still accurate
The following PCRs are extended by shim:
- PCR4:
the Authenticode hash of the binary being loaded will be extended into PCR4 before SB verification.
the hash of any binary for which Verify is called through the shim_lock protocol
- PCR7:
Any certificate in one of our certificate databases that matches a binary we try to load will be extended into PCR7. That includes:
DBX - the system denylist, logged as “dbx”
MokListX - the Mok denylist, logged as “MokListX”
vendor_dbx - shim’s built-in vendor denylist, logged as “dbx”
DB - the system allowlist, logged as “db”
MokList the Mok allowlist, logged as “MokList”
vendor_cert - shim’s built-in vendor allowlist, logged as “Shim”
shim_cert - shim’s build-time generated allowlist, logged as “Shim”
MokSBState will be extended into PCR7 if it is set, logged as “MokSBState”.
- PCR8:
If you’re using the grub2 TPM patchset we cary in Fedora, the kernel command line and all grub commands (including all of grub.cfg that gets run) are measured into PCR8.
- PCR9:
If you’re using the grub2 TPM patchset we cary in Fedora, the kernel, initramfs, and any multiboot modules loaded are measured into PCR9.
- PCR14:
MokList, MokListX, and MokSBState will be extended into PCR14 if they are set.
Use Measured Boot¶
Warning
This page is still under development and not complete. It will be so until this warning is removed.
Introduction¶
In any real-world large-scale production environment, a large number of different types of nodes will typically be found. The TPM 2.0 defines a specific meaning - measurement of UEFI bios, measurement of boot device firmware - for each of the lower-numbered PCRs (e.g., PCRs 0-9), as these are extended during the multiple events of a measured boot log. However, simply comparing the contents of these PCRs against a well-known “golden value” becomes unfeasible. The reason for this is, in addition to the potentially hundreds of variations due to node type, it can be experimentally demonstrated that some PCRs (e.g, PCR 1) vary for each physical machine, if such machine is netbooted (as it encodes the MAC address of the NIC used during boot.)
Fortunately, the UEFI firmware is now exposing the event log through an ACPI table and a “recent enough” Linux kernel (e.g., 5.4 or later) is now consuming this table and exposing this boot event log through the securityfs, typically at the path /sys/kernel/security/tpm0/binary_bios_measurements. When combined with secure boot and a “recent enough” version of grub (2.06 or later), the aforementioned PCR set can be fully populated, including measurements of all components, up to the kernel and initrd.
In addition to these sources of (boot log) data, a “recent enough” version of tpm2-tools (5.0 or later) can be used to consume the contents of such logs and thus rebuild the contents of PCRs [0-9] (and potentially PCRs [11-14]).
Implementation¶
Keylime can make use of this new capability in a very flexible manner. A “measured boot reference state” or mb_refstate for short can be specified by the keylime operator (i.e. the tenant). This operator-provided piece of information is used, in a fashion similar to the “IMA policy” (previously known as “allowlist”), by the keylime_verifier, to compare the contents of the information shipped from the keylime_agent (boot log in one case, IMA log on the other), against such reference state.
Due to the fact that physical node-specific information can be encoded on the “measured boot log”, it became necessary to specify (optionally) a second piece of information, a “measured boot policy” or mb_policy . This information is used to instruct the keylime_verifier on how to do the comparison (e.g., using a regular expression, rather than a simple equality match). The policy name is specified in keylime.conf, under the [cloud_verifier] section of the file, with parameter named measured_boot_policy_name. The default value for it is accept-all, meaning “just don’t try to match the contents, just replay the log and make sure the values of PCRs [0-9] and [11-14] match”.
Whenever a “measured boot reference state” is defined - via a new command-line option in keylime_tenant - –mb_refstate, the following actions will be taken.
PCRs [0-9] and [11-14] will be included in the quote sent by keylime_agent
2) The keylime_agent will also send the contents of /sys/kernel/security/tpm0/binary_bios_measurements
3) The keylime_verifier will replay the boot log from step 2, ensuring the correct values for PCRs collected in step 1. Again, this is very similar to what it is done with “IMA logs” and PCR 10.
4) The very same keylime_verifier will take the boot log, now deemed “attested” and compare it against the “measured boot reference state”, according to the “measured boot policy”, causing the attestation to fail if it does not conform.
How to use¶
The simplest way to use this new functionality is by providing an empty “measured boot reference state” and an accept-all “measured boot policy”, which will cause the keylime_verifier to simply skip the aforementioned step 4.
An example follows:
echo "{}" > measured_boot_reference_state.txt
keylime_tenant -c add -t <AGENT IP> -v <VERIFIER IP> -u <AGENT UUID> --mb_refstate ./measured_boot_reference_state.txt
Note: please keep in mind that the IMA-specific options can be combined with the above options in the example, resulting in a configuration where a keylime_agent sent a quote with PCRs [0-15] and both logs (boot and IMA)
Evidently, to be fully used in a meaningful manner, keylime operators need to provide its own custom mb_refstate and mb_policy. While an user can write a policy that performs an “exact match” on a carefully constructed refstate, the key idea here is to create a pair of specification files which are at once meaningful (for the purposes of trusted computing attestation) and generic (enough to be applied to a set of nodes).
The most convenient way to crate an mb_refstate is starting from the contents of an UEFI boot log from a given node, and then tweak and customize it to make more generic. Keylime includes a tool (under scripts directory) - generate_mb_refstate - which will consume a boot log and output a JSON file containing an mb_refstate. An example follows:
keylime/scripts/create_mb_refstate /sys/kernel/security/tpm0/binary_bios_measurements measured_boot_reference_state.json
keylime_tenant -c add -t <AGENT IP> -v <VERIFIER IP> -u <AGENT UUID> --mb_refstate ./measured_boot_reference_state.json
This reference state can be (as in the example above) consumed “as is”, or it can be tweaked to be made more generic (or even more strict, if the keylime operator chooses so).
The mb_policy is defined within a framework specified in policies.py, where some “trivial” policies such as accept-all and reject-all are pre-defined. The Domain-Specific Language (DSL) used by the framework are defined in tests.py and an illustrative use of it can be seen in the policy example.py, all under the elchecking directory. This example policy was crafted to be meaningful (i.e., with a relevant number of parameters tests) and yet applicable to a large set of nodes. It consumes a mb_refstate such as the one generated by the aforementioned tool or the example_reference_state.json, located under the same directory.
Just to quickly exemplify what this policy does, it for instance tests if a node has SecureBoot enabled (tests.FieldTest(“Enabled”, tests.StringEqual(“Yes”))) and if a node has a well-formed kernel command line boot parameters (e.g., tests.FieldTest(“String”, tests.RegExp(r”.*/grub.*”))). The policy is well documented, and operators are encouraged to just read through the comments in order to understand how the tests are implemented.
While an operator can attempt to write its own policy from scratch, it is recommended that one just copies example.py into mypolicy.py, change it as required and then just points to this new policy on keylime.conf (measured_boot_policy_name) for its own use.
Run-time Integrity Monitoring¶
Keylimes run-time integrity monitoring requires the set up of Linux IMA.
You should refer to your Linux Distributions documentation to enable IMA, but as a general guide most recent versions already have CONFIG_IMA toggled to Y as a value during Kernel compile.
It is then just a case of deploying an ima-policy file. On a Fedora or Debian system, the file is situated in /etc/ima/ima-policy.
For configuration of your IMA policy, please refer to the IMA Documentation
Within Keylime we use the following for demonstration (found in demo/ima-policies/ima-policy-keylime):
# PROC_SUPER_MAGIC dont_measure fsmagic=0x9fa0 # SYSFS_MAGIC dont_measure fsmagic=0x62656572 # DEBUGFS_MAGIC dont_measure fsmagic=0x64626720 # TMPFS_MAGIC dont_measure fsmagic=0x01021994 # RAMFS_MAGIC dont_measure fsmagic=0x858458f6 # SECURITYFS_MAGIC dont_measure fsmagic=0x73636673 # SELINUX_MAGIC dont_measure fsmagic=0xf97cff8c # CGROUP_SUPER_MAGIC dont_measure fsmagic=0x27e0eb # OVERLAYFS_MAGIC # when containers are used we almost always want to ignore them dont_measure fsmagic=0x794c7630 # Don’t measure log, audit or tmp files dont_measure obj_type=var_log_t dont_measure obj_type=auditd_log_t dont_measure obj_type=tmp_t # MEASUREMENTS measure func=BPRM_CHECK measure func=FILE_MMAP mask=MAY_EXEC measure func=MODULE_CHECK uid=0
This default policy measures all executables in bprm_check and all files mmapped executable in file_mmap and module checks and skips several irrelevant files (logs, audit, tmp, etc).
Once your ima-policy is in place, reboot your machine (or even better have it present in your image for first boot).
You can then verify IMA is measuring your system:
# head -5 /sys/kernel/security/ima/ascii_runtime_measurements
PCR template-hash filedata-hash filename-hint
10 3c93cea361cd6892bc8b9e3458e22ce60ef2e632 ima-ng sha1:ac7dd11bf0e3bec9a7eb2c01e495072962fb9dfa boot_aggregate
10 3d1452eb1fcbe51ad137f3fc21d3cf4a7c2e625b ima-ng sha1:a212d835ca43d7deedd4ee806898e77eab53dafa /usr/lib/systemd/systemd
10 e213099a2bf6d88333446c5da617e327696f9eb4 ima-ng sha1:6da34b1b7d2ca0d5ca19e68119c262556a15171d /usr/lib64/ld-2.28.so
10 7efd8e2a3da367f2de74b26b84f20b37c692b9f9 ima-ng sha1:af78ea0b455f654e9237e2086971f367b6bebc5f /usr/lib/systemd/libsystemd-shared-239.so
10 784fbf69b54c99d4ae82c0be5fca365a8272414e ima-ng sha1:b0c601bf82d32ff9afa34bccbb7e8f052c48d64e /etc/ld.so.cache
Keylime IMA allowlists¶
An allowlist is a set of “golden” cryptographic hashes of a files un-tampered state or of keys that may be loaded onto keyrings.
The structure of the allowlist is a hash followed by a full POSIX path to the file:
ffe3ad4c395985d143bd0e45a9a1dd09aac21b91 /path/to/file
For a key that is expected to be loaded on a keyring with the name .ima an entry may look like this:
b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c %keyring:.ima
Keylime will load the allowlist into the Keylime Verifier. Keylime will then poll tpm quotes to PCR 10 on the agents TPM and validate the agents file(s) state against the allowlist. If the object has been tampered with or an unexpected key was loaded onto a keyring, the hashes will not match and Keylime will place the agent into a failed state. Likewise, if any files invoke the actions stated in ima-policy that are not matched in the allowlist, keylime will place the agent into a failed state.
Generate an allowlist¶
Keylime provides a script to generate allowlists from initramfs, but this is only a guide. We encourage developers / users of Keylime to be creative and come up with their own process for securely creating and maintaining an allowlist.
The create_allowlist.sh script is available here
Run the script as follows:
# create_allowlist.sh allowlist.txt [hash-algo]
With [hash-algo] being sha1sum, sha256sum (note, you need the OpenSSL app installed to have the shasum CLI applications available).
This will then result in allowlist.txt being available for Agent provisioning.
Warning
It’s best practice to create the allowlist in a secure environment. Ideally, this should be on a fully encrypted, air gapped computer that is permanently isolated from the Internet. Disable all network cards and sign the allowlist hash to ensure no tampering occurs when transferring to other machines.
Alongside building an allowlist from initramfs, you could also generate good hashes for your applications files or admin scripts that will run on the remotely attested machine.
Excludes List¶
An excludes list can be utilised to exclude any file or path. The excludes list uses the Python regular expression standard, where the syntax is similar to those found in Perl. Note that this syntax is different from POSIX basic regular expressions. For example the tmp directory can be ignored using:
/tmp/.*
Allowlist entries for keys¶
Allowlist entries for keys expected to be loaded onto keyrings can be generated by hashing the files of keys like this:
sha256sum /etc/keys/ima/rsakey-rsa.crt.der
As previously shown, the allowlist entry should be formed of the hash (sha256) and the prefix ‘%keyring:’ in front of the keyring the key will be loaded onto:
b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c %keyring:.ima
The following rule should be added to the IMA policy so that IMA reports keys loaded onto keyrings .ima and .evm (since Linux 5.6):
measure func=KEY_CHECK keyrings=.ima|.evm
IMA Keylime JSON format¶
The tenant parses the allow and exclude list into a JSON object that is then sent to the verifier. Depending of the use case the object can also be constructed manually instead of using the tenant.
{
"allowlist":{
"meta":{
"version":"ALLOWLIST_CURRENT_VERSION"
},
"release":"RELEASE_VERSION",
"hashes":{
"/file/path":[
"VALID_HASH1",
"VALID_HASH2"
]
},
"keyrings":{
"LINUX_KEYRING":[
"VALID_HASH3"
]
},
"ima":{
"ignored_keyrings":[
"IGNORED_KEYRING"
]
}
},
"exclude":[
"REGEX1, REGEX2"
]
}
ALLOWLIST_CURRENT_VERSION (integer): current version of the allow list format (latest is 2).
RELEASE_VERSION (integer): release version of this allowlist.
hashes: dictionary of the file path that should be validated as key and a list of valid hashes as entry.
VALID_HASHn: valid hash of the file or keyring that is measured
keyrings: dictionary of the keyring that should be used for signature validation and a list of valid hashes as entry.
LINUX_KEYRING: kernel keyring like .ima or .evm
ignored_keyrings: successful validated keyrings are used for signature validation. Add * to disable all or add them one by one.
exclude: list of regexes of files to exclude
REGEXn: regex for excluding certain files (e.g. /tmp/.*)
Remotely Provision Agents¶
Now that we have our allowlist available, we can send it to the verifier.
Note
If you’re using a TPM Emulator (for example with the ansible-keylime-tpm-emulator, you will also need to run the keylime ima emulator. To do this, open a terminal and run keylime_ima_emulator
Using the keylime_tenant we can send the allowlist and our excludes list as follows:
keylime_tenant -v <verifier-ip> -t <agent-ip> -f /path/excludes.txt --uuid D432FBB3-D2F1-4A97-9EF7-75BD81C00000 --allowlist /path/allowlist.txt --exclude /path/excludes.txt
Note
If your agent is already registered, you can use -c update
Should you prefer, you can set the values allowlist & ima_excludelist within /etc/keylime.conf, you can then use default as follows:
`keylime_tenant -v 127.0.0.1 -t neptune -f /root/excludes.txt --uuid D432FBB3-D2F1-4A97-9EF7-75BD81C00000 --allowlist default --exclude default`
How can I test this?¶
Create a script that does anything (for example echo “hello world”) that is not present in your allowlist or the excludes list. Run the script as root on the agent machine. You will then see the following output on the verifier showing the agent status change to failed:
keylime.tpm - INFO - Checking IMA measurement list...
keylime.ima - WARNING - File not found in allowlist: /root/evil_script.sh
keylime.ima - ERROR - IMA ERRORS: template-hash 0 fnf 1 hash 0 good 781
keylime.cloudverifier - WARNING - agent D432FBB3-D2F1-4A97-9EF7-75BD81C00000 failed, stopping polling
IMA File Signature Verification¶
Keylime supports the verification of IMA file signatures, which also helps to detect modifications on immutable files and can be used to complement or even replace the allowlist of hashes if all relevant executables and libraries are signed. However, the set up of a system that has all files signed is beyond the scope of this documentation.
In the following we will show how files can be signed and how a system with signed files must be registered. We assume that the system has already been set up for runtime-integrity monitoring following the above steps and that the system would not show any errors on the Keylime Verifier side. It should not be registered with the keylime verifier at this point. If it is, we now deregister it:
keylime_tenant -c delete -u D432FBB3-D2F1-4A97-9EF7-75BD81C00000
Our first step is to enable IMA Appraisal in Linux. Recent Fedora kernels for example have IMA Appraisal support built-in but not activated. To enable it, we need to add the following Linux kernel parameters to the Linux boot command line:
ima_appraise=fix ima_template=ima-sig ima_policy=tcb
For this we edit /etc/default/grub and append the above parameters to the GRUB_CMDLINE_LINUX line and then recreate the system’s grub configuration file with the following command:
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
IMA will be in IMA Appraisal fix-mode when the system is started up the next time. Fix-mode, unlike enforcement mode, does not require that all files be signed but will give us the benefit that the verifier receives all file signatures of signed executables.
For IMA Appraisal to append the file signatures to the IMA log, we need to append the following line to the above IMA policy:
appraise func=BPRM_CHECK fowner=0 appraise_type=imasig
We now create our IMA file signing key using the following commands:
openssl genrsa -out ima-filesigning.pem 2048
openssl rsa -in ima-filesigning.pem -pubout -out ima-pub.pem
Next, we determine the hash (sha1 or sha256) that IMA is using for file measurements by looking at the IMA measurement log and then use evmctl to sign a demo executable that we derive from the echo tool:
sudo dnf -y install ima-evm-utils
cp /bin/echo ./myecho
sudo evmctl ima_sign --key ima-filesigning.pem -a <hash> myecho
Note
It is important that we use the same hash for signing the file that IMA also uses for file measurements. In the case we use ‘sha1’ since the IMA measurement log further above shows sha1 filedata-hashes in the 4th column. On more recent systems we would likely use ‘sha256’.
Note
If the IMA measurement log contains invalid signatures, the system will have to be rebooted to start over with a clean log that the Keylime Verifier can successfully verify.
Invalid signatures may for example be in the log if executables were accidentally signed with the wrong hash, such as sha1 instead of sha256. In this case they all need to be re-signed to match the hash that IMA is using for file signatures.
Another reason for an invalid signature may be that a file was modified after it was signed. Any file modification will invalidate the signature. Similarly, a malformatted or altered security.ima extended attribute will lead to a signature verification failure.
Yet another reason may be that an unknown key was used for signing files. In this case the system should be re-registered with that additional key using the Keylime tenant tool.
To verify that the file has been properly signed, we can use the following command, which will show the security.ima extended attribute’s value:
getfattr -m ^security.ima --dump myecho
We now reboot the machine:
reboot
After the reboot the IMA measurement log should not have any measurement of the myecho tool. The following command should not return anything:
grep myecho /sys/kernel/security/ima/ascii_runtime_measurements
We now register the system and pass along the file signing key:
keylime_tenant -v 127.0.0.1 -t neptune -f /root/excludes.txt \
--uuid D432FBB3-D2F1-4A97-9EF7-75BD81C00000 --allowlist default --exclude default \
--sign_verification_key ima-pub.pem
We can now execute the myecho tool as root:
sudo ./myecho
At this point we should not see any errors on the verifier side and there should be one entry of ‘myecho’ in the IMA measurement log that contains a column after the file path containing the file signature:
grep myecho /sys/kernel/security/ima/ascii_runtime_measurements
To test that signature verification works, we can now invalidate the signature by appending a byte to the file and executing it again:
echo >> ./myecho
sudo ./myecho
We should now see two entries in the IMA measurement log. Each one should have a different measurement:
grep myecho /sys/kernel/security/ima/ascii_runtime_measurements
The verifier log should now indicating a bad file signature:
keylime.tpm - INFO - Checking IMA measurement list on agent: D432FBB3-D2F1-4A97-9EF7-75BD81C00000
keylime.ima - WARNING - signature for file /home/test/myecho is not valid
keylime.ima - ERROR - IMA ERRORS: template-hash 0 fnf 0 hash 0 bad-sig 1 good 3042
keylime.cloudverifier - WARNING - agent D432FBB3-D2F1-4A97-9EF7-75BD81C00000 failed, stopping polling
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 keylime.conf. You can also optionally specify a zip file as the file to be securely delivered. If the extract_payload_zip option in keylime.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 keylime.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 keylime.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 authorized 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 keylime.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.
Create a directory to host the files and autorun.sh script. For this example, we will use the directory payload
Create an autorun.sh script in the payload directory:
#!/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*
Copy the files you wish to provision into the payload directory.
$ ls payload/
autorun.sh
payload_id_rsa.pub
payload_id_rsa
Send the files using the Keylime Tenant tool:
keylime_tenant -t <agent-ip> --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:
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 Agent Revocation page.
Agent Revocation¶
Warning
This page is still under development and not complete. It will be so until this warning is removed.
Design of Keylime¶
Overview of Keylime¶
Keylime mainly consists of an agent, two server components (verifier and registrar) and a commandline tool the tenant.
Agent¶
The agent is a service that runs on the operating system that should be attested. It communicates with the TPM to enroll the AK and to generate quotes and collects the necessary data like the UEFI and IMA event logs to make state attestation possible.
The agent provides an interface to provision the device further once it was attested successfully for the first time using the secure payload mechanism. For more details see: Secure Payloads.
It is possible for the agent to listen to revocation events that are sent by the verifier if an agent attestation failed. This is useful for environments where attested systems directly communicate with each other and require that the other systems are trusted. In this case a revocation message might change local policies so that the compromised system cannot access any resources from other systems.
Registrar¶
The agent registers itself in the registrar. The registrar manages the agent enrollment process which includes getting an UUID for the agent, collecting the EKpub, EK certificate and AKpub from an agent and verifying that the AK belongs to the EK (using MakeCredential and ActivateCredential).
Once an agent has been registered in the registrar, it is ready to be enrolled for attestation. The tenant can use the EK certificate to verify to verify the trustworthiness of the TPM. Both the tenant and verifier
Note
If EK or AK are mentioned outside of internal TPM signing operations, it usually references the EKpub or AKpub because it should not be possible extract the private keys out of the TPM.
Note
The Keylime agent currently generates a AK on every startup and sends the EK and EK certificate. This is done to keep then design simple by not requiring a third party to verify the EK. The EK (and EK certificate) is required to verify the authenticity of the AK once and Keylime does not require a new AK but currently registration only with an AK is not enabled because the agent does not implement persisting the AK.
Verifier¶
The verifier implements the actual attestation of an agent and sends revocation messages if an agent leaves the trusted state.
Once an agent is registered for attestation (using the tenant or the API directly) the verifier continuously pulls the required attestation data from the agent. This can include: a quote over the PCRs, the PCR values, NK public key, IMA log and UEFI event log. After that the quote is validated additional validation of the data can be configured.
Static PCR values¶
The tpm_policy allows for simple checking of PCR values against a known good allowlist. In most cases this is only useful when the boot chain does not change often, there is a way to retrieve the values beforehand and the UEFI event log is unavailable. More information can be found in User Selected PCR Monitoring.
Measured Boot using the UEFI Event Log¶
On larger deployments it is not feasible to collect golden values for the PCR values used for measured boot. To make attestation still possible Keylime includes a policy engine for validating the UEFI event log. This is the preferred method because static PCR values are fragile because they change if something in the boot chain is updated (firmware, Shim, GRUB, Linux kernel, initrd, …). More information can be found in Use Measured Boot.
IMA validation¶
Keylime allows to verify files measured by IMA against either a provided allowlist or a signature. This makes it for example easy to attest all files that were executed by root. More information can be found in Run-time Integrity Monitoring.
Tenant¶
The tenant is a commandline management tool shipped by Keylime to manage agents. This includes adding or removing the agent from attestation, validation of the EK certificate against a cert store and getting the status of an agent. It also provides the necessary tools for the payload mechanism and revocation actions.
For all the features of the tenant see the output of keylime_tenant --help
.
Threat Model¶
Keylime was originally developed with the intention of using it in combination with hypervisors to protect the VMs against by using the vTPM support in Xen. vTPM support for TPM2.0 was never implemented into Keylime and swtpm+libvirt never supported it, so this model no longer fits. Keylime is commonly used either on bare metal hardware or in VMs where the TPM is emulated but from VM side treated the same as a hardware TPM. Therefore the common threat model is defined on the latter use case.
Note
The term vTPM can be confusing because it originally described the deep quote feature in Xen which Keylime used for TPM 1.2. Now it commonly refers to a software implementation of a TPM (e.g. swtpm) or the Virtual TPM Proxy Driver in the Linux kernel.
From Keylime’s perspective the core hardware like CPU, memory, motherboard is trusted, because it does not provide mechanisms to detect tampering with the hardware itself. Keylime chains its root of trust into the TPM therefore the TPM is deemed in general trustworthy. This trust is verified using the EK or EK certificate.
The goal of Keylime is to attest the state of a running system. For this to work the entire boot chain has to be verified. The UEFI with Secure Boot enabled firmware and CRTM are generally trusted because it provides the UEFI event log and the API for other EFI applications to use the TPM. All the other applications in the boot chain are either measured by the firmware or the application that loads them (e.g. GRUB2 loads the kernel). The threat model does not require to trust arbitrary EFI applications during the boot process because it can be verified after boot what was executed.
The threat model includes that an adversary has full control over the network and can either sent rouge messages, drop or modify them. Also the Keylime agent and running operating system itself is not deemed trustworthy by default. Only after the successful initial attestation the system is deemed trustworthy, but still can leave the trusted state at any moment and is therefore continuously attested.
Types of Attacks to detect¶
Keylime tries to detect the following attacks.
TPM Quote Manipulation¶
Because the TPM is the root-of-trust for Keylime, it ensures that the quote is valid. This is vital for all the other attestation steps because the quote is used to validate the data.
Keylime ensures this through three steps:
EK validation: The tenant allows Keylime to verify the EK certificate against the CAs of hardware manufacturers or add custom validation steps. This is done to ensure that the EK belongs to an actual hardware TPM or a trusted software TPM.
AK enrollment: Using the TPM commands MakeCredential, ActivateCredential and enforcing certain key properties (restricted, user with auth, sign encrypt, fixed TPM, fixed parent and sensitive data origin) Keylime ensures that the used AK belongs to the provided EK and has the right properties for signing quotes.
Quote validation: Each quote generated by the TPM is verified with the AK provided during agent registration. The verifier provides a fresh nonce that is included in the quote to prohibit replay attacks.
Modification of the boot process¶
Checking the security of the running system does only make sense if it can be ensured that the system was correctly booted. Therefore Keylime provides two ways to allow users to verify the entire boot chain up to the running system: static PCR value checks (User Selected PCR Monitoring) and the measured boot policy engine (Use Measured Boot).
Runtime file and system integrity¶
Keylime can attest the state of a Linux system and the files using the Linux Integrity Measurement Architecture (IMA). Therefore Keylime can be used to remotely check for attacks that IMA detects.
Rest API’s¶
All Keylime APIs use REST (Representational State Transfer).
Authentication¶
Most API interactions are secured using mTLS connections. There are up to three different CAs involved. (The revocation process also uses a CA, but this is a different to those CAs)
Verifier CA (CV CA)¶
This CA is used by the verifier to authenticate connections to it. The tenant requires a certificate from this CA to add/delete/list agents at the CV.
Registrar CA¶
This CA is used by the registrar to authenticate connections to it. The tenant and the verifier require certificate from this CA to get or delete agent information from the registrar. By default it is the same CA as the CV CA.
Note that the API endpoints from the registrar for the agent are unprotected by design.
Agent Keylime CA¶
The agent runs a HTTPS server and provides it’s certificate to the registrar (mtls_cert). All connections done to the agent are verified against the CA specified in the keylime_ca option. This ensures that only trusted systems can interact with the agents. By default the CV CA is used, but the CA certificate (cacert.crt) needs to be copied to the agents.
Registrar, tenant and verifier can configure this CA separately using the agent_mtls_* options.
RESTful API for Keylime (v2.1)¶
Keylime API is versioned. More information can be found here: https://github.com/keylime/enhancements/blob/master/45_api_versioning.md
Warning
API version 1.0 will no longer be officially supported starting with Keylime 6.4.0.
General responses¶
- ANY /¶
Generic fields in responses
- Response JSON Object:
code (int) – HTTP status code
status (string) – textual context of that status
results (object) – Holds the actual data.
Cloud verifier (CV)¶
- GET /v2.1/agents/{agent_id:UUID}¶
Get status of agent agent_id from CV
Example response:
{ "code": 200, "status": "Success", "results": { "operational_state": 7, "v": "yyNnlWwFRz1ZUzSe2YEpz9A5urtv6oywgttTF7VbBP4=", "ip": "127.0.0.1", "port": 9002, "tpm_policy": "{\"22\": [\"0000000000000000000000000000000000000001\", \"0000000000000000000000000000000000000000000000000000000000000001\", \"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001\", \"ffffffffffffffffffffffffffffffffffffffff\", \"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\", \"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"], \"15\": [\"0000000000000000000000000000000000000000\", \"0000000000000000000000000000000000000000000000000000000000000000\", \"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"], \"mask\": \"0x408000\"}", "vtpm_policy": "{\"23\": [\"ffffffffffffffffffffffffffffffffffffffff\", \"0000000000000000000000000000000000000000\"], \"15\": [\"0000000000000000000000000000000000000000\"], \"mask\": \"0x808000\"}", "meta_data": "{}", "allowlist_len": 0, "mb_refstate_len": 0, "accept_tpm_hash_algs": [ "sha512", "sha384", "sha256", "sha1" ], "accept_tpm_encryption_algs": [ "ecc", "rsa" ], "accept_tpm_signing_algs": [ "ecschnorr", "rsassa" ], "hash_alg": "sha256", "enc_alg": "rsa", "sign_alg": "rsassa", "verifier_id": "default", "verifier_ip": "127.0.0.1", "verifier_port": 8881, "severity_level": 6, "last_event_id": "qoute_validation.quote_validation" } }
- Response JSON Object:
code (int) – HTTP status code
status (string) – Status as string
operational_state (int) – Current state of the agent in the CV. Defined in https://github.com/keylime/keylime/blob/master/keylime/common/states.py
v (string) – V key for payload base64 encoded. Decoded length is 32 bytes
ip (string) – Agents contact ip address for the CV
port (string) – Agents contact port for the CV
tpm_policy (string) – Static PCR policy and mask for TPM
vtpm_policy (string) – Static PCR policy and mask for vTPM
meta_data (string) – Metadata about the agent. Normally contains certificate information if a CA is used.
allowlist_len (int) – Length of the allowlist.
mb_refstate_len (int) – Length of the measured boot reference state policy.
accept_tpm_hash_algs (list[string]) – Accepted TPM hashing algorithms. sha1 must be enabled for IMA validation to work.
accept_tpm_encryption_algs (list[string]) – Accepted TPM encryption algorithms.
accept_tpm_signing_algs (list[string]) – Accepted TPM signing algorithms.
hash_alg (string) – Used hashing algorithm.
enc_alg (string) – Used encryption algorithm.
sign_alg (string) – Used signing algorithm.
verifier_id (string) – Name of the verifier that is used. (Only important if multiple verifiers are used)
verifier_ip (string) – IP of the verifier that is used.
verifier_port (int) – Port of the verifier that is used.
severity_level (int) – Severity level of the agent. Might be null. Levels are the numeric representation of the severity labels.
last_event_id (string) – ID of the last revocation event. Might be null.
- POST /v2.1/agents/{agent_id:UUID}¶
Add new agent instance_id to CV.
Example request:
{ "v": "3HZMmIEc6yyjfoxdCwcOgPk/6X1GuNG+tlCmNgqBM/I=", "cloudagent_ip": "127.0.0.1", "cloudagent_port": 9002, "tpm_policy": "{\"22\": [\"0000000000000000000000000000000000000001\", \"0000000000000000000000000000000000000000000000000000000000000001\", \"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001\", \"ffffffffffffffffffffffffffffffffffffffff\", \"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\", \"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"], \"15\": [\"0000000000000000000000000000000000000000\", \"0000000000000000000000000000000000000000000000000000000000000000\", \"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"], \"mask\": \"0x408000\"}", "vtpm_policy": "{\"23\": [\"ffffffffffffffffffffffffffffffffffffffff\", \"0000000000000000000000000000000000000000\"], \"15\": [\"0000000000000000000000000000000000000000\"], \"mask\": \"0x808000\"}", "allowlist": "{}", "mb_refstate": "null", "ima_sign_verification_keys": "[]", "metadata": "{\"cert_serial\": 71906672046699268666356441515514540742724395900, \"subject\": \"/C=US/ST=MA/L=Lexington/O=MITLL/OU=53/CN=D432FBB3-D2F1-4A97-9EF7-75BD81C00000\"}", "revocation_key": "-----BEGIN PRIVATE KEY----- (...) -----END PRIVATE KEY-----\n", "accept_tpm_hash_algs": [ "sha512", "sha384", "sha256", "sha1" ], "accept_tpm_encryption_algs": [ "ecc", "rsa" ], "accept_tpm_signing_algs": [ "ecschnorr", "rsassa" ], "supported_version": "2.0" }
- Request JSON Object:
v (string) – V key for payload base64 encoded. Decoded length is 32 bytes.
cloudagent_ip (string) – Agents contact ip address for the CV.
cloudagent_port (string) – Agents contact port for the CV.
tpm_policy (string) – Static PCR policy and mask for TPM. Is a string encoded dictionary that also includes a mask for which PCRs should be included in a quote.
vtpm_policy (string) – Static PCR policy and mask for vTPM. Same as tpm_policy.
allowlist (string) – Allowlist JSON object string encoded.
mb_refstate (string) – Measured boot reference state policy.
ima_sign_verification_keys (string) – IMA signature verification public keyring JSON object string encoded.
metadata (string) – Metadata about the agent. Contains cert_serial and subject if a CA is used with the tenant.
revocation_key (string) – Key which is used to sign the revocation message of the agent.
accept_tpm_hash_algs (list[string]) – Accepted TPM hashing algorithms. sha1 must be enabled for IMA validation to work.
accept_tpm_encryption_algs (list[string]) – Accepted TPM encryption algorithms.
accept_tpm_signing_algs (list[string]) – Accepted TPM signing algorithms.
supported_version (string) – supported API version of the agent. v prefix must not be included.
- DELETE /v2.1/agents/{agent_id:UUID}¶
Terminate instance agent_id.
Example response:
{ "code": 200, "status": "Success", "results": {} }
- PUT /v2.1/agents/{agent_id:UUID}/reactivate¶
Start agent agent_id (for an already bootstrapped agent_id node)
- PUT /v2.1/agents/{agent_id:UUID}/stop¶
Stop cv polling on agent_id, but don’t delete (for an already started agent_id). This will make the agent verification fail.
Cloud Agent¶
- GET /v2.1/keys/pubkey¶
Retrieves agents public key.
Example response:
{ "code": 200, "status": "Success", "results": { "pubkey": "-----BEGIN PUBLIC KEY----- (...) -----END PUBLIC KEY-----\n" } }
- Response JSON Object:
pubkey (string) – Public rsa key of the agent used for encrypting V and U key.
- GET /version¶
Returns what API version the agent supports. This endpoint might not be implemented by all agents.
Example response:
{ "code": 200, "status": "Success", "results": { "supported_version": "2.0" } }
- Response JSON Object:
supported_version (string) – The latest version the agent supports.
- POST /v2.1/keys/vkey¶
Send v_key to node.
Example request:
{ "encrypted_key": "MN/F33jjuLiIuRH8fF7pMtw6Hoe2KG10zg+/xuuZLa5d1WB2aR6feVCwknZDe/dhG51yB0tKau8fCNUz8KMxyWoFkalIY4vVG6DNpLouDjb+vMvI6RmVmCBwO5zx6R802wK2z2yUbcn11TU/k2zHq34CNFIgI5pQu7cnLMzCLW6NLEp8N0IOQL6D+uV9emkheJH1g40xYwUaKoABWjZeaJN5dvKwbkpIf2m+CROmCNPCidh87J0g7BENUvlSUO1FPfRjch4kyxLrp+aMu9zmzF/tZErh1zk+nUamtrgl25pEImw+Cn9RIVTd6fBkmzlGzch5foAqZCyZ0AhQ0ONuWw==", }
- Request JSON Object:
encrypted_key (string) – V key encrypted with agents public key base64 encoded.
- POST /v2.1/keys/ukey¶
Send u_key to node (with optional payload)
Example request:
{ "auth_tag" : "3876c08b30c16c4140ee04300bb4262bbcc9034d8a2ed8c90784f13b484a570bf9da3d5c372141bd16d85de05c4c7cce", "encrypted_key": "iAckMZgZc8r43pF0iW8iwwAorD+rvnvF7AShhlz6+am+ryqW+907UynOrWrIrAseyVRE7ouHpr547gnwfF7oKeBFlEdWnE6FbQl9o6tk86BzQy3PImBLxJD/y/MmSuNR5pGQwZCueKI0ji3Nqq6heOgSvnMRC0PHgyumOsYiAnbDNyryvfwO4HsqdqMcEsBu1IVzU3EtJWhfQ8i/UpvHy6Jq4bBh+mw5HZwmK93bmsLXNKgjPWAicsCZINUAPVMCUL7dcDd4zijsBxMxiZF7Js7V25wKKFer2zqKsE5omLy9sKotFfWjgaROPLrKXxuDgNmlONJnD0btLZBa9T+mmA==", "payload": "WcXpUr4G9yfvVaojNx6K2XZuDYRkFoZQhHrvZB+TKZqsq41g" }
- Request JSON Object:
auth_tag (string) – HMAC calculated with K key as key and UUID as data, using SHA-384 as the underlying hash algorithm
encrypted_key (string) – U key encrypted with agents public key base64 encoded
payload (string) – (optional) payload encrypted with K key base64 encoded.
- GET /v2.1/keys/verify¶
Get confirmation of bootstrap key derivation
Example request:
/v2.1/keys/verify?challenge=1234567890ABCDEFHIJ
- Parameters:
challenge (string) – 20 character random string with [a-Z,0-9] as symbols.
Example response:
{ "code": 200, "status": "Success", "results": { "hmac": "719d992fb7d2a0761785fd023fe1cf8a584b835e465e71e2ef2632ff4e9938c080bdefba26194d8ea69dd7f9adee6c18" } }
- Response JSON Object:
hmac (string) – hmac with K key as key and the challenge
- GET /v2.1/quotes/integrity¶
Get integrity quote from node
Example request:
/v2.1/quotes/integrity?nonce=1234567890ABCDEFHIJ&mask=0x10401&partial=0
- Parameters:
nonce (string) – 20 character random string with [a-Z,0-9] as symbols.
mask (string) – Mask for what PCRs from the TPM are included in the quote.
partial (string) – Is either “0” or “1”. If set to “1” the public key is excluded in the response.
ima_ml_entry (string) – (optional) Line offset of the IMA entry list. If not present, 0 is assumed.
Example Response:
{ "code": 200, "status": "Success", "results": { "quote": "r/1RDR4AYABYABPihP2yz+HcGF0vD0c4qiKt4nvSOAARURVNUAAAAAAAyQ9AAAAAAAAAAAAEgGRAjABY2NgAAAAEABAMAAAEAFCkk4YmhQECgWR+MnHqT9zftc3J8:ABQABAEAQ8IwX6Ak83zGhF6w8vOKOxsyTbxACQakYWGJaan3ewf+2O9TtiH5TLB1PXrPdhknsR/yx6OVUze9jTDvML9xkkK1ghXObCJ5gH+QX0udKfrLacm/iMds28SBtVO0rjqDIoYqGgXhH2ZhwGNDwjRCp6HquvtBe7pGEgtZlxf7Hr3wQRLO3FtliBPBR6gjOo7NC/uGsuPjdPU7c9ls29NgYSqdwShuNdRzwmZrF57umuUgF6GREFlxqLkGcbDIT1itV4zJZtI1caLVxqiH0Qv3sNqlNLsSHggkgc5S2EvNqwv/TsEZOq/leCoLtyVGYghPeGwg0RJfbe8cdyBWCQ6nOA==:AQAAAAQAAwAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAUABdJ/ntmsqy2aDi6NhKnLKz4k4uEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", "hash_alg": "sha256", "enc_alg": "rsa", "sign_alg": "rsassa", "pubkey": "-----BEGIN PUBLIC KEY----- (...) -----END PUBLIC KEY-----\n" "boottime": 123456, "ima_measurement_list": "10 367a111b682553da5340f977001689db8366056a ima-ng sha256:94c0ac6d0ff747d8f1ca7fac89101a141f3e8f6a2c710717b477a026422766d6 boot_aggregate\n", "ima_measurement_list_entry": 0, "mb_measurement_list": "AAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAABTcGVjIElEIEV2ZW50MDMAAAAAAAACAAIBAAAACwAgAAAAAAAACAAAAAEAAAALAJailtIk8oXGe [....]" } }
- Response JSON Object:
quote (string) – TPM integrity quote
hash_alg (string) – Used hash algorithm used in the quote (e.g. sha1, sha256, sha512).
enc_alg (string) – Encryption algorithm used in the quote (ecc, rsa).
sign_alg (string) – Signing algorthm used in the quote (rsassa, rsapss, ecdsa, ecdaa or ecschnorr).
pubkey (string) – PEM encoded public portion of the NK (digest is measured into PCR 16).
boottime (int) – Seconds since the system booted
ima_measurement_list (string) – (optional) IMA entry list. Is included if IMA_PCR (10) is included in the mask
ima_measurement_list_entry (int) – (optional) Starting line offset of the IMA entry list returned
mb_measurement_list (string) – (optional) UEFI Eventlog list base64 encoded. Is included if PCR 0 is included in the mask
Quote format: The quote field contains the quote, the signature and the PCR values that make up the quote.
QUOTE_DATA := rTPM_QUOTE:TPM_SIG:TPM_PCRS TPM_QUOTE := base64(TPMS_ATTEST) TPM_SIG := base64(TPMT_SIGNATURE) TPM_PCRS := base64(tpm2_pcrs) // Can hold more that 8 PCR entries. This is a data structure generated by tpm2_quote
- GET /v2.1/quotes/identity¶
Get identity quote from node
Example request:
/v2.1/quotes/identity?nonce=1234567890ABCDEFHIJ
- Parameters:
nonce (string) – 20 character random string with [a-Z,0-9] as symbols.
Example response:
{ "code": 200, "status": "Success", "results": { "quote": "r/1RDR4AYABYABPihP2yz+HcGF0vD0c4qiKt4nvSOAARURVNUAAAAAAAyQ9AAAAAAAAAAAAEgGRAjABY2NgAAAAEABAMAAAEAFCkk4YmhQECgWR+MnHqT9zftc3J8:ABQABAEAQ8IwX6Ak83zGhF6w8vOKOxsyTbxACQakYWGJaan3ewf+2O9TtiH5TLB1PXrPdhknsR/yx6OVUze9jTDvML9xkkK1ghXObCJ5gH+QX0udKfrLacm/iMds28SBtVO0rjqDIoYqGgXhH2ZhwGNDwjRCp6HquvtBe7pGEgtZlxf7Hr3wQRLO3FtliBPBR6gjOo7NC/uGsuPjdPU7c9ls29NgYSqdwShuNdRzwmZrF57umuUgF6GREFlxqLkGcbDIT1itV4zJZtI1caLVxqiH0Qv3sNqlNLsSHggkgc5S2EvNqwv/TsEZOq/leCoLtyVGYghPeGwg0RJfbe8cdyBWCQ6nOA==:AQAAAAQAAwAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAUABdJ/ntmsqy2aDi6NhKnLKz4k4uEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", "hash_alg": "sha256", "enc_alg": "rsa", "sign_alg": "rsassa", "pubkey": "-----BEGIN PUBLIC KEY----- (...) -----END PUBLIC KEY-----\n" "boottime": 123456 } }
- Response JSON Object:
quote (string) – See quotes/integrity
hash_alg (string) – See quotes/integrity
enc_alg (string) – See quotes/integrity
sign_alg (string) – See quotes/integrity
pubkey (string) – See quotes/integrity
boottime (int) – See quotes/integrity
Cloud Registrar¶
- GET /v2.1/agents/¶
Get ordered list of registered agents
Example response:
{ "code": 200, "status": "Success", "results": { "uuids": [ "5e600bce-a5cb-4f5a-bf08-46d0b45081c5", "6dab10e4-6619-4ff9-9062-ee6ad23ec24d", "d432fbb3-d2f1-4a97-9ef7-75bd81c00000" ] } }
- GET /v2.1/agents/{agent_id:UUID}¶
Get EK certificate, AIK and optinal contact ip and port of agent agent_id.
Example response:
{ "code": 200, "status": "Success", "results": { "aik_tpm": "ARgAAQALAAUAcgAAABAAFAALCAAAAAAAAQDjZ4J2HO7ekIONAX/eYIzt7ziiVAqE/1D7I9oEwIE88dIfqH0FQLJAg8u3+ZOgsJDQr9HiMhZRPhv8hRuia8ULdAomyOFA1cVzlBF+xcPUEemOIofbvcBNAoTY/x49r8LpqAEUBBiUeOniQbjfRaV2S5cEAA92wHLQAPLF9Sbf3zNxCnbhtRkEi6C3NYl8/FJqyu5Z9vvwEBBOFFTPasAxMtPm6a+Z5KJ4rDflipfaVcUvTKLIBRI7wkuXqhTR8BeIByK9upQ3iBo+FbYjWSf+BaN+wodMNgPbzxyL+tuxVqiPefBbv+sTWVxmYfo5i84FlbNOAW3APH8c+jZ3tgbt", "ek_tpm": "AToAAQALAAMAsgAgg3GXZ0SEs/gakMyNRqXXJP1S124GUgtk8qHaGzMUaaoABgCAAEMAEAgAAAAAAAEA0YwlPPIoXryMvbD5cIokN9OkljL2mV1oDxy7ETBXBe1nL9OWrLNO8Nbf8EaSNCtYCo5iqCwatnVRMPqNXcX8mQP0f/gDAqXryb+F192IJLKShHYSN32LJjCYOKrvNX1lrmr377juICFSRClE4q+pCfzhNj0Izw/eplaAI7gq41vrlnymWYGIEi4McErWG7qwr7LR9CXwiM7nhBYGtvobqoaOm4+f6zo3jQuks/KYjk0BR3mgAec/Qkfefw2lgSSYaPNl/8ytg6Dhla1LK8f7wWy/bv+3z7L11KLr8DZiFAzKBMiIDfaqNGYPhiFLKAMJ0MmJx63obCqx9z5BltV5YQ==", "ekcert": "MIIEGTCCAoGgAwIBAgIBBTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDEw1zd3RwbS1sb2NhbGNhMB4XDTIxMDQwOTEyNDAyNVoXDTMxMDQwNzEyNDAyNVowODE2MDQGA1UEAxMtZmVkb3JhMzM6NDdjYzJlMDMtNmRmMi00OGMyLWFmNGUtMDg1MWY1MWQyODJiMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0YwlPPIoXryMvbD5cIokN9OkljL2mV1oDxy7ETBXBe1nL9OWrLNO8Nbf8EaSNCtYCo5iqCwatnVRMPqNXcX8mQP0f/gDAqXryb+F192IJLKShHYSN32LJjCYOKrvNX1lrmr377juICFSRClE4q+pCfzhNj0Izw/eplaAI7gq41vrlnymWYGIEi4McErWG7qwr7LR9CXwiM7nhBYGtvobqoaOm4+f6zo3jQuks/KYjk0BR3mgAec/Qkfefw2lgSSYaPNl/8ytg6Dhla1LK8f7wWy/bv+3z7L11KLr8DZiFAzKBMiIDfaqNGYPhiFLKAMJ0MmJx63obCqx9z5BltV5YQIDAQABo4HNMIHKMBAGA1UdJQQJMAcGBWeBBQgBMFIGA1UdEQEB/wRIMEakRDBCMRYwFAYFZ4EFAgEMC2lkOjAwMDAxMDE0MRAwDgYFZ4EFAgIMBXN3dHBtMRYwFAYFZ4EFAgMMC2lkOjIwMTkxMDIzMAwGA1UdEwEB/wQCMAAwIgYDVR0JBBswGTAXBgVngQUCEDEOMAwMAzIuMAIBAAICAKIwHwYDVR0jBBgwFoAUaO+9FEi5yX/GEnU+Vc6b3Si6JeAwDwYDVR0PAQH/BAUDAwcgADANBgkqhkiG9w0BAQsFAAOCAYEAaP/jI2i/hXDrthtaZypQ8VUG5AWFnMDtgiMhDSaKwOBfyxiUiYMTggGYXLOXGIu1SJGBtRJsh3QSYgs2tJCnntWF9Jcpmk6kIW/MC8shE+hdu/gQZKjAPZS4QCLIldv+GVZdNYEIv2FYDsKl6Bq1qUsYhAb7z29Nu1itpdvja2qy7ODJ0u+ThccBuH60VGFclFdJg19dvVQMnffxzjwxxJTMnVPmGoEdR94O0z7yxvqQ22+ITD9s1c3AfWcV+yLEpHqhXRqtKGdkAM5kU85kEs/ZPTLNutJHmF0/Vk9W2pRym8SrUe8G6mwxVW8lP9M7fhovKTzoXVFW3gQWQeUxhvWOncXxtARFLp/+f2mzGBRWxIslW17vpZ3QLlCdJ2C7P3U8x2tvkuyyDfz3/pq+8ECupZhdSvpHlBnWvqs1tAWKW0qI9d0xNYjj3Kfl3Lfy7kqqe6FIkvbDlVhw3vnJlclW+M6D86jBulL9ze+3zyMxy2z8m7UHiLCbamSe6m7W", "mtls_cert": "-----BEGIN CERTIFICATE----- (...) -----END CERTIFICATE-----", "ip": "127.0.0.1", "port": 9002, "regcount": 1 } }
- Response JSON Object:
aik_tpm (string) – base64 encoded AIK. The AIK format is TPM2B_PUBLIC from tpm2-tss.
ek_tpm (string) – base64 encoded EK. When a ekcert is submitted it will be the public key of that certificate.
ekcert (string) – base64 encoded EK certificate. Should be in DER format. Gets extracted from NV 0x1c00002.
mtls_cert (string) – Agent HTTPS server certificate. PEM encoded.
ip (string) – IPv4 address for contacting the agent. Might be null.
port (integer) – Port for contacting the agent. Might be null.
- POST /v2.1/agents/{agent_id:UUID}¶
Add agent agent_id to registrar.
Example request:
{ "ekcert": "MIIEGTCCAoGgAwIBAgIBBTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDEw1zd3RwbS1sb2NhbGNhMB4XDTIxMDQwOTEyNDAyNVoXDTMxMDQwNzEyNDAyNVowODE2MDQGA1UEAxMtZmVkb3JhMzM6NDdjYzJlMDMtNmRmMi00OGMyLWFmNGUtMDg1MWY1MWQyODJiMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0YwlPPIoXryMvbD5cIokN9OkljL2mV1oDxy7ETBXBe1nL9OWrLNO8Nbf8EaSNCtYCo5iqCwatnVRMPqNXcX8mQP0f/gDAqXryb+F192IJLKShHYSN32LJjCYOKrvNX1lrmr377juICFSRClE4q+pCfzhNj0Izw/eplaAI7gq41vrlnymWYGIEi4McErWG7qwr7LR9CXwiM7nhBYGtvobqoaOm4+f6zo3jQuks/KYjk0BR3mgAec/Qkfefw2lgSSYaPNl/8ytg6Dhla1LK8f7wWy/bv+3z7L11KLr8DZiFAzKBMiIDfaqNGYPhiFLKAMJ0MmJx63obCqx9z5BltV5YQIDAQABo4HNMIHKMBAGA1UdJQQJMAcGBWeBBQgBMFIGA1UdEQEB/wRIMEakRDBCMRYwFAYFZ4EFAgEMC2lkOjAwMDAxMDE0MRAwDgYFZ4EFAgIMBXN3dHBtMRYwFAYFZ4EFAgMMC2lkOjIwMTkxMDIzMAwGA1UdEwEB/wQCMAAwIgYDVR0JBBswGTAXBgVngQUCEDEOMAwMAzIuMAIBAAICAKIwHwYDVR0jBBgwFoAUaO+9FEi5yX/GEnU+Vc6b3Si6JeAwDwYDVR0PAQH/BAUDAwcgADANBgkqhkiG9w0BAQsFAAOCAYEAaP/jI2i/hXDrthtaZypQ8VUG5AWFnMDtgiMhDSaKwOBfyxiUiYMTggGYXLOXGIu1SJGBtRJsh3QSYgs2tJCnntWF9Jcpmk6kIW/MC8shE+hdu/gQZKjAPZS4QCLIldv+GVZdNYEIv2FYDsKl6Bq1qUsYhAb7z29Nu1itpdvja2qy7ODJ0u+ThccBuH60VGFclFdJg19dvVQMnffxzjwxxJTMnVPmGoEdR94O0z7yxvqQ22+ITD9s1c3AfWcV+yLEpHqhXRqtKGdkAM5kU85kEs/ZPTLNutJHmF0/Vk9W2pRym8SrUe8G6mwxVW8lP9M7fhovKTzoXVFW3gQWQeUxhvWOncXxtARFLp/+f2mzGBRWxIslW17vpZ3QLlCdJ2C7P3U8x2tvkuyyDfz3/pq+8ECupZhdSvpHlBnWvqs1tAWKW0qI9d0xNYjj3Kfl3Lfy7kqqe6FIkvbDlVhw3vnJlclW+M6D86jBulL9ze+3zyMxy2z8m7UHiLCbamSe6m7W", "aik_tpm": "ARgAAQALAAUAcgAAABAAFAALCAAAAAAAAQCg5mMzNFqdlUbW8uI/GuMcIIvOXXTohHFTas59JlwrJQVed+5klWP+j7tI7492YPmCnoZvP4T4YdT1PN7tHHGfF81AeMnuw5GV5RkW/QeSD+ssB4f6AfuzYJgBkc28zKmpRRHUbwN4rb/HnJgRXdXsuIcnOqGcC39pD0kiu5TrN6hekjxTQtfAbIlQwwDwHCxKWdtH5x7avd15hqc6cBc2gjTQksXrk+OiMwOFTJ68n0qY+dQYuBTjE66YXn9S8cdU9sbjCTSdLRqFEpAyfkSV8F2An7N3DWNIA+PW/mVmd8XhPeYUoMlweXBOwc3e9zM9lZmMvregrFHKYc7CXChz", "mtls_cert": "-----BEGIN CERTIFICATE----- (...) -----END CERTIFICATE-----", "ip": "127.0.0.1", "port": "9002" }
- Request JSON Object:
ekcert (string) – base64 encoded EK certificate. Should be in DER format. Gets extracted from NV 0x1c00002.
aik_tpm (string) – base64 encoded AIK. The AIK format is TPM2B_PUBLIC from tpm2-tss.
mtls_cert (string) – Agent HTTPS server certificate. PEM encoded.
ip (string) – (Optional) contact IPv4 address for the verifier and tenant to use.
port (string) – (Optional) contact port for the verifier and tenant to use.
Example response:
{ "code": 200, "status": "Success", "results": { "blob": "utzA3gAAAAEARAAgC/w9LP1PKZ9thEk+GkMg4m+tkc9TkavcvFiFL6xbXM2q2fTRyKmQnxuCJc0tQdgsRXMftGiKJyA/SUo8kGNVmcNfAQCs79kl9Ir49JJ8rfyMfDIqOuSVlu9PhxGUOeVzAdxyUmPxq5Qp0s431n/KeL/5nUaVXC+qpOftF4bmVtXwLGTTUbKtyT3GG+9ujkjiwHCQhSKTQ8HiuARgXXh13ntFsJ75PBD5dWauLTuciYZI/WQDVXAcgMnQNxodJUi9ir1GxJWz8zufjVQTVjrlgsgeBdOKbB6+H81K1d9prWhZaVLP+wIwO3YuWgtNHNi90E1z/dah2pzfUpLvJo3lNZ4bJgrJUR507AokGKIFm7EfOf+5WWWAvGxGtgqTJB27vgE0CVBLEuDUHoRcLVBi1Np4GGNTByalxbulg8x1eGtZyuQF" } }
- Response JSON Object:
blob (string) – base64 encoded blob containing the aik_tpm name and a challenge. Is encrypted with ek_tpm.
- DELETE /v2.1/agents/{agent_id:UUID}¶
Remove agent agent_id from registrar.
Example response:
{ "code": 200, "status": "Success", "results": {} }
- PUT /v2.1/agents/{agent_id:UUID}/activate¶
Activate physical agent agent_id
Example request:
{ "auth_tag": "7087ba88746886262de743587ed97aea6b6e3f32755de5d85415c40feef3169bc58d38855ddb96e32efdd8745d0bdfef" }
- Request JSON Object:
auth_tag (string) – hmac containing the challenge from blob and the agent_id.
- PUT /v2.1/agents/{agent_id:UUID}/vactivate¶
Activate virtual (vTPM) agent agent_id
Requires JSON Body:
{ "deepquote" : b64, }
Changelog¶
Changes between the different API versions.
Changes from v2.0 to v2.1¶
API version 2.1 was first implemented in Keylime 6.4.0.
Added ak_tpm field to POST /v2.1/agents/{agent_id:UUID} in cloud verifier.
Added mtls_cert field to POST /v2.1/agents/{agent_id:UUID} in cloud verifier.
Removed vmask parameter from
This removed the requirement for the verifier to connect to the registrar.
Changes from v1.0 to v2.0¶
API version 2.0 was first implemented in Keylime 6.3.0.
Added mTLS authentication to agent endpoints.
Added supported_version field to POST /v2.0/agents/{agent_id:UUID} in cloud verifier.
Added mtls_cert field to POST/GET /v2.0/agents/{agent_id:UUID} in registrar.
Added /version endpoint to agent. Note that this endpoint is not implemented by all agents.
Dropped zlib encryption for quote field data in GET /v2.0/quotes/integrity/GET /v2.0/quotes/identity.
KeyLime Development¶
Contributing¶
When contributing any keylime repository, please first discuss the change you wish to make via an issue in the relevant repository for your change or email to the keylime mailing list
Pull Request Process¶
Create an issue outlining the fix or feature.
Fork the keylime repository to your own github account and clone it locally.
Hack on your changes.
Update the README.md or documentation with details of changes to any interface, this includes new environment variables, exposed ports, useful file locations, CLI parameters and configuration values.
Add and commit your changes with some descriptive text on the nature of the change / feature in your commit message. Also reference the issue raised at [1] as follows: Fixes #45. See the following link for more message types
Ensure that CI passes, if it fails, fix the failures.
Every pull request requires a review from the core keylime team
If your pull request consists of more than one commit, please squash your commits as described in see Squash Commits.
Commit Message Guidelines¶
We follow the commit formatting recommendations found on Chris Beams’ How to Write a Git Commit Message article.
Well formed commit messages not only help reviewers understand the nature of the Pull Request, but also assists the release process where commit messages are used to generate release notes.
A good example of a commit message would be as follows:
Summarize changes in around 50 characters or less
More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as the
subject of the commit and the rest of the text as the body. The
blank line separating the summary from the body is critical (unless
you omit the body entirely); various tools like `log`, `shortlog`
and `rebase` can get confused if you run the two together.
Explain the problem that this commit is solving. Focus on why you
are making this change as opposed to how (the code explains that).
Are there side effects or other unintuitive consequences of this
change? Here's the place to explain them.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Typically a hyphen or asterisk is used for the bullet, preceded
by a single space, with blank lines in between, but conventions
vary here
If you use an issue tracker, put references to them at the bottom,
like this:
Resolves: #123
See also: #456, #789
Note the Resolves #123 tag, this references the issue raised and allows us to ensure issues are associated and closed when a pull request is merged.
Please refer to the github help page on message types for a complete list of issue references.
Squash Commits¶
Should your pull request consist of more than one commit (perhaps due to a change being requested during the review cycle), please perform a git squash once a reviewer has approved your pull request.
A squash can be performed as follows. Let’s say you have the following commits:
initial commit
second commit
final commit
Run the command below with the number set to the total commits you wish to squash (in our case 3 commits):
git rebase -i HEAD~3
You default text editor will then open up and you will see the following:
pick eb36612 initial commit
pick 9ac8968 second commit
pick a760569 final commit
# Rebase eb1429f..a760569 onto eb1429f (3 commands)
We want to rebase on top of our first commit, so we change the other two commits to squash:
pick eb36612 initial commit
squash 9ac8968 second commit
squash a760569 final commit
After this, should you wish to update your commit message to better summarise all of your pull request, run:
git commit --amend
You will then need to force push (assuming your initial commit(s) were posted to github):
git push origin your-branch --force
Docker Development Environment¶
The following is a guide to mounting your local repository as a Docker volume and performing a test run using a TPM simulator. This will replicate the same test that occurs within the KeyLime CI gate for keylime.
This requires a working installation of Docker. See your distributions guide on how to set that up.
As an example, on Fedora 29:
sudo dnf -y install dnf-plugins-core
sudo dnf install docker-ce docker-ce-cli containerd.io
sudo usermod -aG docker $USER
sudo systemctl enable docker
sudo systemctl start docker
Note: login and out of your shell, if you want to run docker as $USER
Save the following script to your local machine (tip: create an alias to call the script in an easy to remember way):
#!/bin/bash
# Your local keylime (you should likely change this)
REPO="/home/${USER}/keylime"
# keylime images
tpm12image="lukehinds/keylime-ci-tpm12"
tpm12tag="v550"
tpm20image="lukehinds/keylime-ci-tpm20"
tpm20tag="v301"
echo -e "Grabbing latest images"
docker pull ${tpm12image}:${tpm12tag}
docker pull ${tpm20image}:${tpm20tag}
function tpm1 {
container_id=$(mktemp)
docker run --detach --privileged \
-v $REPO:/root/keylime \
-it ${tpm12image}:${tpm12tag} >> ${container_id}
docker exec -u 0 -it --tty "$(cat ${container_id})" \
/bin/sh -c 'cd /root/keylime/test; chmod +x ./run_tests.sh; ./run_tests.sh -s openssl'
docker stop "$(cat ${container_id})"
docker rm "$(cat ${container_id})"
}
function tpm2 {
container_id=$(mktemp)
docker run --detach --privileged \
-v $REPO:/root/keylime \
-v /sys/fs/cgroup:/sys/fs/cgroup:ro \
-it ${tpm20image}:${tpm20tag} >> ${container_id}
docker exec -u 0 -it --tty "$(cat ${container_id})" \
/bin/bash /root/keylime/.ci/test_wrapper.sh
docker stop "$(cat ${container_id})"
docker rm "$(cat ${container_id})"
}
while true; do
echo -e ""
read -p "Do you wish to test against TPM1.2(a) / TPM 2.0(b) or q(quit): " abq
case $abq in
[a]* ) tpm1;;
[b]* ) tpm2;;
[q]* ) exit;;
* ) echo "Please answer 1, 2 q(quit)";;
esac
done
Securing Keylime¶
Warning
This page is still under development and not complete. It will be so until this warning is removed.
System Hardening¶
TLS configuration¶
Reporting an issue¶
Please contact us directly at security@keylime.groups.io for any bug that might impact the security of this project. Do not use a github issue to report any potential security bugs.