apache / apache/iggy

fix(connectors): runtime config API returns plugin credentials with auth disabled by default

Open
#3,802 2 comments 0 reactions 0 assignees View on GitHub
bug connectors rust
Dominant language
Rust
Stars
4.9k
Forks
432
Avg merge
2d 10h
Merged PRs (30d)
173

Description

## Summary

The connectors runtime control API returns plugin configuration verbatim, credentials included, and its authentication is disabled by default. The default bind is loopback, so this is a hardening gap rather than a remote disclosure — but nothing warns an operator who moves the listener off loopback, which is a normal thing to do when running the runtime in a container.

Filing publicly rather than to the security list because of the loopback default. Happy to move it if maintainers judge otherwise.

## Configuration returns secrets

`core/connectors/runtime/src/api/source.rs::get_source_plugin_config` serves `source.config.plugin_config` as-is:

```rust
let (content_type, config) = map_connector_config(config, format)?;
```

`plugin_config` is the raw `serde_json::Value` parsed from TOML, so every credential an operator configured comes back in the clear. `GET /sources/{key}/configs` and `/configs/{version}` and `/configs/active` return the enclosing `SourceConfig`, which carries the same field. The sink routes mirror all of it. A repo-wide grep finds no redaction anywhere in `core/connectors/runtime/src` — the only `redact` hits are log-line truncation in `stream.rs`.

For most connectors that means a database connection string. For `iggy_connector_http_source` (#3798) it also means a `management_token`, which mints webhook endpoints, and each endpoint's HMAC secret.

## Authentication is off by default

`core/connectors/runtime/src/api/auth.rs::resolve_api_key`:

```rust
if context.api_key.expose_secret().is_empty() {
return Ok(next.run(request).await);
}
```

`core/connectors/runtime/config.toml` ships:

```toml
address = "127.0.0.1:8081"
api_key = "" # Optional API key for authentication to be passed as `api-key` header
```

So out of the box the API is unauthenticated. Loopback confines that to local processes, which is a defensible posture for an admin API — the gap is that the two defaults compose into "any local process can read every connector credential", and an operator who changes `address` to `0.0.0.0` to reach the API from outside a container gets an unauthenticated endpoint serving credentials with no warning at any layer.

## Suggested fixes, roughly in order of value

1. Redact credential-bearing fields in the config responses, or gate those specific routes behind a configured `api_key` regardless of the global default.
2. Log a warning at startup when `api_key` is empty and `address` is not loopback.
3. Document in the runtime README that the control API returns credentials in plaintext and should be treated as privileged.

Related: #3801, which is why the plugin-side `SecretString` annotations do not help here.

Found while preparing #3798, whose README now documents this exposure for its own users.

Contributor guide

Open the contributing guide

Research direction

Start with core/connectors/runtime/src/api/source.rs::get_source_plugin_config and trace the GET /sources/{key}/configs routes, then inspect api/auth.rs::resolve_api_key and core/connectors/runtime/config.toml. Review the runtime README and related issues #3801 and #3798 before choosing the security boundary. Done means credential exposure and the unauthenticated non-loopback configuration are addressed, with operator guidance where needed.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
api, backend, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.