Managed Stream Connector Reads Worker TLS Private Key via Trust-Cert Path
The Risk
Any project member with permission to create a streaming connector could trick the platform into reading any file on the worker machine and copying the contents into the connector's error log. That included the worker's own private master key, the file used to prove the worker's identity inside the platform's network. Even read-only project members could then read that key through the normal status and logs page. A single normal connector creation, with no exploit chain, exposed credentials that should never have left the vendor's infrastructure.
The Vulnerability
The managed connector service exposed a database-sink connector with configuration fields including db.enable.tls=true and db.trust.certificate.path=<path>. When a connector was created, the worker process opened the path with its effective UID, the certificate parser rejected the bytes (because they were not a certificate), and the raw file contents were copied verbatim into the connector task trace field. That trace was then returned by:
GET /v1/project/{project}/service/{connect-service}/connectors/{name}/statusGET /v1/project/{project}/service/{connect-service}/logs
Both endpoints are legitimately readable by any project member with read_only role. The connector-create endpoint requires services:write, but once one privileged user created the malicious connector, the leaked bytes surfaced inside endpoints the entire read-only project membership could read by design.
The platform had an existing systemd hardening profile with an InaccessiblePaths= list explicitly blocking broker config files, JAAS, authentication user lists, ACL lists, and coordinator config. The connector's certificate-path field bypassed that list entirely, reading anything not on it: the worker TLS private key, the worker's PKCS12 client-credential bundle, internal config, host kernel build info, container topology, and process environment.
The Attack
Primary primitive
A single POST /connectors call with the path field set to the worker's key location triggered the leak. The connector payload that did the work:
{
"name": "poc-key-<timestamp>",
"connector.class": "com.example.sink.StreamSinkConnector",
"tasks.max": "1",
"topics": "poc-key-<timestamp>",
"target.nodes": "127.0.0.1:5555",
"target.bucket": "testbucket",
"db.enable.tls": "true",
"db.enable.hostname.verification": "false",
"db.trust.certificate.path": "/run/<vendor>/keys/service.key"
} Polling the status endpoint until the trace contained BEGIN PRIVATE KEY took a few seconds. The extracted PEM block validated cleanly with openssl pkey -check returning Key is valid. The connector self-deleted after the read, leaving no persistent state.
Cross-role amplification
To prove the read-only blast radius, a second script created a fresh non-super application user, granted only project read_only, and generated an app token. That user received HTTP 403 when trying to create the same connector. An admin then created the vulnerable connector. The read-only user then read both connector status and service logs successfully, retrieving the same private key byte-for-byte (confirmed by matching SHA-256 hashes against the admin-side extraction).
Other paths reflected
The same primitive read many other worker-local paths and reflected their content, each with a fresh single-use connector:
/etc/passwd(full account inventory, including connector, broker, and agent service accounts)/run/<vendor>/keys/public.keystore.p12(the worker's client-credential bundle, a second disclosed credential)/proc/self/environ(process env vars including invocation IDs and cgroup paths)/proc/self/cmdline(JVM args, including an unauthenticated management port on[::]:9998)/proc/self/mountinfo(full container layout and per-tenant overlay mounts)/etc/systemd/system/<connector>.service(the hardening unit file containing the bypassedInaccessiblePaths=list)/etc/hosts(internal hostnames and the project's IPv6 mesh prefix)/proc/version(custom vendor-built kernel version string)
The Impact
The disclosed material included the worker's TLS private key (validated offline as a valid key) and a second credential in the worker's PKCS12 client bundle. These are orchestration-plane credentials with which a read-only project user has no relationship, made readable to that role through the vendor's own API surface.
Beyond credentials, the same primitive exposed the platform's internal hardening profile, container topology, JVM management port configuration, and host kernel build, all useful pivots for further attack against the multi-tenant infrastructure. No data was written; the entire primitive is read-only and self-cleaning, making it suitable for stealthy use against any project the attacker has even basic write access to.
The exposure broke the platform's stated permission model directly: documented as "access granted only to resources the principal has a relationship with", but here a normal connector-write user obtained credential material from the underlying worker host.
Remediation
- Reject arbitrary filesystem paths in customer-controlled certificate config for the managed connector service, or restrict them to an allowlist of customer-uploaded directories.
- Apply the same input-path filter to every local-path field, not just the one tested:
db.trust.certificate.path,db.client.certificate.path, anddb.trust.store.path. - Redact raw parser input from connector status and service logs at the API layer. Any string that resembles file contents should never reach a read-only project role.
- Add regression tests for the certificate-path fields with sensitive paths (
/etc/passwd,/proc/self/environ, anything under the vendor's key directory). - Scope connector status and service logs so that read-only users do not see raw exception traces from connector tasks created by higher-privileged users.
- Rotate the disclosed worker key and the PKCS12 bundle on any host that may have been targeted. Ensure the keystore protection password is per-service, not a shared default.