Supported Runtimes
Kloak works by attaching eBPF uprobes to TLS library functions in your application's process. The specific functions targeted depend on which TLS library your application uses. This guide covers what works, what does not, and what to watch out for in each runtime.
Detection Strategy
When Kloak's controller detects a new pod, it resolves the container's PID and probes the process in this order:
- Go
crypto/tls-- Looks for thecrypto/tls.(*Conn).Writesymbol in the binary - OpenSSL / BoringSSL (statically linked) -- Looks for
SSL_writeandSSL_write_exsymbols in the main executable - OpenSSL / BoringSSL (dynamically linked) -- Scans
/proc/<pid>/mapsfor shared libraries matchinglibssl.so*,libboringssl.so*, orlibcrypto.so*, then probes those forSSL_write/SSL_write_ex - GnuTLS (dynamically linked) -- Scans
/proc/<pid>/mapsforlibgnutls.so*and looks forgnutls_record_send/gnutls_record_send2symbols
The first successful attachment wins. Host filtering uses DNS-verified resolution which works identically for all runtimes.
Host Filtering
Host filtering works the same way for all supported runtimes. It does not depend on the TLS library or protocol (HTTP/1.1, HTTP/2, or raw TLS).
Kloak uses DNS-verified host filtering: a kprobe on the kernel's udp_recvmsg captures DNS responses, and connect tracepoints track TCP connections. At TLS write time, the destination hostname is resolved through this chain. See the Host Filtering guide for details.
TIP
Unlike SNI-based approaches, DNS-verified filtering works for HTTP/2, raw TLS sockets, and any protocol. No application-level changes are needed.
Go (crypto/tls)
Status: Fully supported
Go applications using the standard crypto/tls package are intercepted via a uprobe on crypto/tls.(*Conn).Write. Since Go statically links the TLS implementation into the binary, no shared library scanning is needed.
How It Works
App calls http.Client.Do(req)
→ net/http serializes headers + body
→ tls.(*Conn).Write(plaintext)
→ eBPF uprobe fires, scans for kloak: prefix
→ Real secret injected before encryptionLimitations
- Connection pooling: Go's
http.Transportreuses TLS connections. The DNS-verified host is cached on first successful resolution, so subsequent writes on the same connection use the cached hostname. - Stripped binaries: If compiled with
-ldflags="-s -w", thecrypto/tls.(*Conn).Writesymbol may not be resolvable. Ensure your Go binaries retain symbol tables.
Python (OpenSSL)
Status: Fully supported
Python's ssl module wraps OpenSSL via libssl.so. Kloak attaches uprobes to SSL_write in the dynamically linked library.
How It Works
App calls requests.get("https://api.example.com", headers={"Authorization": secret})
→ urllib3 → ssl.SSLSocket.write()
→ OpenSSL SSL_write() in libssl.so
→ eBPF uprobe fires, scans for kloak: prefix
→ Real secret injected before encryptionimport requests
# Works out of the box -- no special configuration needed
response = requests.get(
"https://api.stripe.com/v1/charges",
headers={"Authorization": f"Bearer {secret}"},
)TIP
Python applications work out of the box with Kloak. No HTTP version forcing or special TLS configuration is needed.
Node.js (BoringSSL)
Status: Fully supported
Node.js statically links BoringSSL into its binary. Kloak attaches to SSL_write and SSL_write_ex in the main executable.
How It Works
App calls https.request() or fetch()
→ Node TLS module → BoringSSL SSL_write()
→ eBPF uprobe fires, scans for kloak: prefix
→ Real secret injected before encryptionconst https = require('https');
// Works out of the box
const options = {
hostname: 'api.stripe.com',
path: '/v1/charges',
headers: {
'Authorization': `Bearer ${secret}`,
},
};
https.request(options, (res) => {
// Response handling
});Go + BoringSSL
Status: Fully supported
Some Go applications use BoringSSL instead of the standard crypto/tls -- for example, applications built with GOEXPERIMENT=boringcrypto or those linking against BoringSSL for FIPS compliance. Kloak detects this and attaches to SSL_write in the main executable or in the linked BoringSSL shared library.
How It Works
When Kloak probes the binary:
- The standard Go
crypto/tls.(*Conn).Writesymbol is not found (or is a wrapper) - Kloak falls back to checking for
SSL_write/SSL_write_exin the binary - If found (statically linked BoringSSL), uprobes are attached directly
For dynamically linked BoringSSL:
- Kloak scans
/proc/<pid>/mapsforlibboringssl.so*orlibssl.so* - Attaches
SSL_writeuprobes to the shared library
Any OpenSSL-Linked Binary
Status: Supported
Any application that dynamically links against libssl.so is automatically supported. This includes:
- Ruby (OpenSSL via
opensslgem) - PHP (OpenSSL via
php-opensslextension) - Rust (when using
opensslornative-tlscrates with system OpenSSL) - C/C++ applications using OpenSSL directly
- Java (when using native TLS via JNI, though most Java apps use the JVM's built-in TLS)
Kloak scans /proc/<pid>/maps for any library matching libssl.so*, libboringssl.so*, or libcrypto.so* and attaches uprobes automatically.
GnuTLS
Status: Experimental
Kloak detects libgnutls.so in container processes and looks for gnutls_record_send and gnutls_record_send2 symbols. Uprobe attachment is attempted, but the eBPF handler for GnuTLS is not yet implemented. Applications using GnuTLS (common in GNOME-based tools, wget, and some C/C++ applications) will have uprobes attached but secret rewriting will not occur.
WARNING
GnuTLS support is a work in progress. The shadow secret mechanism still works — your application will read kloak:<ULID> placeholders — but in-kernel rewriting is not yet active for GnuTLS connections.
What Is NOT Supported
Custom TLS Stacks
Applications that implement their own TLS handshake and encryption (without using OpenSSL, BoringSSL, Go's crypto/tls, or GnuTLS) are not supported. The eBPF uprobes have no known function to attach to.
Examples of unsupported stacks:
- Java's built-in JSSE (TLS is implemented in pure Java, not via native OpenSSL)
- mbedTLS (different API:
mbedtls_ssl_write) - s2n-tls (AWS's TLS library, different API)
TIP
If your application uses an unsupported TLS stack, you may still benefit from Kloak's shadow secret mechanism. The application will read kloak:<ULID> values, but they will be sent as-is without in-kernel rewriting. You would need a sidecar proxy or application-level integration to perform the substitution.
Statically Linked Go Binaries Without Symbol Table
If a Go binary is compiled with -ldflags="-s -w" (stripped symbols), the crypto/tls.(*Conn).Write symbol may not be resolvable. Kloak will fail to attach the uprobe and log an error. Ensure your Go binaries retain symbol tables in production images used with Kloak.
Runtime Compatibility Matrix
| Runtime | TLS Library | eBPF Hook | Host Filtering | HTTP/2 | Notes |
|---|---|---|---|---|---|
| Go | crypto/tls | tls.(*Conn).Write | DNS-verified | Yes | Works out of the box |
| Go + BoringSSL | BoringSSL | SSL_write | DNS-verified | Yes | FIPS compliance builds |
| Python | OpenSSL (libssl) | SSL_write | DNS-verified | Yes | Works out of the box |
| Node.js | BoringSSL | SSL_write | DNS-verified | Yes | Works out of the box |
| Ruby | OpenSSL (libssl) | SSL_write | DNS-verified | Yes | Via system OpenSSL |
| Rust | OpenSSL (libssl) | SSL_write | DNS-verified | Yes | When using native-tls |
| C/C++ | OpenSSL (libssl) | SSL_write | DNS-verified | Yes | Direct OpenSSL usage |
| Any (GnuTLS) | GnuTLS | gnutls_record_send | -- | -- | Experimental — detection works, rewriting not yet active |
| Java (JSSE) | JVM built-in | -- | -- | -- | Not supported |