Skip to content

feat(auth): emit OCSF logon events for accepted gateway credentials - #4761

Merged
krishicks merged 1 commit into
mainfrom
feat/2912-authn-logon-events/krishicks
Oct 20, 2026
Merged

krishicks merged 1 commit into
mainfrom
feat/2912-authn-logon-events/krishicks

Conversation

@krishicks

@krishicks krishicks commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

When a gateway replica accepts a credential for the first time, it now emits an Informational OCSF Authentication event with status Success. The caller's request proceeds exactly as before. The event records:

  • Who: the principal (user, mTLS operator, sandbox, or gateway peer), with the same user types as authorization denials.
  • How: the authentication protocol (OpenID, mtls, sandbox_jwt, compute_driver, or k8s_service_account).
  • Where: the gRPC method of the request that logged on.
  • Correlation: the caller's address and request ID, the same fields the failure and denial events carry.

Every gRPC request authenticates, and an idle sandbox's supervisor polls the gateway several times a minute, so an event per request would bury user logons. Each credential therefore logs on once: later requests with the same bearer token or client certificate emit nothing until the credential has been in use for an hour, when it logs on again.

Requests admitted by allow_unauthenticated_users, or by a gateway with no authentication configured, emit no success event, since no credential was verified.

Related Issue

Refs #2912

This is the fifth of several PRs for #2912 and does not close it. Success events are on by default with no setting to turn them off, so the criterion "Authentication success events can be enabled or disabled independently through documented operator configuration" stays open on purpose: we'd like to see real volumes before adding the knob.

Changes

  • auth::audit::Logons. A record of credentials that have recently logged on, held on ServerState and shared by every connection to the replica. It keys on a SHA-256 digest (through openshell-crypto) of the principal and the credential that authenticated it: the parsed bearer token, or the fingerprint of the verified client certificate for operators and mTLS users. Headers the authenticator ignored, such as an Authorization: Basic header on an mTLS connection or a differently cased Bearer scheme, therefore cannot force new logons, and a renewed certificate with the same subject logs on again. Digests stay in memory, and a digest failure logs the credential on rather than deduplicating it. It holds up to 16,384 credentials. Only a new credential evicts the oldest early, so a full record of credentials still in use records nothing more.
  • AuthGrpcRouter calls audit::emit_logon once it has established the principal, before authorization runs, so a caller that authenticates and is then refused records both the logon and the denial. Health and reflection requests skip it, as they skip authentication.
  • Docs. ocsf-json-export.mdx describes the event, when a credential logs on again, and that consumers selecting rejected requests should filter on status_id: 2 rather than class_uid alone. logging.mdx updates the AUTH: row and adds a shorthand example.

Events never include presented credentials or request headers other than the canonical request ID.

Testing

  • cargo nextest run -p openshell-server -p openshell-ocsf: all 2,604 tests pass.
  • cargo clippy --workspace --all-targets -- -D warnings
  • Unit tests added or updated:
    • Repeated requests with one bearer token emit a single schema-valid logon carrying the principal, protocol, source address and request ID, and no credential canary. Health requests emit nothing.
    • A refreshed token logs on again; returning to the first token within the hour does not.
    • mTLS users and operators log on once per certificate, across connections, and again with a renewed certificate for the same subject.
    • Varying an unverified Authorization header on an mTLS connection, or the case of the Bearer scheme, does not make a new logon.
    • Gateway peer and compute-driver logons name k8s_service_account and compute_driver.
    • Two connections sharing the replica's record emit one logon.
    • The local-dev unauthenticated user emits nothing.
    • A credential logs on again after the hour. When the record is full, reusing the credentials it holds records nothing, and a new credential evicts the oldest.
    • Existing authorization-denial tests now ignore the logon event.
  • E2E tests added or updated: not applicable.

Follow-ups

  • An operator setting for success events, once volumes are known.
  • Remaining authentication paths: SSH tokens and IssueSandboxToken, with the checks inside IssueSandboxToken, RefreshSandboxToken, and GetCurrentUser.
  • Source address of the edge tunnel's own connection on tunneled requests.

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Docs updated

@github-actions

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

Compatibility review (advisory)

New or updated breaking changes; see the full report for all findings.

1. api-behavior — No explicit risk acceptance found

class_uid: 3002 and AUTH:LOGON change from failure-only records to a mix of rejected and successful authentication records.

  • Impact: SIEM rules and dashboards using the documented class or shorthand selector for rejected requests will count first successful credential uses as failures.
  • Action: Keep successful Authentication records opt-in and disabled by default for v0.1.6, or move the change to a minor train with migration guidance to filter failures by status_id.
  • PR: feat(auth): emit OCSF logon events for accepted gateway credentials #4761
Review details
  • Execution: complete
  • Candidate: PR #4761 (a5e2d021e8c20e66aa9abd68c43fb03ea0273ae8)
  • Baseline: ea265a2f9a86e6fc1911041fec4b5fdcefd674dd (ea265a2f9a86e6fc1911041fec4b5fdcefd674dd)
  • Reviewed: rust-sdk, python-sdk, go-sdk, typescript-sdk, api-behavior, cli, configuration, policy, helm, state

Advisory review; publication gates are unchanged.

Branch check run

@krishicks

Copy link
Copy Markdown
Collaborator Author

Compatibility review, finding 1 (api-behavior): I accept this breaking change and its consumer risk. Since v0.1.5, class_uid: 3002 / AUTH:LOGON records have all been rejections. With this PR they also include successful logons, so SIEM rules or dashboards that count every 3002 record as a failure will count first credential uses as failures.

I'm not taking the suggested action (opt-in, disabled by default). The reasons:

  • OCSF already separates the outcomes. Authentication 3002 covers both outcomes by definition. Every failure the gateway has emitted already carries status_id: 2 (Failure), action_id: 2 (Denied) and a status_detail reason, and its shorthand reads DENIED. Logons carry status_id: 1, action_id: 1 and read ALLOWED. Consumers following OCSF already filter on those fields.
  • The exposure is short. Failure-only 3002 events have been public for one release, v0.1.5, which went out today.
  • Default-on is deliberate. We want to see real logon volume before adding an operator setting. That leaves the feat(auth): emit structured gateway authentication and authorization events #2912 configuration criterion open, as the PR description says.

@krishicks
krishicks force-pushed the feat/2912-authn-logon-events/krishicks branch from 0f39228 to 249ca20 Compare October 20, 2026 20:34
@github-actions

Copy link
Copy Markdown

Compatibility review (advisory)

New or updated breaking changes; see the full report for all findings.

1. api-behavior — Risk explicitly accepted

class_uid: 3002 and AUTH:LOGON change from failure-only records to a mix of rejected requests and successful first credential uses.

  • Impact: SIEM rules and dashboards using the documented class or shorthand selector for rejected requests will count successful credential uses as failures.

  • Action: Keep successful Authentication records opt-in and disabled by default for v0.1.6, or defer the default-on behavior to a minor release; the PR author has now explicitly accepted the risk.

  • PR: feat(auth): emit OCSF logon events for accepted gateway credentials #4761

  • Acceptance: comment

Review details
  • Execution: complete
  • Candidate: PR #4761 (23fb95a7ffc3fa6013376c3d367dae2169640565)
  • Baseline: ea265a2f9a86e6fc1911041fec4b5fdcefd674dd (ea265a2f9a86e6fc1911041fec4b5fdcefd674dd)
  • Reviewed: rust-sdk, python-sdk, go-sdk, typescript-sdk, api-behavior, cli, configuration, policy, helm, state

Advisory review; publication gates are unchanged.

Branch check run

TaylorMutch
TaylorMutch previously approved these changes Oct 20, 2026
When a gateway replica accepts a credential for the first time, it now
emits an Informational OCSF Authentication event with status Success.
The caller's request proceeds exactly as before. The event records:

- Who: the principal (user, mTLS operator, sandbox, or gateway peer),
  with the same user types as authorization denials.
- How: the authentication protocol (OpenID, mtls, sandbox_jwt,
  compute_driver, or k8s_service_account).
- Where: the gRPC method of the request that logged on.
- Correlation: the caller's address and request ID, the same fields the
  failure and denial events carry.

Every gRPC request authenticates, and an idle sandbox's supervisor polls
the gateway several times a minute, so an event per request would bury
user logons. Each credential therefore logs on once: later requests with
the same bearer token or client certificate emit nothing until the
credential has been in use for an hour, when it logs on again. The
replica's connections share this record, which holds up to 16,384
credentials and forgets the oldest early when a new one arrives while
full. It keys on a SHA-256
digest of the credential that authenticated the caller (the bearer
token or the client certificate's fingerprint), so headers the
authenticator ignored cannot force new logons, and a renewed certificate
for the same subject logs on again. Digests stay in memory.

Requests admitted by allow_unauthenticated_users, or by a gateway with
no authentication configured, emit no success event, since no credential
was verified.

Refs #2912

Signed-off-by: Kris Hicks <khicks@nvidia.com>
@krishicks
krishicks added this pull request to the merge queue Oct 20, 2026
Merged via the queue into main with commit 915a5d1 Oct 20, 2026
81 of 83 checks passed
@krishicks
krishicks deleted the feat/2912-authn-logon-events/krishicks branch October 20, 2026 21:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants