Skip to content

[9.5] [HTTP/2] Fix KibanaSocket to use session-level socket for HTTP/2 requests (#285344) - #287792

Merged
jeramysoucy merged 1 commit into
elastic:9.5from
jeramysoucy:backport/9.5/pr-285344
Aug 28, 2026
Merged

[9.5] [HTTP/2] Fix KibanaSocket to use session-level socket for HTTP/2 requests (#285344)#287792
jeramysoucy merged 1 commit into
elastic:9.5from
jeramysoucy:backport/9.5/pr-285344

Conversation

@jeramysoucy

Copy link
Copy Markdown
Contributor

Backport

This will backport the following commits from main to 9.5:

Questions ?

Please refer to the Backport tool documentation

…ests (elastic#285344)

## Summary

Fixes the root cause of elastic#258232
— PKI sessions being invalidated after upgrading to Kibana 9.x, where
`server.protocol` silently defaults to `http2` when `ssl.enabled: true`.

This is the companion to elastic#285153, which fixes the symptom in `pki.ts`.
This PR fixes the underlying socket resolution in Core HTTP.

## Problem

For HTTP/2 requests, `Http2ServerRequest.socket` returns a `Proxy` over
the **stream**, not the session's `TLSSocket`. The load-bearing traps
branch on `stream[kSession]`:

```js
// Node.js lib/internal/http2/compat.js
getPrototypeOf(stream) {
  if (stream.session !== undefined) return ReflectGetPrototypeOf(stream.session[kSocket]);
  return ReflectGetPrototypeOf(stream);   // <-- Http2Stream, NOT TLSSocket
}
```

Node clears `stream[kSession]` whenever the stream is destroyed —
RST_STREAM, GOAWAY, client abort, timeout. After that point the proxy
resolves against the `Http2Stream` itself, so every `instanceof
TLSSocket` check in `KibanaSocket` fails and the accessors degrade:

| accessor | value after stream destruction |
|---|---|
| `authorized` | `undefined` |
| `authorizationError` | `undefined` |
| `getPeerCertificate(true)` | `null` |
| `getProtocol()` | `null` |
| `remoteAddress` | `undefined` |

This happens on **live, authorized connections** — the TLS handshake
succeeded and the client certificate is valid, but the stream-level
proxy can no longer report it.

Kibana's own frontend aborts in-flight requests constantly via
`AbortController` (search sessions, unified search, autocomplete). On
HTTP/1.1 an abort closes a dedicated socket; on HTTP/2 it emits
RST_STREAM on a **shared** session mid-flight, so one cancelled stream
degrades the socket view for concurrent requests.

## Fix

Resolve the **session-level** socket (`req.stream.session.socket`) at
`KibanaSocket` construction time instead of using the stream-level
proxy. The session socket resolves against `session[kSocket]`, is a real
`TLSSocket`, and remains stable for the lifetime of the TCP connection.

This is semantically correct: in HTTP/2 the client certificate is a
property of the **connection**, not of an individual stream.

Falls back to `req.socket` when the session socket is unavailable —
either an HTTP/1.1 request (no `stream` property) or a fully destroyed
session (`session.socket` throws `ERR_HTTP2_SOCKET_UNBOUND`).

## Secondary fix

`audit_service.ts` reads `request.socket.remoteAddress` for audit log
client IP enrichment. On destroyed HTTP/2 streams this silently yielded
`undefined`, losing the client IP in audit records for **all** auth
providers. This patch fixes that too, since the session socket carries a
stable `remoteAddress`.

## Testing

Four new unit tests in `socket.test.ts` covering `resolveRawSocket`:
- HTTP/1.1 request (no `stream` property) → returns `req.socket`
- HTTP/2 request → returns the session-level socket
- `stream.session` undefined → falls back to `req.socket`
- `session.socket` throws `ERR_HTTP2_SOCKET_UNBOUND` → falls back to
`req.socket`

```
node scripts/jest src/core/packages/http/router-server-internal/src/socket.test.ts
# 20 passed, 20 total
```

## Relationship to elastic#285153

- **elastic#285153** (`pki.ts`) — defensive guard so an unknowable socket state
(`authorized === undefined`) is not treated as an explicit cert
rejection. Narrow, safe to backport.
- **This PR** (Core HTTP) — removes the condition that produces the
unknowable state in the first place.

Both are worth having. The `pki.ts` guard remains correct
defense-in-depth for the fully-destroyed-session case, where even the
session socket is unavailable.

## Risk

Touches socket resolution for all Core HTTP requests, not just PKI.
Mitigations:
- HTTP/1.1 requests are unaffected — no `stream` property means the
original `req.socket` is returned unchanged.
- The HTTP/2 path only changes *which* socket object is wrapped;
`KibanaSocket`'s own logic is untouched.
- Fallback preserves today's behaviour whenever the session socket
cannot be resolved.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---

## Release note

Fixes an issue where audit log records could omit the client IP address
for requests made over HTTP/2.

---------

Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Co-authored-by: kibanamachine <[email protected]>
(cherry picked from commit d433465)

# Conflicts:
#	x-pack/platform/plugins/shared/security/test/scout_pki_stress/.meta/api/standard.json
@jeramysoucy jeramysoucy added the backport This PR is a backport of another PR label Aug 28, 2026
@jeramysoucy
jeramysoucy enabled auto-merge (squash) August 28, 2026 10:01
@kibanamachine kibanamachine added the reviewer:scout Agentic PR Scout test review label Aug 28, 2026
@kibanamachine
kibanamachine requested review from elena-shostak and removed request for kibanamachine August 28, 2026 10:02
@kibanamachine

Copy link
Copy Markdown
Contributor

💚 Build Succeeded

Metrics [docs]

✅ unchanged

@jeramysoucy
jeramysoucy merged commit eee9878 into elastic:9.5 Aug 28, 2026
91 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport This PR is a backport of another PR reviewer:scout Agentic PR Scout test review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants