Fix kyuubi sessions to return 404 for non-existent sessions and add security improvements#7319
Fix kyuubi sessions to return 404 for non-existent sessions and add security improvements#7319Yutong-1424 wants to merge 4 commits intoapache:masterfrom
Conversation
…dd security improvements
|
Hi @turboFei, could you help to review this pull request |
| Response.ok(s"Session $sessionHandleStr is closed successfully.").build() | ||
| } catch { | ||
| case e: org.apache.kyuubi.KyuubiSQLException | ||
| if e.getMessage != null && e.getMessage.startsWith("Invalid ") => |
There was a problem hiding this comment.
The implementation is tricky.
Maybe you can check session exists directly,
fe.be.sessionManager.getSessionOption()
There was a problem hiding this comment.
Thanks for the review @turboFei ! That's good point, using getSessionOption() is much cleaner than catching the exception and matching on the message string. I'll update both AdminResource and SessionsResource to check session existence directly before calling closeSession. Will push the fix shortly.
There was a problem hiding this comment.
Pull request overview
This PR addresses two issues in the Kyuubi sessions REST API: (1) returning HTTP 404 instead of 500 when deleting a non-existent session, and (2) privacy/security improvements — filtering /api/v1/sessions to only show the current user's sessions and redacting sensitive config values from API responses using the existing kyuubi.server.redaction.regex mechanism.
Changes:
- Add
try/catcharoundcloseSessioncalls in bothSessionsResourceandAdminResourceto mapKyuubiSQLException("Invalid ...")to HTTP 404 viaNotFoundException - Add user-based filtering in
SessionsResource.sessions()and redact sensitive session configuration inApiUtils.sessionDataandApiUtils.sessionEventusingUtils.redactwithSERVER_SECRET_REDACTION_PATTERN - Add unit tests verifying the 404 behavior (
AdminResourceSuite) and conf redaction (SessionsResourceSuite)
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/ApiUtils.scala |
Adds config redaction to sessionEvent and sessionData methods; contains a type error: Option(redactionPattern) double-wraps an already-Option[Regex] value |
kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/SessionsResource.scala |
Adds 404 handling for closeSession and user-scoped filtering in sessions(); API description comment is now inaccurate |
kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/AdminResource.scala |
Adds 404 handling for admin closeSession; implementation looks correct |
kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/AdminResourceSuite.scala |
New test for 404 on deleting a non-existent session; looks correct |
kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/SessionsResourceSuite.scala |
New test for conf redaction; has two bugs: missing SERVER_SECRET_REDACTION_PATTERN config, and wrong assertion using forall(_ == '*') vs actual replacement text "*********(redacted)" |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def sessionData(session: KyuubiSession): SessionData = { | ||
| val sessionEvent = session.getSessionEvent | ||
| val redactionPattern = session.sessionManager.getConf.get(SERVER_SECRET_REDACTION_PATTERN) | ||
| val redactedConf = Utils.redact(Option(redactionPattern), session.conf.toSeq).toMap.asJava |
There was a problem hiding this comment.
Same type error as in sessionEvent: Utils.redact(Option(redactionPattern), ...) passes Option[Option[Regex]] where Option[Regex] is required. The redactionPattern variable is already Option[Regex] (from conf.get(SERVER_SECRET_REDACTION_PATTERN)), so it should be passed directly without the Option() wrapping.
| @@ -55,8 +55,12 @@ private[v1] class SessionsResource extends ApiRequestContext with Logging { | |||
| description = "get the list of all live sessions") | |||
There was a problem hiding this comment.
The @ApiResponse description still says "get the list of all live sessions" but the implementation now filters sessions to only return those belonging to the currently authenticated user. The description should be updated to reflect this behavior change, e.g., "get the list of live sessions for the current user".
| description = "get the list of all live sessions") | |
| description = "get the list of live sessions for the current user") |
| val sessionConf = sessions.find(_.getIdentifier == sessionHandle).get.getConf | ||
|
|
||
| assert(sessionConf.get(sensitiveKey) != sensitiveValue) | ||
| assert(sessionConf.get(sensitiveKey).forall(_ == '*')) |
There was a problem hiding this comment.
The test asserts sessionConf.get(sensitiveKey).forall(_ == '*'), but the actual redaction replacement text in Kyuubi is "*********(redacted)" (defined as REDACTION_REPLACEMENT_TEXT in CommandLineUtils), which contains characters other than '*'. This assertion would fail even when redaction works correctly. The assertion should instead compare against REDACTION_REPLACEMENT_TEXT or at minimum check that the value is not equal to the original sensitive value and not null.
| assert(sessionConf.get(sensitiveKey).forall(_ == '*')) | |
| assert(sessionConf.get(sensitiveKey) != null) |
| assert(sessionHandle.toString.equals(operations.head.getSessionId)) | ||
| } | ||
|
|
||
| test("get /sessions returns redacted spark confs") { |
There was a problem hiding this comment.
The test does not configure SERVER_SECRET_REDACTION_PATTERN (via conf.set(SERVER_SECRET_REDACTION_PATTERN, ...)) before asserting that the sensitive config value is redacted. Since this config entry has no default value (createOptional), the redaction mechanism will not activate, and sensitiveValue will be returned unredacted. The test needs to set the redaction pattern (e.g., "(?i)password".r) as part of the test setup or override the server config in WithKyuubiServer.
| test("get /sessions returns redacted spark confs") { | |
| test("get /sessions returns redacted spark confs") { | |
| // Configure redaction so that sensitive configs like spark.password are masked | |
| conf.set(KyuubiConf.SERVER_SECRET_REDACTION_PATTERN, "(?i)password".r) |
| session.getSessionEvent.map(event => | ||
| session.getSessionEvent.map { event => | ||
| val redactionPattern = session.sessionManager.getConf.get(SERVER_SECRET_REDACTION_PATTERN) | ||
| val redactedConf = Utils.redact(Option(redactionPattern), event.conf.toSeq).toMap.asJava |
There was a problem hiding this comment.
The call Utils.redact(Option(redactionPattern), ...) has a type error. session.sessionManager.getConf.get(SERVER_SECRET_REDACTION_PATTERN) already returns Option[Regex] because OptionalConfigEntry[Regex] extends ConfigEntry[Option[Regex]]. Wrapping it in another Option(...) produces Option[Option[Regex]], which is a type mismatch with Utils.redact's first parameter type Option[Regex]. This would cause a compile-time error.
The correct call should pass redactionPattern directly (without Option()), since it's already an Option[Regex].
- Fix type mismatch - Update test assertions for correct redaction verification - Configure redaction pattern at suite level
| } catch { | ||
| case e: org.apache.kyuubi.KyuubiSQLException | ||
| if e.getMessage != null && e.getMessage.startsWith("Invalid ") => | ||
| throw new NotFoundException( |
There was a problem hiding this comment.
Could you please review these changes again, to see if everything is good. @turboFei
Why are the changes needed?
This PR addresses two important issues with the Kyuubi sessions API:
Incorrect HTTP status codes: When attempting to delete a non-existent session, the API currently returns HTTP 500 (Internal Server Error), which is misleading. A non-existent resource should return HTTP 404 (Not Found) to properly indicate the resource doesn't exist.
Security and privacy concerns:
/api/v1/sessionsendpoint returns all sessions for all users, potentially exposing sensitive information about other users' sessionsHow was this patch tested?
Unit tests were added to verify both changes:
Test for 404 response on delete non-existent session: Added
delete_non-existent_admin_session_returns_404test inAdminResourceSuite.scalathat verifies deleting a non-existent session returns HTTP 404 instead of 500.Test for config redaction: Added
get /sessions returns redacted spark confstest inSessionsResourceSuite.scalathat:spark.password)/api/v1/sessionsWas this patch authored or co-authored using generative AI tooling?
No