Commit 65a78f4
Add per-user rate limit types and limiter support (#4692)
* Add per-user rate limit CRD types and CEL validation
Add PerUser field to RateLimitConfig and ToolRateLimitConfig so
administrators can configure per-user token bucket rate limits on
MCPServer. Make ToolRateLimitConfig.Shared optional since a tool
entry may now have only a perUser limit.
CEL admission validation enforces that perUser rate limiting
requires authentication (oidcConfig, oidcConfigRef, or
externalAuthConfigRef) at both server-level and per-tool level.
The existing "at least one scope" rule is updated to include
perUser alongside shared and tools.
Add RateLimitConfigValid condition type and reason constants for
use in the operator reconciler (wired in a following commit).
Part of #4550
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add RateLimitConfigValid status condition to reconciler
Validate that per-user rate limiting has authentication enabled at
reconciliation time (defense-in-depth alongside CEL admission).
Set RateLimitConfigValid condition with appropriate reason:
- RateLimitConfigValid when configuration is valid
- PerUserRequiresAuth when perUser is set without auth
- RateLimitNotApplicable when rate limiting is not configured
Part of #4550
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Support per-user buckets in rate limiter
Extend the limiter to create per-user token buckets keyed by userID.
Per-user buckets are stored as deferred specs (bucketSpec) at
construction time and materialized into TokenBucket structs at Allow()
time since the userID is request-scoped. bucket.New() only allocates
a struct (no I/O), so per-request creation is cheap.
All applicable buckets (shared server, shared per-tool, per-user
server, per-user per-tool) are checked atomically via ConsumeAll.
The Lua script's two-phase check-then-consume ensures a per-user
rejection does not drain the shared bucket.
Redis keys follow the RFC format:
- Server per-user: thv:rl:{ns:name}:user:{userID}
- Tool per-user: thv:rl:{ns:name}:user:{userID}:tool:{toolName}
Part of #4550
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix struct field alignment from linter
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address review feedback on limiter
- Use pointer for optional perUserSpec (clearer than bool+value)
- Use distinct key prefix "user-tool:" for per-tool per-user buckets
to prevent key collisions when userID contains delimiter characters
- Extract shared validateBucketCRD helper to deduplicate validation
between newBucket and newBucketSpec
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Regenerate swagger docs for perUser rate limit fields
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Regenerate CRD API reference docs for perUser fields
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address review feedback from JAORMX
Must fix:
- Replace context.Background() with t.Context() in all limiter tests
(new and pre-existing)
- Fix RateLimitBucket swagger description by trimming field-level
comments so the shared type gets a neutral description
- Add comment in Allow() documenting RFC key format deviation and why
"user-tool:" prefix prevents cross-type key collisions
Should fix:
- Change condition to ConditionTrue with NotApplicable when rate
limiting is not configured (matches ImageValidated/Skipped pattern)
- Add defense-in-depth comment explaining reconciliation continues
intentionally (CEL is the primary gate)
- Add Redis memory sizing note on PerUser CRD field
Nits:
- Fix test object names to use K8s-valid slugs (no spaces)
- Keep nolint:lll on ToolRateLimitConfig (kubebuilder marker is 146
chars, exceeds the 130 limit)
- Improve bucketSpec comment noting state lives in Redis
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>1 parent 967257e commit 65a78f4
12 files changed
Lines changed: 683 additions & 59 deletions
File tree
- cmd/thv-operator
- api/v1alpha1
- controllers
- deploy/charts/operator-crds
- files/crds
- templates
- docs
- operator
- server
- pkg/ratelimit
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
172 | 172 | | |
173 | 173 | | |
174 | 174 | | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
175 | 187 | | |
176 | 188 | | |
177 | 189 | | |
| |||
180 | 192 | | |
181 | 193 | | |
182 | 194 | | |
| 195 | + | |
| 196 | + | |
183 | 197 | | |
184 | 198 | | |
185 | 199 | | |
| |||
519 | 533 | | |
520 | 534 | | |
521 | 535 | | |
522 | | - | |
| 536 | + | |
523 | 537 | | |
524 | | - | |
| 538 | + | |
525 | 539 | | |
526 | 540 | | |
527 | 541 | | |
528 | | - | |
| 542 | + | |
529 | 543 | | |
530 | 544 | | |
531 | 545 | | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
532 | 553 | | |
533 | 554 | | |
534 | 555 | | |
| |||
538 | 559 | | |
539 | 560 | | |
540 | 561 | | |
541 | | - | |
| 562 | + | |
| 563 | + | |
542 | 564 | | |
543 | 565 | | |
544 | 566 | | |
| |||
555 | 577 | | |
556 | 578 | | |
557 | 579 | | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
558 | 585 | | |
559 | 586 | | |
560 | 587 | | |
561 | 588 | | |
562 | 589 | | |
563 | 590 | | |
564 | | - | |
565 | | - | |
566 | | - | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
567 | 598 | | |
568 | 599 | | |
569 | 600 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
222 | 222 | | |
223 | 223 | | |
224 | 224 | | |
225 | | - | |
| 225 | + | |
226 | 226 | | |
227 | 227 | | |
| 228 | + | |
228 | 229 | | |
229 | 230 | | |
230 | 231 | | |
| |||
2407 | 2408 | | |
2408 | 2409 | | |
2409 | 2410 | | |
| 2411 | + | |
| 2412 | + | |
| 2413 | + | |
| 2414 | + | |
| 2415 | + | |
| 2416 | + | |
| 2417 | + | |
| 2418 | + | |
| 2419 | + | |
| 2420 | + | |
| 2421 | + | |
| 2422 | + | |
| 2423 | + | |
| 2424 | + | |
| 2425 | + | |
| 2426 | + | |
| 2427 | + | |
| 2428 | + | |
| 2429 | + | |
| 2430 | + | |
| 2431 | + | |
| 2432 | + | |
| 2433 | + | |
| 2434 | + | |
| 2435 | + | |
| 2436 | + | |
| 2437 | + | |
| 2438 | + | |
| 2439 | + | |
| 2440 | + | |
| 2441 | + | |
| 2442 | + | |
| 2443 | + | |
| 2444 | + | |
| 2445 | + | |
| 2446 | + | |
| 2447 | + | |
| 2448 | + | |
| 2449 | + | |
| 2450 | + | |
| 2451 | + | |
| 2452 | + | |
| 2453 | + | |
| 2454 | + | |
| 2455 | + | |
| 2456 | + | |
| 2457 | + | |
| 2458 | + | |
| 2459 | + | |
| 2460 | + | |
| 2461 | + | |
| 2462 | + | |
| 2463 | + | |
| 2464 | + | |
| 2465 | + | |
2410 | 2466 | | |
2411 | 2467 | | |
2412 | 2468 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
979 | 979 | | |
980 | 980 | | |
981 | 981 | | |
| 982 | + | |
| 983 | + | |
| 984 | + | |
| 985 | + | |
| 986 | + | |
| 987 | + | |
| 988 | + | |
| 989 | + | |
| 990 | + | |
| 991 | + | |
| 992 | + | |
| 993 | + | |
| 994 | + | |
| 995 | + | |
| 996 | + | |
| 997 | + | |
| 998 | + | |
| 999 | + | |
| 1000 | + | |
| 1001 | + | |
| 1002 | + | |
| 1003 | + | |
| 1004 | + | |
| 1005 | + | |
| 1006 | + | |
| 1007 | + | |
| 1008 | + | |
| 1009 | + | |
| 1010 | + | |
| 1011 | + | |
| 1012 | + | |
| 1013 | + | |
| 1014 | + | |
| 1015 | + | |
| 1016 | + | |
| 1017 | + | |
| 1018 | + | |
| 1019 | + | |
| 1020 | + | |
| 1021 | + | |
| 1022 | + | |
| 1023 | + | |
| 1024 | + | |
| 1025 | + | |
| 1026 | + | |
| 1027 | + | |
| 1028 | + | |
| 1029 | + | |
| 1030 | + | |
| 1031 | + | |
| 1032 | + | |
| 1033 | + | |
| 1034 | + | |
| 1035 | + | |
| 1036 | + | |
| 1037 | + | |
| 1038 | + | |
| 1039 | + | |
| 1040 | + | |
| 1041 | + | |
| 1042 | + | |
| 1043 | + | |
| 1044 | + | |
| 1045 | + | |
| 1046 | + | |
| 1047 | + | |
| 1048 | + | |
| 1049 | + | |
| 1050 | + | |
| 1051 | + | |
| 1052 | + | |
| 1053 | + | |
| 1054 | + | |
| 1055 | + | |
| 1056 | + | |
| 1057 | + | |
| 1058 | + | |
| 1059 | + | |
| 1060 | + | |
| 1061 | + | |
| 1062 | + | |
| 1063 | + | |
| 1064 | + | |
| 1065 | + | |
| 1066 | + | |
| 1067 | + | |
| 1068 | + | |
| 1069 | + | |
| 1070 | + | |
| 1071 | + | |
| 1072 | + | |
| 1073 | + | |
| 1074 | + | |
| 1075 | + | |
| 1076 | + | |
| 1077 | + | |
| 1078 | + | |
| 1079 | + | |
| 1080 | + | |
| 1081 | + | |
| 1082 | + | |
| 1083 | + | |
| 1084 | + | |
| 1085 | + | |
| 1086 | + | |
| 1087 | + | |
| 1088 | + | |
| 1089 | + | |
| 1090 | + | |
| 1091 | + | |
| 1092 | + | |
| 1093 | + | |
| 1094 | + | |
| 1095 | + | |
| 1096 | + | |
| 1097 | + | |
| 1098 | + | |
| 1099 | + | |
| 1100 | + | |
| 1101 | + | |
| 1102 | + | |
| 1103 | + | |
| 1104 | + | |
| 1105 | + | |
| 1106 | + | |
| 1107 | + | |
| 1108 | + | |
| 1109 | + | |
| 1110 | + | |
| 1111 | + | |
| 1112 | + | |
| 1113 | + | |
| 1114 | + | |
| 1115 | + | |
| 1116 | + | |
| 1117 | + | |
| 1118 | + | |
| 1119 | + | |
| 1120 | + | |
| 1121 | + | |
| 1122 | + | |
| 1123 | + | |
| 1124 | + | |
| 1125 | + | |
| 1126 | + | |
| 1127 | + | |
| 1128 | + | |
| 1129 | + | |
| 1130 | + | |
| 1131 | + | |
| 1132 | + | |
| 1133 | + | |
| 1134 | + | |
0 commit comments