apache / apache/fluss

Support impersonation via SASL/PLAIN authorization id

Open
#3,837 1 comment 0 reactions 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.