Support impersonation via SASL/PLAIN authorization id
- Dominant language
- Java
- Stars
- 2.1k
- Forks
- 625
- Avg merge
- 3d 14h
- Merged PRs (30d)
- 97
Description
### Search before asking
- [x] I searched in the [issues](https://github.com/apache/fluss/issues) and found nothing similar.
### Motivation
Middle-tier services (gateways, web consoles, query services) often connect to Fluss with their own service credentials but need to run operations on behalf of end users, so that ACLs are evaluated against the end user rather than the service account. This "impersonation" (a.k.a. doAs / proxy user) capability is standard in comparable systems: Hadoop proxy user, Hive doAs, Pulsar originalPrincipal, Trino impersonation.
The SASL/PLAIN protocol ([RFC 4616](https://datatracker.ietf.org/doc/html/rfc4616)) already carries this semantic natively via the optional authorization id:
```
message = [authzid] UTF8NUL authcid UTF8NUL passwd
```
Fluss currently parses the authorization id in `PlainSaslServer.evaluateResponse()` but unconditionally rejects any value different from the authenticated username, and the client hard-codes it to `null` in `SaslServerFactory.createSaslClient()`. The rest of the plumbing is already in place: `SaslServerAuthenticator.createPrincipal()` builds the principal from `saslServer.getAuthorizationID()`.
### Solution
Enable the RFC 4616 authorization id end-to-end, gated by an explicit server-side allowlist. No wire protocol change, no new SPI.
**Client:** new config option `client.security.sasl.authorization-id` (String, no default). When set, it is passed to `Sasl.createSaslClient(...)` and the JDK `PlainClient` encodes it per RFC 4616. When unset, the bytes on the wire are identical to today.
**Server:** allow `authzid != authcid` only if granted via a JAAS option on `PlainLoginModule`, consistent with the existing `user_` style:
```yaml
security.sasl.plain.jaas.config: |
org.apache.fluss.security.auth.sasl.plain.PlainLoginModule required
user_admin="admin-pass"
user_gateway="gw-pass"
user_alice="alice-pass"
impersonate_admin="*" # admin may impersonate any user
impersonate_gateway="alice,bob"; # gateway may only impersonate alice/bob
```
* If the check passes, the effective principal becomes the authorization id, and an INFO log records the impersonation for auditing. All downstream ACL checks apply to the effective principal, matching the existing `createPrincipal()` behavior.
* If `impersonate_` is not configured, the request is rejected exactly as today — fully backward compatible, and impersonation cannot be enabled from the client side alone.
* The impersonator must still present its own valid password; the authorization id does not weaken authentication. Docs will keep the existing recommendation to use TLS with SASL/PLAIN.
Out of scope (possible follow-ups, would need a FIP): exposing original/effective principal on `Session`, an `IMPERSONATE` ACL operation type, and impersonation for future non-PLAIN mechanisms.
### Anything else?
_No response_
### Willingness to contribute
- [x] I'm willing to submit a PR!
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with PlainSaslServer.evaluateResponse(), SaslServerFactory.createSaslClient(), SaslServerAuthenticator.createPrincipal(), and PlainLoginModule to trace the existing authorization-id flow. Verify how the new client option and server-side impersonate_ allowlist should pass through authentication. Done means authorized impersonation uses the effective authorization id, unauthorized requests remain rejected, and unset client configuration preserves current wire behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- authentication, authorization, security
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100