Feb 7, 2026·4 min read·25 visits
Keylime's Registrar component (v7.12.x-7.13.0) explicitly set SSL verification to 'Optional', effectively disabling mTLS authentication. Attackers could list, query, and delete TPM agent registrations without credentials.
For a framework dedicated to establishing the Root of Trust in hostile cloud environments, Keylime briefly forgot to verify who was knocking at the door. A critical misconfiguration in the Registrar component turned mandatory mutual TLS (mTLS) into a polite suggestion, allowing unauthenticated attackers to manipulate the attestation database.
In the world of Cloud Native computing, you can't trust the hardware you're running on. That's where Keylime comes in. It is the CNCF's heavy hitter for remote attestation—a fancy way of using a Trusted Platform Module (TPM) to mathematically prove that a server hasn't been tampered with before you send it your secrets.
The Keylime architecture relies on three pillars: the Agent (running on the node), the Verifier (checking the TPM quotes), and the Registrar. Think of the Registrar as the secure phonebook. It's the first place an Agent calls home to drop off its public keys (Endorsement Key and Attestation Identity Key). If you compromise the Registrar, you can disrupt the chain of trust before it even begins.
Usually, Keylime is paranoid. It uses Mutual TLS (mTLS) for everything. The Agent needs a cert, the Verifier needs a cert, and the Registrar needs a cert. It's a zero-trust party where everyone checks ID at the door. Or at least, it was supposed to be.
The vulnerability (CVE-2026-1709) is a classic case of 'Explicit is better than Implicit, unless you explicitly choose the wrong setting.'
In Python's ssl module, SSLContext.verify_mode controls how the server treats client certificates. A secure mTLS setup requires ssl.CERT_REQUIRED. This tells the server: 'If the client doesn't show a valid certificate signed by our CA, hang up immediately.'
However, in Keylime versions 7.12.0 through 7.13.0, the code was changed to use ssl.CERT_OPTIONAL. In the documentation, this mode sounds harmless enough: it asks for a certificate if the client has one. But the fatal nuance is what happens when the client doesn't have one. With CERT_OPTIONAL, the handshake succeeds anonymously.
It's like a bouncer at a club who asks for ID, but if you just shrug and say 'I left it at home,' he lets you in anyway. The authentication logic downstream assumed that if a connection existed, it must have been authenticated. It was not.
Let's look at the smoking gun in keylime/web/base/server.py. The developers had a perfectly good helper function, web_util.init_mtls, which presumably set up the context correctly. But then, they manually overrode it.
Here is the diff that fixed the issue. Notice the line being removed:
# keylime/web/base/server.py
self._ssl_ctx = web_util.init_mtls(component)
- self._ssl_ctx.verify_mode = CERT_OPTIONALThat single line, - self._ssl_ctx.verify_mode = CERT_OPTIONAL, was the kill switch for authentication. It effectively downgraded the Registrar's API from a fortress to a public library. By removing this line in the patch, the _ssl_ctx reverts to the default secure settings defined in init_mtls, which enforces CERT_REQUIRED.
Exploiting this does not require heap spraying, ROP chains, or race conditions. You just need curl and a lack of manners.
Normally, to talk to the Registrar, you'd need a command like this:
curl --cert client.crt --key client.key --cacert ca.crt https://registrar:8891/v2/agents/But thanks to CERT_OPTIONAL, an attacker on the same network can simply do:
# The --insecure flag here just ignores the server's cert validity,
# but crucially, we are NOT providing a client cert.
curl --insecure https://registrar:8891/v2/agents/The Result:
Instead of a 401 Unauthorized or a TLS handshake failure, the server happily responds with a JSON list of all registered agents and their statuses.
From there, the attacker can:
/v2/agents/{uuid}) to grab public keys.DELETE request to /v2/agents/{uuid}. This deregisters the node. When the Verifier next tries to attest that node, it will fail, likely causing the orchestration layer (Kubernetes/OpenShift) to mark the node as untrusted and terminate workloads. It is a trivial Denial of Service against the infrastructure's trust layer.If you are running Keylime 7.12.x or 7.13.0, you are vulnerable. The fix is straightforward:
CERT_OPTIONAL override.ssl_verify_client on; (in Nginx) and forward the traffic only if the certificate is valid.CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
keylime Keylime (CNCF) | >= 7.12.0, < 7.12.2 | 7.12.2 |
keylime Keylime (CNCF) | == 7.13.0 | 7.13.1 |
| Attribute | Detail |
|---|---|
| CVE | CVE-2026-1709 |
| CVSS v3.1 | 9.4 (Critical) |
| CWE | CWE-287 (Improper Authentication) |
| Vector | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Platform | Python (Tornado/SSL) |
| Impact | Auth Bypass / DoS |
Improper Authentication