Decrypting TLS in Kubernetes without private keys

Two of your services disagree about what they said to each other, and the conversation is encrypted. This is the point where most investigations stall, because the obvious ways to see inside TLS either do not work any more or change the thing you are trying to observe.

Why the obvious answers fail

The server private key

This worked twenty years ago and does not work now. Any cipher suite with forward secrecy derives a fresh session key per connection from an ephemeral exchange, and the server's long-term private key never reproduces it. Forward secrecy is mandatory in TLS 1.3 and effectively universal in TLS 1.2. Holding the private key lets you impersonate the server; it does not let you read the conversation.

Terminating at a proxy or a mesh sidecar

Putting a proxy in the path gives you plaintext at the proxy, which is real but partial. You see what the proxy sees, in the shape the proxy reports it, and you have changed the topology you were investigating. If the problem is between two pods that speak mutual TLS to each other, the traffic you care about never passes through the thing you added.

SSLKEYLOGFILE

The standard debugging route: ask the process to write its session keys to a file, then hand the file to Wireshark. It requires setting an environment variable and restarting each process you want to observe, most production runtimes do not honour it, and restarting the process is often the one thing that makes the problem disappear.

Instrumenting the application

Logging requests and responses in code works, at the cost of an SDK per language, a deployment per service, and the fundamental limit that you only ever see what somebody remembered to log. The interesting field is reliably the one that was not logged.

What actually works: reading the key from memory

Every process doing TLS holds the session key in its own memory, because it has to. eBPF lets you attach a probe to a function inside a running process from outside it, without a kernel module, without an SDK, and without restarting anything. Attach to the TLS library's read and write entry points and the key is right there as each connection is established.

In Spider this is done by an agent called Gocipher. It hooks OpenSSL, covering roughly ten version variants plus Node, and handles statically-linked Go binaries through a separate mode that finds Go's own build information in the ELF file and hooks the key-log entry point in crypto/tls. The keys go to the backend, get matched to the TCP sessions they belong to, and the captured packets are decrypted from there. It needs Linux kernel 5.6 or later, which is checked when the agent starts. Agent documentation →

Not hiding the TLS, showing it

There are two eBPF approaches here and the difference matters more than it looks.

Most tools in this space take the plaintext directly. A probe on SSL_read and SSL_write copies the buffer after decryption and before encryption, and that copy is what you look at. It is efficient, it needs no packet capture at all, and it works whether or not anything could ever have decrypted the session.

Spider probes the same functions but takes the session key, and applies it to packets captured off the wire. The payload is identical either way. What differs is everything around it.

The cost of this is real and worth stating. Capturing and storing packets is more expensive than copying a buffer, and if a key is missed, Spider holds a session it cannot read, where a plaintext probe would have had the payload regardless. Spider makes that trade on purpose: it is buying evidence, not just visibility.

Who else can do this

Kubeshark, Pixie and DeepFlow all read encrypted traffic without private keys, all using the plaintext approach described above. Hubble does not decrypt at all. If what you need is the payload and nothing more, any of them will give it to you, and all three are open source, in Kubeshark's case its core. How the five tools compare →

Competitor details checked on 9 August 2026 against vendor documentation.

Common questions

Can I decrypt TLS with my server private key?
Not for modern traffic. Any cipher suite offering forward secrecy negotiates a fresh key per connection through an ephemeral exchange, so the server private key never derives the session key. Forward secrecy is mandatory in TLS 1.3 and the default in practice for TLS 1.2, which means passive decryption from a stored private key does not work on traffic worth looking at.
Does decrypting TLS require changing my application?
No. eBPF probes attach to the TLS library inside the running process from outside it. There is no SDK to import, no library to link, no environment variable to set and no restart. The application is not aware of it.
What is the difference between capturing plaintext and extracting session keys?
Both use eBPF probes on the same TLS library functions. A plaintext probe copies the buffer after decryption or before encryption, so it gets the payload and nothing else. Key extraction takes the session key and uses it to decrypt captured packets, so you keep the payload and the packets and the handshake around them. The first is cheaper, the second is evidence.
Can I see a TLS handshake that failed?
Only if you captured the packets. A handshake that never completes produces no application data, so a plaintext probe has nothing to capture and there is nothing for that approach to record. Spider captures packets first, so a failed negotiation, an unacceptable cipher suite, an expired certificate or a TLS alert are all still there to look at.
Does this work with mTLS and service meshes?
Yes, when the probes can reach the TLS library each side is using. Mutual TLS between pods is still TLS: the same probes apply on both ends. What matters is the library and the binary, not who authenticated whom.