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 handshake is visible. Cipher suites, certificates, renegotiation, resumption and alerts are all packets, so they are all still there. To a plaintext probe, TLS itself is invisible; it only ever sees payload.
- Failures are visible. A handshake that never completes carries no application data, so a plaintext probe has nothing to record, even though the failed connection attempt is right there on the wire. That is precisely the case you most need to see, and it is the case that approach cannot reach.
- The transport is visible. Retransmissions, reordering, loss, MTU problems and resets are properties of the wire. A plaintext probe shows what the application meant to send, not what the network carried.
- The decrypted request stays attached to its packets. You can read the request and then look at the bytes that carried it, because they are the same object. When the plaintext never came from packets, there are no packets to go back to.
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.