Skip to content

Kernel Interface

Every interface the Riptides kernel module exposes to user space, and who is allowed to use each one. If you are reviewing the module’s attack surface, this page is the complete list.

There is no ioctl and no netlink interface. The module is reached through one character device (two with the pro module), a set of module parameters, sysfs and procfs entries, kernel tracepoints, a single socket option, and a hook on every execve, which only acts when env_inject is on.

OperationRequirement
Load the moduleCAP_SYS_MODULE
Open /dev/riptides or /dev/riptides_pro and command the driverCAP_SYS_ADMIN
Change a module parameter at runtimeCAP_SYS_ADMIN (or CAP_SYS_MODULE)
Read the diagnostic entriesfile permissions (see below)
Read a credential filethe reading process must resolve to the credential’s workload
Read the tracepointsroot, or CAP_PERFMON (with CAP_BPF for eBPF)
Opt out of environment injectionnone: any process, through its own environment

CAP_SYS_ADMIN is checked inside the module, so file ownership is not the only gate: a process holding CAP_DAC_OVERRIDE cannot command the driver on the strength of that capability alone.

On SELinux hosts the optional riptides-selinux package narrows it further, so that only the daemon’s own SELinux domain, and privileged containers, can open the devices through /dev, regardless of capabilities. See Host Hardening. It is not required: the driver runs without it, and the capability check above is independent of SELinux.

PathModePurpose
/dev/riptides0600Daemon IPC: Protocol Buffer messages in both directions
/dev/riptides_pro0600Pro module only: the daemon writes the credentials injected into workload traffic

Each device is exclusive: one holder at a time. A second open returns EBUSY, so a stray process cannot quietly attach alongside the daemon. They are the only interfaces that can change policy, identities or credentials. A CAP_SYS_ADMIN holder can still switch enforcement off as a whole: enabled set to N stops interception, and force_close kills the daemon, after which new connections go through unevaluated until it is back.

Under /sys/module/riptides/parameters/, mode 0644 — world-readable, and writable only with CAP_SYS_ADMIN, or CAP_SYS_MODULE, which could load a module of its own anyway. Values may also be set at load time.

ParameterTypeDefaultEffect
enabledboolYEnable or disable interception at runtime
ktls_enabledboolYOffload the record layer to kTLS where available
mdx_enabledboolYPerform the metadata-exchange handshake
mdx_tcpopt_enabledboolYCarry MDX hints in a TCP option
mdx_dscp_enabledboolNCarry MDX hints in the DSCP field
trace_all_socketsboolYAttach a traced socket to every new TCP connection
eval_fail_closedboolNReset a connection when a policy evaluation fails, instead of letting it proceed unprotected
key_typestringecKey type for certificate operations: ec or rsa
command_timeoutuint1Seconds to wait for a daemon answer
augmentation_heightuint5Process-tree depth used to attribute a connection to a workload
eval_cache_sizeuint4096Policy evaluation cache capacity
eval_cache_ttl_secuint30Lifetime of a cached allow decision
eval_cache_negative_ttl_secuint5Lifetime of a cached deny decision
intercept_debugboolNVerbose interception logging, including decrypted intercepted traffic; see Kernel log
env_injectboolNInject TLS trust-store environment variables at execve
env_inject_debugboolNLog every environment-injection decision

All read-only except force_close, which takes the same capability as the device itself.

PathModeContents
/sys/module/riptides/config0444Effective configuration
/sys/module/riptides/health0444Module health and daemon connection state
/sys/module/riptides/self0444The reading process’s workload UID, or EACCES if it has none
/sys/module/riptides/self_spiffe0444The reading process’s SPIFFE ID, or EACCES if it has none
/sys/module/riptides/certs/ca-certificates.crt0444Full CA bundle, for applications that verify TLS themselves
/sys/module/riptides/certs/ca-spiffe.crt0444SPIFFE trust anchor
/sys/module/riptides/certs/ca-intercept.crt0444Intercept CA
/sys/module/riptides/credentials/<workload-uid>/<binding>/<file>0444Credential files delivered to workloads, replaced when the credential is refreshed; pro module only. <workload-uid> is the value self returns to that workload
/sys/class/riptides/force_close0200Write-only: writing 1 sends SIGKILL to the process holding /dev/riptides, normally the daemon. Requires CAP_SYS_ADMIN

The certificate files hold only public certificates and trust anchors, and any local process can read them.

Credential files are mode 0444, but the mode is not what protects them. On every read the module resolves the reading process to a workload identity and returns EACCES unless it is the workload the credential belongs to. Any process that resolves to that workload can read it, so the boundary is the workload’s identity selectors, not the Unix user. The files hold the credential itself, secrets included: an OCI credential delivers the workload’s RSA private key as private_key.pem.

Read-only, mode 0440, so root only.

PathContents
/proc/riptides/certificatesLoaded certificates as JSON: type, subject, issuer, serial, SANs, expiry, chain length, trust status
/proc/riptides/trust_anchorsTrust anchors currently in force
/proc/riptides/connectionsEvery tracked TCP connection — all of them with trace_all_sockets on — with addresses, process identity, SPIFFE IDs, hostname, ALPN, negotiated TLS version and key-exchange group. riptides_managed marks the ones Riptides handles

connections lists live sockets only — sample it while traffic is flowing.

The module defines kernel tracepoints under the riptides system (/sys/kernel/tracing/events/riptides/): connection open and close, sendmsg and recvmsg, credential injection, and its own structured trace events. The daemon reads its telemetry through them with eBPF.

They are read-only and carry connection metadata — addresses, ports, hostnames, ALPNs, process and workload identities, credential names and IDs. No credential values and no payload. Reading them takes what any tracepoint does: root, or CAP_PERFMON for perf and CAP_BPF with CAP_PERFMON for eBPF.

Normal operation logs connection events and errors, not payload or credential values. Two debug switches go further, and both should stay off in production, since the kernel log is readable by root and usually persisted by journald:

  • intercept_debug writes each decrypted chunk an intercepted connection receives, in hex.
  • Dynamic debug, enabled with dyndbg= at load time or through /sys/kernel/debug/dynamic_debug/control (root only), turns on debug statements that dump HTTP/2 requests after credential injection, and any message on either device that fails to decode, credential updates included.

An application can ask the kernel about one of its own sockets:

#define SOL_RIPTIDES 7891
#define RIPTIDES_TLS_INFO 1

It returns whether policy allowed the connection (riptides_enabled), the mTLS mode, the negotiated ALPN, and the local and peer SPIFFE IDs. It reports on the calling process’s own file descriptor and exposes nothing about other processes’ connections, so it needs no privilege. On a socket Riptides does not handle the kernel answers EOPNOTSUPP (IPv4) or ENOPROTOOPT (IPv6); on one whose handshake has not finished, ENOPROTOOPT. See Kernel Module for a worked example.

The module registers a binary-format handler ahead of the ELF loader whenever it loads, so every execve on the host passes through it. With env_inject off, the default, the handler returns at once; setting it takes effect from the next execve, without a reload. With it on, the handler reads the new program’s environment and adds these variables, each pointing at /sys/module/riptides/certs/ca-certificates.crt, so unmodified programs trust the intercept CA:

SSL_CERT_FILE, CURL_CA_BUNDLE, REQUESTS_CA_BUNDLE, NODE_EXTRA_CA_CERTS, AWS_CA_BUNDLE, GIT_SSL_CAINFO, GRPC_DEFAULT_SSL_ROOTS_FILE_PATH

It injects only once the daemon has published the bundle, and only when that path resolves in the program’s mount namespace, so a container that cannot see the file gets nothing. It adds every missing variable or none. A variable already set to this path is left alone; one set to a different path gets a second entry ahead of it, which most runtimes read first.

A program started with RIPTIDES_NO_ENV_INJECT set to anything but 0 gets nothing injected, and variables an earlier injection left in its environment are disabled. Its children inherit the opt-out.

For review: with env_inject on, this is kernel code parsing input that any unprivileged process controls, its own environment, and it rewrites the argument block of every new program. It resolves one unexported kernel symbol, expand_downwards, through kallsyms to grow that block; without it, injection is limited to space already mapped. env_inject_debug logs each decision, including the program name, to the kernel log.