Skip to content

libsql-server: enforce namespace authorization on gRPC replication RPCs - #28

Closed
bartzon wants to merge 1 commit into
libsql-rs-v0.9.30-releasefrom
replication-namespace-authz
Closed

libsql-server: enforce namespace authorization on gRPC replication RPCs#28
bartzon wants to merge 1 commit into
libsql-rs-v0.9.30-releasefrom
replication-namespace-authz

Conversation

@bartzon

@bartzon bartzon commented Sep 7, 2026

Copy link
Copy Markdown

Problem

The gRPC replication path in libsql-server validated the JWT signature but never checked the token's namespace claim against the namespace being served:

  • rpc/replication/auth.rs called auth.authenticate(context)? and discarded the returned Authenticated.
  • replication_log.rs (Hello/LogEntries/BatchLogEntries/Snapshot) then called self.namespaces.with(...) directly, so nothing compared p.ro.ns against the namespace being replicated.

Consequence: any valid replication token — including one a legitimate client holds for its own namespace — could replicate any namespace by connecting to that namespace's host. This is the cross-namespace authorization gap behind shop/issues-retail#34480.

Fix

In rpc/replication/auth.rs, use the returned Authenticated and call is_namespace_authorized(&namespace), returning permission_denied on mismatch. All four replication RPCs flow through authenticate(), so this covers the whole path.

is_namespace_authorized on Legacy{ namespace: None } returns true, so legacy/no-namespace tokens (e.g. the CloudSync streamer) still replicate every namespace — no regression for existing server-side callers.

Test

Adds replicate_cross_namespace_denied in tests/embedded_replica/mod.rs:

  • ns1-scoped token replicates ns1 (control)
  • ns1-scoped token replicating ns2 is denied (the bug)
  • token scoped to both namespaces replicates both (control)

Upstream's replicate_auth only asserts token-present vs token-absent; this is the missing negative cross-namespace test.

Verification

  • cargo check -p libsql-server and --tests pass clean.
  • The new test run requires Linux/CI (libsql-server tests don't link on macOS/arm64) — verified compile-only locally.

The replication path validated the JWT signature but discarded the
returned Authenticated, so any valid token could replicate any
namespace regardless of its ns claim. Check is_namespace_authorized
against the served namespace and add a negative cross-namespace test.
@bartzon bartzon self-assigned this Sep 7, 2026
@bartzon

bartzon commented Sep 7, 2026

Copy link
Copy Markdown
Author

Closing in favor of router-layer enforcement. Per team direction (Rafał), we want to avoid further forking libsql — the namespace-claim check will live in retail-libsql-router (Shopify/libsql-infrastructure), which sees all public and internal traffic anyway. This branch is kept as a reference for the negative cross-namespace test shape.

@bartzon bartzon closed this Sep 7, 2026
@bartzon
bartzon deleted the replication-namespace-authz branch September 7, 2026 11:12
@bartzon
bartzon restored the replication-namespace-authz branch September 7, 2026 13:47
@bartzon
bartzon deleted the replication-namespace-authz branch September 7, 2026 13:59
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.

1 participant