Skip to content

server: restrict GlobalConfig to its namespace - #11076

Open
rleungx wants to merge 6 commits into
tikv:masterfrom
rleungx:codex/fix-global-config-path-master
Open

server: restrict GlobalConfig to its namespace#11076
rleungx wants to merge 6 commits into
tikv:masterfrom
rleungx:codex/fix-global-config-path-master

Conversation

@rleungx

@rleungx rleungx commented Jul 31, 2026

Copy link
Copy Markdown
Member

What problem does this PR solve?

Issue Number: close #11079

The GlobalConfig gRPC endpoints trust the client-provided ConfigPath and use it as an etcd key or prefix. A caller that can access PD gRPC can therefore make StoreGlobalConfig, LoadGlobalConfig, or WatchGlobalConfig operate outside the intended global-config namespace. Using path.Join for etcd keys also interprets otherwise literal Name components such as .., which changes the requested key.

What is changed and how does it work?

  • Map an empty ConfigPath and /global/config to /global/config/.
  • Allow other ConfigPath values only when their literal byte prefix is /global/config/ or the legacy cloud-storage-engine prefix resource_group/controller; etcd treats ., .., duplicate separators, and backslashes as ordinary key bytes.
  • Build ConfigPath/Name by string concatenation instead of path.Join. An empty Name continues to address ConfigPath itself, so the default path maps it to /global/config.
  • Apply the /global/config/ authorization boundary to Store, Load, and Watch.
  • Preserve cloud-storage-engine compatibility by applying its resource_group/controller prefix to Store, Load, and Watch through the same validation path.
  • Add integration coverage for literal etcd path/name semantics, invalid paths, empty names, and resource-controller Store, Load, and Watch operations.

Existing TiDB and TiCDC callers use an empty path and remain compatible. cloud-storage-engine and tikv-one retain their existing resource_group/controller prefix Load behavior.

Check List

Tests

  • Integration test:
    • go test -tags without_dashboard ./client -run '^TestGlobalConfigTestSuite$' -count=1

Side effects

  • Breaking backward compatibility: arbitrary ConfigPath values outside /global/config/ and resource_group/controller now receive InvalidArgument.
  • Path-like non-empty names are treated as literal etcd key bytes instead of being normalized by path.Join.

Related changes

Release note

Restrict GlobalConfig gRPC access to authorized etcd keys while preserving existing TiDB, TiCDC, and resource-controller callers.

@ti-chi-bot ti-chi-bot Bot added do-not-merge/needs-linked-issue release-note Denotes a PR that will be considered when it comes time to generate release notes. dco-signoff: yes Indicates the PR's author has signed the dco. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Jul 31, 2026
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Global configuration store, load, and watch requests now validate paths and names. Valid paths normalize to globalConfigPath. Invalid requests return gRPC InvalidArgument errors. Integration tests cover invalid inputs, nested paths, compatible names, resource-group controller handling, and cleanup.

Changes

Global configuration validation

Layer / File(s) Summary
Validate global configuration requests
server/grpc_service.go
Store, load, and watch operations validate and normalize configuration paths. Store and load operations resolve configuration names before backend operations. Resource-group controller loads use exact-key reads. Invalid inputs return InvalidArgument.
Verify validation and path behavior
tests/integrations/client/global_config_test.go
Integration tests assert InvalidArgument for invalid paths and names, verify literal and nested path behavior, resource-group controller handling, compatible names, sibling isolation, and updated cleanup behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main change: restricting GlobalConfig operations to its namespace.
Description check ✅ Passed The description covers the problem, implementation, tests, compatibility impact, related backport, and release note.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@rleungx
rleungx force-pushed the codex/fix-global-config-path-master branch from ab9950f to ef3c52b Compare July 31, 2026 04:26
@rleungx rleungx changed the title server: restrict GlobalConfig to the default namespace server: restrict GlobalConfig to its namespace Jul 31, 2026
@rleungx
rleungx force-pushed the codex/fix-global-config-path-master branch from ef3c52b to 3170cdd Compare July 31, 2026 04:52
@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.55172% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 79.32%. Comparing base (070828c) to head (2d2943d).
⚠️ Report is 4 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #11076      +/-   ##
==========================================
+ Coverage   79.23%   79.32%   +0.08%     
==========================================
  Files         540      541       +1     
  Lines       76010    76504     +494     
==========================================
+ Hits        60226    60686     +460     
- Misses      11533    11542       +9     
- Partials     4251     4276      +25     
Flag Coverage Δ
unittests 79.32% <96.55%> (+0.08%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Signed-off-by: Ryan Leung <rleungx@gmail.com>
@rleungx
rleungx force-pushed the codex/fix-global-config-path-master branch from 3170cdd to 60f6b19 Compare July 31, 2026 05:27
@ti-chi-bot ti-chi-bot Bot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Jul 31, 2026
Signed-off-by: Ryan Leung <rleungx@gmail.com>
@rleungx
rleungx force-pushed the codex/fix-global-config-path-master branch from 3fc90a5 to 9823c66 Compare July 31, 2026 07:09
rleungx added 2 commits July 31, 2026 16:24
Signed-off-by: Ryan Leung <rleungx@gmail.com>
Signed-off-by: Ryan Leung <rleungx@gmail.com>
Signed-off-by: Ryan Leung <rleungx@gmail.com>
@ti-chi-bot ti-chi-bot Bot added needs-cherry-pick-release-8.5 Should cherry pick this PR to release-8.5 branch. and removed do-not-merge/needs-triage-completed labels Jul 31, 2026
@rleungx

rleungx commented Jul 31, 2026

Copy link
Copy Markdown
Member Author

/retest

Comment thread server/grpc_service.go Outdated
return globalConfigPath, nil
}
if !strings.HasPrefix(configPath, globalConfigPath) &&
!strings.HasPrefix(configPath, resourceGroupControllerPath) {

@lhy1024 lhy1024 Jul 31, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[blocking] The current check conflates two separate concerns: which client-provided ConfigPath values are authorized, and whether an exact controller Load should retain the legacy WithPrefix() range behavior.

As written, it accepts resource_group/controller-other and resource_group/controllers as direct request paths. With an empty item name, Store/Delete can therefore mutate that exact sibling key.

I checked current cloud-storage-engine, its history, TiDB/tidb-cse, and tidbcloud/tikv-one. The observed compatibility requirement is LoadGlobalConfig with Names == nil and the exact resource_group/controller path. I found no controller Store, named Load, Watch, descendant-path, or controller-* request caller.

I am not necessarily asking to remove WithPrefix() from an exact controller Load; that legacy result behavior can be preserved separately if it is part of the supported API contract. The blocking question is why arbitrary raw-prefix values are accepted as direct ConfigPath values, and why the exception is applied to Store, named Load, and Watch. Unless there are concrete callers for those operations, please scope the exception to the exact controller Load and reject direct sibling paths.

Comment thread server/grpc_service.go Outdated
// `globalConfigPath`.
globalConfigPath = "/global/config/"
// cloud-storage-engine stores its resource controller configs under this
// prefix through the GlobalConfig API.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This compatibility comment does not match the actual consumer: cloud-storage-engine only loads the exact resource_group/controller key; it does not store controller configs through GlobalConfig.

Also, are we still expected to maintain tidbcloud/tikv-one as a supported compatibility target? Its master has not moved since 2024-12-06 and the repository is not archived. If old deployed binaries remain supported, could we document the owner and supported versions? Either way, its code has the same exact-Load-only behavior as cloud-storage-engine and does not justify broader Store/Watch or sibling-prefix access.

Comment thread server/grpc_service.go Outdated
globalConfigPath = "/global/config/"
// cloud-storage-engine stores its resource controller configs under this
// prefix through the GlobalConfig API.
resourceGroupControllerPath = "resource_group/controller"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Please reuse keypath.ControllerConfigPath() instead of introducing another copy of this storage key. It is already the canonical path used by SaveControllerConfig, LoadControllerConfig, and the resource-group metadata watcher.

Kind: pdpb.EventType_PUT,
Name: siblingKey,
Payload: []byte("other"),
}}, res.Items)

@lhy1024 lhy1024 Jul 31, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you clarify whether returning raw-prefix siblings from an exact resource_group/controller Load is an intentionally supported API compatibility contract?

The old implementation did use WithPrefix(), so retaining that range behavior may be reasonable independently of current callers. However, both cloud-storage-engine and tikv-one select only configs.get("resource_group/controller") and do not consume sibling entries.

If preserving the generic prefix-query semantics is intentional, this assertion is useful, but please document that contract. Otherwise, an exact-item assertion would match all observed consumers. This question is separate from whether a sibling value should be accepted directly as ConfigPath.

Payload: []byte("1"),
}},
})
re.NoError(err)

@lhy1024 lhy1024 Jul 31, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These operations were possible before this PR because GlobalConfig accepted arbitrary ConfigPath values, so the question here is which legacy behavior should remain in the new allowlist.

I found no inspected caller that stores controller configuration or performs a named Load under this path through GlobalConfig. Do we intentionally consider controller Store and named Load part of the supported compatibility contract? If so, please identify or document that caller. Otherwise, could this section assert InvalidArgument for those operations while keeping only the exact Names == nil Load exception?

err = suite.server.WatchGlobalConfig(&pdpb.WatchGlobalConfigRequest{
ConfigPath: resourceGroupControllerPath,
}, testReceiver{re: re, ctx: watchCtx})
re.NotEqual(codes.InvalidArgument, status.Code(err))

@lhy1024 lhy1024 Jul 31, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the purpose here is only to verify that the controller path is authorized for Watch, canceling the context first is a practical way to avoid blocking. However, this does not establish a watch or verify any event, so it provides no coverage for the actual Watch behavior.

Do we intentionally need to retain controller-path Watch support? I found no such caller in the inspected consumers. If it is supported, could we either add a live put/watch assertion or make the validation-only intent explicit in the test name/comment? If it is not required, this should instead expect InvalidArgument.

Signed-off-by: Ryan Leung <rleungx@gmail.com>
@rleungx

rleungx commented Jul 31, 2026

Copy link
Copy Markdown
Member Author

/retest

@lhy1024

lhy1024 commented Jul 31, 2026

Copy link
Copy Markdown
Member

The PR body appears to be stale after 2d2943d8 and currently describes a broader compatibility exception than the implementation.

The current code:

  • restricts Store, Watch, and named Load requests to /global/config/;
  • permits resource_group/controller only for an exact LoadGlobalConfig request with Names == nil;
  • preserves the legacy WithPrefix() result behavior only for that exact Load;
  • rejects controller Store, named Load, Watch, sibling paths, and descendant paths.

Could you update the What is changed, test coverage, and Side effects sections accordingly? In particular, the statements saying that the controller prefix is allowed for Store/Load/Watch and that cloud-storage-engine stores controller configs through GlobalConfig no longer match the code.

@ti-chi-bot ti-chi-bot Bot added the needs-1-more-lgtm Indicates a PR needs 1 more LGTM. label Jul 31, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: lhy1024

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot

ti-chi-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

[LGTM Timeline notifier]

Timeline:

  • 2026-07-31 12:08:29.273412247 +0000 UTC m=+2184295.309507313: ☑️ agreed by lhy1024.

@ti-chi-bot ti-chi-bot Bot added the approved label Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved dco-signoff: yes Indicates the PR's author has signed the dco. needs-1-more-lgtm Indicates a PR needs 1 more LGTM. needs-cherry-pick-release-8.5 Should cherry pick this PR to release-8.5 branch. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GlobalConfig APIs can access etcd keys outside the intended paths

2 participants