feat(providers): Nebius MysteryBox integration#5868
feat(providers): Nebius MysteryBox integration#5868Skarlso merged 20 commits intoexternal-secrets:mainfrom
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThis pull request adds comprehensive support for Nebius Mysterybox as a new secret provider in External Secrets Operator. The implementation includes new CRD types, provider client with IAM token caching, gRPC-based secret retrieval, validation logic, documentation, and tests. Changes
Possibly related PRs
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
69eaa25 to
e96bafe
Compare
There was a problem hiding this comment.
Actionable comments posted: 11
🤖 Fix all issues with AI agents
In `@apis/externalsecrets/v1/secretstore_nebius_types.go`:
- Around line 46-58: The NebiusMysteryboxProvider struct's CAProvider field is
optional but its JSON tag lacks omitempty, causing nil pointers to be serialized
as "caProvider": null; update the CAProvider field tag in the
NebiusMysteryboxProvider definition to `json:"caProvider,omitempty"` (i.e.,
change the tag on the CAProvider *NebiusCAProvider field) so nil values are
omitted during JSON serialization.
- Around line 19-38: Add CRD validation to require at least one credential in
NebiusAuth by adding a kubebuilder marker on the NebiusAuth type (similar to
DopplerAuth): annotate the NebiusAuth struct with a validation directive such as
"// +kubebuilder:validation:MinProperties=1" (or the project's established
marker for "at least one of" constraints) so the CRD schema enforces that at
least one of ServiceAccountCreds or Token is provided; keep the existing fields
ServiceAccountCreds and Token and their json tags unchanged.
In `@config/crds/bases/external-secrets.io_clustersecretstores.yaml`:
- Around line 3716-3787: The auth schema currently allows an empty object
because both serviceAccountCredsSecretRef and tokenSecretRef are optional; add a
CEL validation on the auth object to require exactly one of these fields be set
(i.e., xor). Update the CRD for the auth properties to include a top-level
validation rule (using CEL) that references auth.serviceAccountCredsSecretRef
and auth.tokenSecretRef and enforces (has(serviceAccountCredsSecretRef) !=
has(tokenSecretRef)) so exactly one credential source is provided at validation
time.
In `@config/crds/bases/external-secrets.io_secretstores.yaml`:
- Around line 3716-3787: The auth object currently allows an empty object
because both serviceAccountCredsSecretRef and tokenSecretRef are optional;
update the CRD schema for the auth property to require at least one of these by
adding an anyOf/oneOf constraint (e.g., oneOf with required:
[serviceAccountCredsSecretRef] and required: [tokenSecretRef]) so that auth must
contain either serviceAccountCredsSecretRef or tokenSecretRef (or use oneOf if
they must be mutually exclusive); modify the auth schema block that defines
serviceAccountCredsSecretRef and tokenSecretRef to include this validation.
In `@docs/introduction/stability-support.md`:
- Line 143: Add a missing row for the Nebius Mysterybox provider to the Provider
Stability and Support Level table (the table covering provider stability lines
~65-100) by inserting an entry for "Nebius Mysterybox" with its stability and
maintainer; for example add a row linking to the provider docs and declaring its
stability and maintainer like: Nebius Mysterybox → alpha → `@greenmapc`. Ensure
the display text matches the Feature Support table ("Nebius Mysterybox") and the
link points to the provider docs
(https://external-secrets.io/latest/provider/nebius-mysterybox).
In `@docs/provider/nebius-mysterybox.md`:
- Line 44: The include directive for
'nebius-mysterybox-external-secret-by-key.yaml' is not wrapped in a YAML code
fence, so surround that include line with a triple-backtick code fence
specifying yaml (```yaml ... ```) to match the other includes and ensure the
YAML renders as a formatted code block.
- Line 51: Wrap the include directive {% include
'nebius-mysterybox-external-secret-all.yaml' %} in a fenced code block (triple
backticks with language `yaml`) so the included YAML is rendered/formatted
correctly; locate the line containing the include string and surround it with
```yaml before and ``` after.
In `@providers/v1/nebius/common/sdk/mysterybox/grpc_client.go`:
- Around line 63-77: The code assumes entry.GetData() is non-nil before reading
Payload and can panic; before constructing PayloadEntry and switching on
entry.GetData().Payload (around PayloadEntry, entry.GetData(), and the switch),
add a nil-check for data := entry.GetData(); if data == nil return an error (use
fmt.Errorf with notSupportedPayloadType or a new error indicating missing data
and include key) so you avoid dereferencing nil and provide a clear failure
path; then use data.Payload (and data.GetStringValue()/GetBinaryValue()) inside
the switch.
In `@providers/v1/nebius/mysterybox/provider_test.go`:
- Around line 727-759: The test mutates package-level mysteryboxClientsCacheSize
without restoring it, which can leak state to other tests; update
TestMysteryboxClientsCache_EvictionClosesClient to capture the original
mysteryboxClientsCacheSize at the start, set mysteryboxClientsCacheSize = 1 for
the test, and register a t.Cleanup closure that restores the original value (or
alternatively refactor createOrGetMysteryboxClient/Provider to accept an
injected cache size for the test); ensure references to
mysteryboxClientsCacheSize and the test helper p.createOrGetMysteryboxClient
remain intact so the cleanup reliably restores global state after the test.
- Around line 638-646: The test spawns goroutines that call
p.createOrGetMysteryboxClient and currently use t.Errorf inside each goroutine
which may not be reported; replace that with a thread-safe error collection
(e.g., an errors channel or an atomic/uint32 failure flag) captured by each
goroutine instead of calling t.Errorf, close or read the channel / check the
atomic after wg.Wait() and then call t.Fatalf/t.Errorf from the main test
goroutine if any error was recorded; update the goroutine body that reads from
start and invokes createOrGetMysteryboxClient to send errors into the chosen
collector and ensure the main test checks it after wg.Wait() before finishing.
In `@providers/v1/nebius/mysterybox/secrets_client.go`:
- Around line 63-66: The error message and constant used around json.Marshal in
secrets_client.go are wrong (they reference unmarshal); update the error
handling in the function containing the json.Marshal call (look for the out, err
:= json.Marshal(keyToValue) block) to use a marshal-specific error
constant/message (e.g., errJSONMarshal with "failed to marshal JSON") and
include the original error when returning (wrap or format the err) so the
log/return reflects the actual marshal failure.
🧹 Nitpick comments (13)
providers/v1/nebius/common/sdk/sdk.go (1)
37-43: Simplify the nil check.The
caCertificate != nilcheck is redundant sincelen(nil) == 0in Go for slices.♻️ Suggested simplification
- if caCertificate != nil && len(caCertificate) > 0 { + if len(caCertificate) > 0 { certPool := x509.NewCertPool() if ok := certPool.AppendCertsFromPEM(caCertificate); !ok { return nil, errors.New("failed to append CA certificate. PEM parse error") } tlsCfg.RootCAs = certPool }providers/v1/nebius/common/sdk/mysterybox/fake/fake_client.go (3)
38-47: Inconsistent receiver types between methods.
Close()at line 33 uses a pointer receiver*FakeMysteryboxClient, whileGetSecretandGetSecretByKeyuse value receivers. While this works functionally, it's inconsistent style. Consider using pointer receivers consistently for all methods.♻️ Suggested fix
-func (f FakeMysteryboxClient) GetSecret(_ context.Context, _, secretId, versionId string) (*mysterybox.Payload, error) { +func (f *FakeMysteryboxClient) GetSecret(_ context.Context, _, secretId, versionId string) (*mysterybox.Payload, error) {
49-64: Apply same receiver fix here for consistency.♻️ Suggested fix
-func (f FakeMysteryboxClient) GetSecretByKey(_ context.Context, _, secretID, versionID, key string) (*mysterybox.PayloadEntry, error) { +func (f *FakeMysteryboxClient) GetSecretByKey(_ context.Context, _, secretID, versionID, key string) (*mysterybox.PayloadEntry, error) {
66-74: Consider adding mutex for thread safety in parallel tests.
MysteryboxServiceaccessessecretDatamap without synchronization. If tests using this fake service run in parallel and share the same service instance, this could cause data races.♻️ Suggested fix
+import "sync" + type MysteryboxService struct { + mu sync.RWMutex secretData map[string]map[string][]mysterybox.Entry }Then add lock/unlock calls in
GetSecret,CreateSecret, andCreateNewSecretVersionmethods.providers/v1/nebius/mysterybox/token_service_test.go (2)
119-121: Confusing parameter type inaddSecondsToClockhelper.The parameter
secondhas typetime.Durationbut is multiplied bytime.Second, which is confusing. If a caller passes5 * time.Second, the actual advancement would be 5 seconds × 1 second = 5,000,000,000 nanoseconds² (incorrect). The current usage passes integer literals which work due to implicit conversion, but this is fragile.♻️ Proposed fix - use int64 for clarity
-func addSecondsToClock(clk *clocktesting.FakeClock, second time.Duration) { - clk.SetTime(clk.Now().Add(second * time.Second)) +func addSecondsToClock(clk *clocktesting.FakeClock, seconds int) { + clk.SetTime(clk.Now().Add(time.Duration(seconds) * time.Second)) }
200-203: Consider checking errors in key rotation test.The errors returned by
GetTokenare discarded. While the test focuses on cache key behavior, ignoring errors could hide unexpected failures during test execution.♻️ Proposed fix
- t1, _ := env.TokenCacheService.GetToken(ctx, "api", base, nil) - t2, _ := env.TokenCacheService.GetToken(ctx, "api", rotatedKeyID, nil) - t3, _ := env.TokenCacheService.GetToken(ctx, "api", rotatedPriv, nil) - t4, _ := env.TokenCacheService.GetToken(ctx, "api", rotatedSubject, nil) + t1, err := env.TokenCacheService.GetToken(ctx, "api", base, nil) + tassert.NoError(t, err) + t2, err := env.TokenCacheService.GetToken(ctx, "api", rotatedKeyID, nil) + tassert.NoError(t, err) + t3, err := env.TokenCacheService.GetToken(ctx, "api", rotatedPriv, nil) + tassert.NoError(t, err) + t4, err := env.TokenCacheService.GetToken(ctx, "api", rotatedSubject, nil) + tassert.NoError(t, err)providers/v1/nebius/common/sdk/mysterybox/grpc_client.go (1)
130-134: Consider using named fields for struct initialization.Using positional struct initialization can be fragile if the struct fields are reordered in the future. Named fields improve readability and maintainability.
♻️ Suggested refactor
return &GrpcClient{ - mbox.NewPayloadService(mysteryboxSdk), - mysteryboxSdk, + PayloadService: mbox.NewPayloadService(mysteryboxSdk), + sdk: mysteryboxSdk, }, nilproviders/v1/nebius/mysterybox/validation_test.go (1)
194-206: Minor: Redundant nil check after assertion.The
if err != nilcheck on line 202 is redundant sincetassert.NotNilon line 201 would have already failed the test iferrwas nil. Consider simplifying.♻️ Suggested simplification
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := p.ValidateStore(tt.store) if tt.wantErr == "" { tassert.NoError(t, err, "%s: unexpected error", tt.name) return } - tassert.NotNil(t, err, "%s: expected error containing %q, got nil", tt.name, tt.wantErr) - if err != nil { - tassert.Contains(t, err.Error(), tt.wantErr, "%s: error %q does not contain %q", tt.name, err.Error(), tt.wantErr) - } + tassert.Error(t, err, "%s: expected error containing %q, got nil", tt.name, tt.wantErr) + tassert.Contains(t, err.Error(), tt.wantErr, "%s: error %q does not contain %q", tt.name, err.Error(), tt.wantErr) }) }providers/v1/nebius/common/sdk/iam/grpc_client.go (2)
51-58: Consider including original error details in the returned error.When JSON unmarshalling fails, the original error is only passed to the observer but not included in the returned error. This loses diagnostic information that could help users troubleshoot malformed credentials.
♻️ Suggested improvement
parsedSubjectCreds := &auth.ServiceAccountCredentials{} if err := json.Unmarshal([]byte(subjectCreds), parsedSubjectCreds); err != nil { if t.exchangeTokenObserveCall != nil { t.exchangeTokenObserveCall(err) } - return nil, errors.New(errInvalidSubjectCreds) + return nil, fmt.Errorf("%s: %w", errInvalidSubjectCreds, err) }
86-92: Clarify metrics observation on success path.The observer is called with
err(which may benilon success) before checking iferr != nil. This appears intentional for metrics tracking, but a brief comment would clarify the design choice.📝 Optional: Add clarifying comment
tok, err := tokenExchanger.Exchange(ctx, req) + // Always observe the result for metrics, regardless of success/failure if t.exchangeTokenObserveCall != nil { t.exchangeTokenObserveCall(err) }providers/v1/nebius/mysterybox/provider_test.go (1)
761-779: Non-atomic increment in fakeTokenService.
f.calls++is not atomic, but since this fake is used in single-threaded test contexts, it's acceptable. If concurrent usage is anticipated, consider usingatomic.AddInt32.providers/v1/nebius/mysterybox/token_service.go (1)
88-95: Unsafe type assertion could panic if cache contains unexpected type.The type assertion
value.(*iam.Token)will panic if the cache somehow contains a value of a different type. Use the comma-ok idiom for defensive coding.♻️ Suggested fix
value, ok := c.tokenCache.Get(*cacheKey) if ok { - token := value.(*iam.Token) - tokenExpired := isTokenExpired(token, c.Clock) - if !tokenExpired { - return token.Token, nil + if token, ok := value.(*iam.Token); ok { + if !isTokenExpired(token, c.Clock) { + return token.Token, nil + } } }providers/v1/nebius/mysterybox/provider.go (1)
35-35: Consider a more descriptive import alias.The alias
mysterybox2doesn't convey its purpose. Considermysteryboxsdkor similar to clarify it's the SDK client package.♻️ Suggested improvement
- mysterybox2 "github.com/external-secrets/external-secrets/providers/v1/nebius/common/sdk/mysterybox" + mysteryboxsdk "github.com/external-secrets/external-secrets/providers/v1/nebius/common/sdk/mysterybox"
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Fix all issues with AI agents
In `@deploy/crds/bundle.yaml`:
- Around line 17431-17540: The auth object for nebiusmysterybox currently allows
empty objects; update the schema for both SecretStore and ClusterSecretStore
nebiusmysterybox entries so that auth requires at least one credential reference
by adding an anyOf with two alternatives: one requiring
auth.serviceAccountCredsSecretRef and another requiring auth.tokenSecretRef
(reference the auth property, serviceAccountCredsSecretRef and tokenSecretRef
symbols to locate where to add the anyOf). Ensure the anyOf sits alongside the
existing properties/type/required block so validation fails unless one of those
two fields is present.
In `@pkg/register/nebiusmysterybox.go`:
- Around line 22-25: The build fails because the package imported as mbox
(github.com/external-secrets/external-secrets/providers/v1/nebius/mysterybox) is
covered by a replace but not listed in go.mod's require; add the missing module
requirement by running "go get
github.com/external-secrets/external-secrets/providers/v1/nebius" or manually
add a require directive for
github.com/external-secrets/external-secrets/providers/v1/nebius in go.mod, then
run "go mod tidy" to update dependencies so imports used in
pkg/register/nebiusmysterybox.go (mbox) resolve.
In `@providers/v1/nebius/mysterybox/provider_test.go`:
- Around line 551-564: The test uses the global log variable instead of the
locally-defined logger in several places; update the Provider instances in
TestCreateOrGetMysteryboxClient_CachesByKey (and the other listed tests at lines
noted in the comment) to set the Logger field to the local logger variable
rather than log so the Provider.Logger uses the test-local logger; specifically
replace occurrences where Provider{ Logger: log, ... } is constructed (and any
similar constructions in this file) with Provider{ Logger: logger, ... } while
keeping NewMysteryboxClient and mysteryboxClientsCache unchanged.
In `@providers/v1/nebius/mysterybox/provider.go`:
- Around line 85-91: The nil check on p.TokenService in Provider.NewClient is
racy and redundant because initTokenService already uses sync.Once (tokenOnce);
remove the conditional and always call p.initTokenService() (retaining the
existing error handling) so initialization is delegated to
initTokenService/tokenOnce, or alternatively move the nil check inside the
tokenOnce.Do, referencing Provider.NewClient, Provider.initTokenService,
TokenService and tokenOnce to locate the relevant code.
- Around line 233-251: The initMysteryboxClientsCache function currently
swallows initialization errors because cacheOnce.Do runs only once; capture and
persist the error from lru.NewWithEvict in a package/struct-level variable
(e.g., add a p.mysteryboxCacheInitErr error) that you set inside the
cacheOnce.Do closure when lru.NewWithEvict fails, return that stored error from
initMysteryboxClientsCache and update callers to check it; apply the same
stored-error pattern to initTokenService (and its tokenServiceInitErr) so
subsequent calls correctly report prior initialization failures while still
using cacheOnce for one-time setup.
In `@providers/v1/nebius/mysterybox/token_service.go`:
- Around line 79-103: GetToken on TokenCacheService reads and writes
c.tokenCache without synchronization causing duplicate concurrent token
exchanges; fix by coalescing concurrent requests for the same cacheKey (built by
buildTokenCacheKey) before calling TokenExchanger.NewIamToken: add
synchronization (e.g., a singleflight.Group keyed by the cacheKey string or a
map of per-key sync.Mutexes on the TokenCacheService struct) so the code checks
tokenCache and if missing/expired uses singleflight.Do (or locks the per-key
mutex) to ensure only one goroutine performs NewIamToken and then stores the
returned token with c.tokenCache.Add; keep the existing isTokenExpired and
c.Clock checks outside/inside the coalesced call as appropriate and ensure any
errors from NewIamToken (wrapped with MapGrpcErrors) are propagated to callers.
♻️ Duplicate comments (9)
docs/introduction/stability-support.md (1)
143-143: Missing entry in Provider Stability table.The Nebius Mysterybox provider is added to the Feature Support table but still lacks a corresponding entry in the Provider Stability and Support Level table (lines 65-100). Please add an entry declaring stability level and maintainer.
docs/provider/nebius-mysterybox.md (1)
44-51: Wrap the include directives in YAML code fences.Same issue as previously noted: without fences these includes render as plain text.
providers/v1/nebius/mysterybox/provider_test.go (2)
638-646: Usingt.Errorfinside a goroutine may not properly report test failures.When
t.Errorfis called from a goroutine, the test may exit before the error is reported, potentially causing a panic. Use a channel oratomic.Valueto collect errors and check them afterwg.Wait().
727-759: Global state mutation without cleanup may affect parallel tests.Setting
mysteryboxClientsCacheSize = 1modifies package-level state without restoring it. Consider usingt.Cleanupto restore the original value, or restructure the test to inject the cache size.config/crds/bases/external-secrets.io_clustersecretstores.yaml (1)
3716-3787: Require exactly one credential inauth.
authis required but both credential refs are optional, soauth: {}validates and will fail at runtime. Add a CEL rule to enforce exactly one ofserviceAccountCredsSecretRefortokenSecretRef.🔧 Proposed CRD validation
auth: description: Auth defines parameters to authenticate in Mysterybox properties: serviceAccountCredsSecretRef: @@ tokenSecretRef: @@ - type: object + type: object + x-kubernetes-validations: + - message: Exactly one of 'serviceAccountCredsSecretRef' or 'tokenSecretRef' must be specified + rule: (has(self.serviceAccountCredsSecretRef) && !has(self.tokenSecretRef)) || (!has(self.serviceAccountCredsSecretRef) && has(self.tokenSecretRef))apis/externalsecrets/v1/secretstore_nebius_types.go (2)
19-38: Require at least one auth source inNebiusAuth.
Right nowauth: {}passes validation but fails at runtime. Add a CRD rule to require eitherserviceAccountCredsSecretRefortokenSecretRef.Suggested fix
// NebiusAuth defines the authentication method for the Nebius provider. +// +kubebuilder:validation:XValidation:rule="has(self.serviceAccountCredsSecretRef) || has(self.tokenSecretRef)",message="either serviceAccountCredsSecretRef or tokenSecretRef must be set" type NebiusAuth struct {
55-57: Avoid serializingcaProvider: null.
CAProvideris optional but missingomitempty, so nil pointers serialize asnull. Align with other optional provider fields.Suggested fix
- CAProvider *NebiusCAProvider `json:"caProvider"` + CAProvider *NebiusCAProvider `json:"caProvider,omitempty"`config/crds/bases/external-secrets.io_secretstores.yaml (1)
3709-3824: Require at least one Nebius auth method (repeat).
authis required but bothserviceAccountCredsSecretRefandtokenSecretRefare optional, soauth: {}passes validation and will likely fail at runtime. Please add a schema rule (oneOf/anyOf or x-kubernetes-validations) to require at least one.providers/v1/nebius/mysterybox/secrets_client.go (1)
63-66: Error message is misleading - operation is marshalling, not unmarshalling.The error constant
errJSONUnmarshaland message "failed to unmarshal JSON" are incorrect here sincejson.Marshalis being called.🐛 Suggested fix
Update the error constant and usage:
const ( errNotImplemented = "not implemented" - errJSONUnmarshal = "failed to unmarshal JSON" + errJSONMarshal = "failed to marshal JSON" ... )out, err := json.Marshal(keyToValue) if err != nil { - return nil, errors.New(errJSONUnmarshal) + return nil, fmt.Errorf("%s: %w", errJSONMarshal, err) }
🧹 Nitpick comments (7)
providers/v1/nebius/common/sdk/mysterybox/client.go (1)
1-13: Unusual license header format.The license header uses
// /*which appears to be double-commented. This differs from the standard license format used elsewhere in the codebase. Consider using the standard format without the leading//on each line.♻️ Suggested fix
-// /* -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// */ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/providers/v1/nebius/common/sdk/mysterybox/grpc_client_test.go (2)
1-13: Unusual license header format.Same issue as in
client.go- the license header uses double-comment format (// /*). Consider using the standard block comment format for consistency.
159-172: Simplify payload entry validation.The validation logic copies
tt.expectedinto a new map, then deletes matched entries - but the copy on lines 159-162 is unnecessary sincett.expectedis already amap[string][]byte. Also, the assertion argument order should be(expected, actual)per testify conventions.♻️ Suggested simplification
- expected := make(map[string][]byte, len(tt.expected)) - for k, v := range tt.expected { - expected[k] = v - } - - tassert.Equal(t, len(payload.Entries), len(expected)) - for _, entry := range payload.Entries { - value, _ := expected[entry.Key] - if (entry.BinaryValue != nil && bytes.Equal(value, entry.BinaryValue)) || (entry.StringValue != "" && bytes.Equal(value, []byte(entry.StringValue))) { - delete(expected, entry.Key) - continue - } - } - tassert.Empty(t, expected, "not all expected entries found: %+v", expected) + tassert.Equal(t, len(tt.expected), len(payload.Entries)) + for _, entry := range payload.Entries { + expectedValue, ok := tt.expected[entry.Key] + tassert.True(t, ok, "unexpected key: %s", entry.Key) + if entry.BinaryValue != nil { + tassert.Equal(t, expectedValue, entry.BinaryValue) + } else { + tassert.Equal(t, expectedValue, []byte(entry.StringValue)) + } + }providers/v1/nebius/common/sdk/mysterybox/fake/fake_client.go (2)
1-13: Unusual license header format.Same issue as other files - consider using standard block comment format.
38-47: Inconsistent receiver types and potential version ID issue.Two concerns:
GetSecretandGetSecretByKeyuse value receivers whileCloseuses a pointer receiver. For consistency and to avoid copying the struct, consider using pointer receivers throughout.In
GetSecretByKey(line 57), whenversionIDis empty (requesting latest), the returnedPayloadEntry.VersionIDwill be empty instead of the actual version ID from the secret. This differs fromGetSecretwhich correctly returnssecret.VersionId.♻️ Suggested fix
-func (f FakeMysteryboxClient) GetSecret(_ context.Context, _, secretId, versionId string) (*mysterybox.Payload, error) { +func (f *FakeMysteryboxClient) GetSecret(_ context.Context, _, secretId, versionId string) (*mysterybox.Payload, error) { -func (f FakeMysteryboxClient) GetSecretByKey(_ context.Context, _, secretID, versionID, key string) (*mysterybox.PayloadEntry, error) { +func (f *FakeMysteryboxClient) GetSecretByKey(_ context.Context, _, secretID, versionID, key string) (*mysterybox.PayloadEntry, error) { secret, err := f.mysteryboxService.GetSecret(secretID, versionID) if err != nil { return nil, err } for _, entry := range secret.Entries { if entry.Key == key { return &mysterybox.PayloadEntry{ - VersionID: versionID, + VersionID: secret.VersionId, Entry: entry, }, nil } }Also applies to: 49-64
providers/v1/nebius/mysterybox/validation_test.go (1)
290-351: LGTM with minor suggestion:TestValidateStore_APIDomainCasesprovides thorough domain validation coverage.The test comprehensively validates:
- Valid domain formats (simple, subdomain, with port, hyphen, uppercase)
- Invalid formats (single label, empty label, leading/trailing dots, hyphen positioning, invalid characters)
- Length constraints (label > 63 chars, overall domain length)
Minor suggestion: The error containment check at lines 346-348 could be made conditional to improve clarity:
if tc.valid { tassert.NoError(t, err, "%s: expected valid, got error", tc.name) } else { tassert.Error(t, err, "%s: expected error for domain %q", tc.name, tc.domain) -} -if err != nil { tassert.Contains(t, err.Error(), errInvalidAPIDomain, "%s: error should contain invalid api domain", tc.name) }This makes it explicit that the error message check only applies to invalid cases.
providers/v1/nebius/mysterybox/provider.go (1)
35-35: Consider using a clearer import alias.The alias
mysterybox2is unconventional. Consider a more descriptive alias likemysteryboxsdkormbclientto clarify it refers to the SDK client package.♻️ Suggested change
- mysterybox2 "github.com/external-secrets/external-secrets/providers/v1/nebius/common/sdk/mysterybox" + mysteryboxsdk "github.com/external-secrets/external-secrets/providers/v1/nebius/common/sdk/mysterybox"
providers/v1/nebius/mysterybox/utils.go
Dismissed
|
|
||
| // HashBytes calculate a hash of the bytes by sha256 algorithm. | ||
| func HashBytes(b []byte) string { | ||
| sum := sha256.Sum256(b) |
Check failure
Code scanning / CodeQL
Use of a broken or weak cryptographic hashing algorithm on sensitive data High
There was a problem hiding this comment.
it's used only for hashing purposes to be used in the cache key, can we leave it as is?
There was a problem hiding this comment.
I closed this, should go away.
25a7179 to
0b77a05
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@providers/v1/nebius/common/sdk/mysterybox/fake/fake_client.go`:
- Around line 56-66: GetSecretByKey currently returns the input versionID
instead of the resolved secret version; update the return to use the resolved
secret.VersionId (not the input versionID) when constructing the
mysterybox.PayloadEntry so callers receive the actual version (e.g., set
VersionID: secret.VersionId in the success return inside GetSecretByKey where
you build the PayloadEntry).
- Around line 68-122: MysteryboxService's secretData is accessed concurrently
causing data races; add a sync.RWMutex field (e.g., mu) to the MysteryboxService
struct, initialize it in InitMysteryboxService, use mu.RLock()/mu.RUnlock() in
GetSecret, and use mu.Lock()/mu.Unlock() in CreateSecret and
CreateNewSecretVersion to protect reads and writes to secretData so concurrent
tests are safe.
♻️ Duplicate comments (4)
providers/v1/nebius/mysterybox/token_service.go (1)
81-105: Coalesce concurrent cache misses to avoid duplicate exchanges.
Parallel callers can miss the cache and exchange in parallel; consider a per‑key mutex to coalesce those calls and reduce redundant IAM requests.providers/v1/nebius/common/sdk/mysterybox/grpc_client.go (1)
65-79: Potential nil dereference whenentry.GetData()returns nil.This issue was previously flagged. If the gRPC response's
Datafield is nil, accessingentry.GetData().GetKey()orentry.GetData().Payloadwill cause a panic.providers/v1/nebius/mysterybox/secrets_client.go (1)
65-68: Error message is misleading - operation is marshalling, not unmarshalling.This issue was previously flagged. The error constant
errJSONUnmarshaland message "failed to unmarshal JSON" are incorrect sincejson.Marshalis being called.providers/v1/nebius/mysterybox/provider.go (1)
230-248: Error fromsync.Onceis not properly propagated on subsequent calls.This issue was previously flagged. If
lru.NewWithEvictfails, the error is captured butcacheOnce.Dowill never execute again on subsequent calls. This means later calls toinitMysteryboxClientsCache()will returnnilerror even though initialization failed.
🧹 Nitpick comments (7)
providers/v1/nebius/common/sdk/mysterybox/grpc_client.go (2)
101-117: Same nil dereference risk inGetSecretmethod.The
GetSecretmethod iterates overpayload.GetData()entries and accessesentry.Keyandentry.Payloaddirectly without checking if entries could be nil. WhileGetData()returns a slice (less likely to be nil itself), individual entries within could still have nil fields depending on the protobuf generation.More critically, this duplicates the payload-type switch logic from
GetSecretByKey. Consider extracting a helper function to reduce duplication and ensure consistent handling.♻️ Suggested refactor to extract common payload conversion
func convertPayloadToEntry(data *proto.Payload, key string) (Entry, error) { if data == nil { return Entry{}, fmt.Errorf("received nil data for key: %v", key) } entry := Entry{Key: data.GetKey()} switch data.Payload.(type) { case *proto.Payload_StringValue: entry.StringValue = data.GetStringValue() case *proto.Payload_BinaryValue: entry.BinaryValue = data.GetBinaryValue() default: return Entry{}, fmt.Errorf(notSupportedPayloadType, key) } return entry, nil }Then use this helper in both
GetSecretByKeyandGetSecret.
132-135: Use named fields in struct literal for clarity.The struct literal uses positional initialization which can lead to subtle bugs if field order changes.
♻️ Suggested fix
return &GrpcClient{ - mbox.NewPayloadService(mysteryboxSdk), - mysteryboxSdk, + PayloadService: mbox.NewPayloadService(mysteryboxSdk), + sdk: mysteryboxSdk, }, nilproviders/v1/nebius/common/sdk/mysterybox/fake/fake_client.go (1)
35-49: Inconsistent receiver types between methods.
Close()uses a pointer receiver*FakeMysteryboxClient, whileGetSecret()andGetSecretByKey()use value receiversFakeMysteryboxClient. This is inconsistent and could cause issues since value receivers won't see mutations to theClosedfield.♻️ Suggested fix
-func (f FakeMysteryboxClient) GetSecret(_ context.Context, _, secretId, versionId string) (*mysterybox.Payload, error) { +func (f *FakeMysteryboxClient) GetSecret(_ context.Context, _, secretId, versionId string) (*mysterybox.Payload, error) {-func (f FakeMysteryboxClient) GetSecretByKey(_ context.Context, _, secretID, versionID, key string) (*mysterybox.PayloadEntry, error) { +func (f *FakeMysteryboxClient) GetSecretByKey(_ context.Context, _, secretID, versionID, key string) (*mysterybox.PayloadEntry, error) {providers/v1/nebius/mysterybox/secrets_client.go (3)
119-122:Close()does not close the underlyingmysteryboxClient.The
Closemethod returnsnilwithout closing themysteryboxClient. Since the client is cached at the provider level, this may be intentional, but it's inconsistent with themysterybox.Clientinterface which has aClose()method. Consider adding a comment explaining why the client isn't closed here (i.e., it's managed by the provider's cache).📝 Suggested clarification
// Close cleans up resources when the provider is done being used. +// Note: The underlying mysteryboxClient is not closed here because it is +// managed by the Provider's client cache and may be shared across multiple SecretsClient instances. func (c *SecretsClient) Close(_ context.Context) error { return nil }
109-112:Validate()always returns Ready without performing validation.The
Validatemethod unconditionally returnsValidationResultReadywithout checking if the client can actually connect to the Mysterybox service. This could mask configuration issues. Consider adding at least a basic connectivity check or documenting why validation is skipped.
163-171: Unreachable code path inhandleGetSecretByKeyError.The condition
if ref.Property != ""on line 164 will always be true when this function is called, becauseGetSecretByKeyis only invoked whensecretKey := ref.Propertyis non-empty (see line 54). The outerif ref.Property != ""check is redundant.♻️ Simplified version
func handleGetSecretByKeyError(err error, ref esv1.ExternalSecretDataRemoteRef) error { if err == nil { return nil } st, ok := status.FromError(err) if !ok { return err // not a grpc error } if st.Code() == codes.NotFound { - if ref.Property != "" { - if ref.Version != "" { - return fmt.Errorf(errSecretVersionByKeyNotFound, ref.Version, ref.Key, ref.Property, esv1.NoSecretErr) - } - return fmt.Errorf(errSecretByKeyNotFound, ref.Property, ref.Key, esv1.NoSecretErr) + if ref.Version != "" { + return fmt.Errorf(errSecretVersionByKeyNotFound, ref.Version, ref.Key, ref.Property, esv1.NoSecretErr) } + return fmt.Errorf(errSecretByKeyNotFound, ref.Property, ref.Key, esv1.NoSecretErr) } return MapGrpcErrors("get secret", err) }providers/v1/nebius/mysterybox/provider.go (1)
236-242: Variable shadowing:errin eviction callback shadows outererr.The
errvariable declared on line 231 is shadowed byerron line 238 inside the eviction callback. IfClose()fails, this innererris logged but doesn't affect the outererrthat is returned. While the current logic handles this correctly (the inner error is only logged), the shadowing is confusing and could lead to bugs during future maintenance.♻️ Suggested fix
func(key, value interface{}) { p.Logger.V(1).Info("Evicting a Nebius Mysterybox client", "apiDomain", key.(ClientCacheKey).APIDomain) - err := value.(mysterybox2.Client).Close() - if err != nil { - p.Logger.Error(err, "Failed to close Nebius Mysterybox client") + closeErr := value.(mysterybox2.Client).Close() + if closeErr != nil { + p.Logger.Error(closeErr, "Failed to close Nebius Mysterybox client") } })
|
Hello. I'm not sure how this would differ from our #5057 grpc plugin initiative. |
evrardj-roche
left a comment
There was a problem hiding this comment.
Do you intend to maintain this long term with us?
If so, the most important is indeed to get to know each other and have contact points...
Hey @Skarlso, Thanks for sharing the issue - it’s an interesting one. The idea itself looks solid, and it seems you already have something similar on the HTTP side with webhook provider. I had been exploring a slightly different approach: building a kind of “bridge” between our gRPC server and the webhook provider contract. The main goal was to make integrations easier. However, after running a few PoCs, I decided to move away from that idea and go with a direct integration instead. It turned out to be a more stable solution overall, especially since I ran into some authorization-related issues with the bridge approach. Regarding the issue you shared: in general, I agree it could be a good solution. That said, as far as I understand, it would require implementing a dedicated plugin and maintaining the corresponding API (which is pretty the same as for my initial webhook bridge idea). Also, unless I’m missing something, it doesn’t look like the solution or design has progressed further so far. I’m also a bit concerned that this might become a significant blocker for the integration. Would it be possible to move forward with the current proposal instead? |
|
This is extremely valuable input @greenmapc ! I personally believe it would be unfair to accept other providers directly while refusing Nebius, especially if you are willing to maintain it. For progress, I can't judge, I did not put my hands into it yet. However, I would be eager to discuss with you about the issues you encountered with that bridge you tried. I think those are good inputs for our proposal. Would you be okay for that? |
|
It hasn't progressed yeah, but @moolen has better insight on that. I'm just afraid that with the GRPC stuff being shared so much, it will become either obsolete, or duplicated a lot. We'll see. I'm unsure. This is a ton of code so it's going to take quite some time to go through it and see if it fits the ecosystem and our guidelines. Not saying no, ofc. I'm just saying as a heads up that it's going to take a lot. :) |
|
Hey @evrardj-roche! Thank you so much for your reply! Yes, of course, let's discuss it! Would you like to discuss this separately, outside of PR? |
|
@Skarlso regarding your comment - I agree that there’s already quite a bit of duplication between providers, since they all follow very similar logic to communicate with backends (HTTP or gRPC). It might be useful to have a shared library for gRPC connections and caching that follows common guidelines and makes provider integrations easier. Just thinking out loud here, but if you have other thoughts, maybe this is something we could start shaping either in this PR or in a follow-up. I’d be happy to contribute, I’ve already spent some time researching the project. I also understand that this is a fairly large amount of code and may take time to review. I’d really appreciate it if you could find the time to take a look at our integration 🙂 |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@providers/v1/nebius/mysterybox/provider_test.go`:
- Around line 553-731: Fix the failing/unclear assertion messages by replacing
uses of &factoryCalls (pointer) in the fmt.Sprintf calls with the actual count
(atomic.LoadInt32(&factoryCalls)) and ensure the "want" value matches the
asserted expected value; update the messages in
TestCreateOrGetMysteryboxClient_CachesByKey,
TestCreateOrGetMysteryboxClient_EmptyCA_EqualsNil,
TestCreateOrGetMysteryboxClient_Concurrent_SingleClient, and
TestCreateOrGetMysteryboxClient_Concurrent_MultipleClients so the formatted
string prints the current factory call count and the correct expected number
(e.g., for the last test the message should say want 4 to match tassert.Equal(t,
int32(4), ...)).
In `@providers/v1/nebius/mysterybox/secrets_client.go`:
- Around line 155-172: The error mapping in handleGetSecretByKeyError uses a
generic operation label ("get secret") which loses the by-key context; update
the final MapGrpcErrors call in handleGetSecretByKeyError to use a more specific
operation string like "get secret by key" (or similar) so logs and mapped errors
reflect the by-key operation; locate the handleGetSecretByKeyError function and
replace the operation argument passed to MapGrpcErrors("get secret", err) with
the new descriptive label.
🧹 Nitpick comments (5)
providers/v1/nebius/common/sdk/mysterybox/grpc_client.go (2)
105-121: Consider adding nil checks for entries in the loop.Unlike
GetSecretByKeywhich now validatesentry.GetData() != nil,GetSecretiterates overpayload.GetData()without checking if individual entries orentry.Payloadare nil before the type switch. If the gRPC response contains a nil entry, this could panic.🛡️ Proposed defensive check
payloadEntries := make([]Entry, 0, len(payload.Data)) for _, entry := range payload.GetData() { + if entry == nil { + continue + } payloadEntry := Entry{ Key: entry.Key, }
136-139: Use named fields in struct initialization.Positional struct initialization can silently break if fields are reordered or new fields are added to
GrpcClient. Using named fields improves maintainability.♻️ Suggested change
return &GrpcClient{ - mbox.NewPayloadService(mysteryboxSdk), - mysteryboxSdk, + PayloadService: mbox.NewPayloadService(mysteryboxSdk), + sdk: mysteryboxSdk, }, nilapis/externalsecrets/v1/secretstore_nebius_types.go (2)
44-47: Add+optionalmarker andomitemptytoCertificatefield.The
Certificatefield inNebiusCAProviderhasomitemptyin its JSON tag but lacks the+optionalkubebuilder marker. For consistency with other optional fields in the codebase, consider adding the marker. Additionally, if the CA provider itself is optional and Certificate is the only field, users might create an emptycaProvider: {}block. Consider whether this struct should also have validation.♻️ Suggested change
// NebiusCAProvider The provider for the CA bundle to use to validate Nebius server certificate. type NebiusCAProvider struct { + // +optional Certificate esmeta.SecretKeySelector `json:"certSecretRef,omitempty"` }
55-56: Use//comment style for consistency.The comment on line 55 uses
/* */style while all other comments in this file use//. Using consistent comment style improves readability.♻️ Suggested change
- /* Auth defines parameters to authenticate in Mysterybox */ + // Auth defines parameters to authenticate in Mysterybox. Auth NebiusAuth `json:"auth"`config/crds/bases/external-secrets.io_secretstores.yaml (1)
3788-3791: Consider dropping provider-specific CEL validation for auth.To keep CRD validations consistent across providers, it may be better to rely on runtime validation/documentation instead of adding a provider-specific
x-kubernetes-validationsrule here.♻️ Suggested change
- x-kubernetes-validations: - - message: either serviceAccountCredsSecretRef or tokenSecretRef - must be set - rule: has(self.serviceAccountCredsSecretRef) || has(self.tokenSecretRef)Based on learnings: In CRD YAML files under config/crds, avoid provider-specific schema validations (e.g., format: uri or CEL rules) when other providers don’t use them; prioritize consistency across providers.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@go.mod`:
- Line 341: Update the Nebius SDK entry in go.mod by changing the version string
for github.com/nebius/gosdk from v0.0.0-20260122112135-b2ef6b4c6a76 to the newer
v0.0.0-20260204094009-511fd4d4f7a1; run `go get
github.com/nebius/gosdk@v0.0.0-20260204094009-511fd4d4f7a1` (or `go mod tidy`)
to apply and verify builds, and add a short note in the repository docs (or
changelog) explaining that nebuis/gosdk is a pre-1.0 module and that an older
commit should be pinned explicitly if compatibility is required.
233d348 to
557cb75
Compare
|
@Skarlso I can't make CI green, looks like linter does not work for the new provider's module. I thought there is a new model/code problem, but I looked around and saw that other PRs where new providers have been added experience the same problem! Also, there is an highlighted issue regarding SHA256. I don't think it's a real issue here, because it's used only for hashing private keys as a key for the cache. If you agree, can we skip it? Whenever you get a moment to look at it, I’d love to hear your feedback! Thanks. |
|
@greenmapc Hey Anna! Thanks so much for working on this. I'm still trying to get the other members together on the consensus with this, because there is just so much overlap with the GRPC plugin work we are doing to get out-of-tree providers rolling. Once we go ahead with that, this is going to be completely obsolete and/or would have to be entirely rewritten or consist of a lot of duplicate work. We are going to meet at KubeCon with the person who is writing that PR, and I'm trying to discuss this in the next community meeting as well, which I'm hoping will have more of the core maintainers joining. It's going to be on the 18th of February. UTC 12:00. If you would like to further discuss this, please try and join the meeting, or look at the recording for the potential outcome. |
Signed-off-by: annakuzmenko <annakuzmenko@nebius.com>
Signed-off-by: annakuzmenko <annakuzmenko@nebius.com>
Signed-off-by: annakuzmenko <annakuzmenko@nebius.com>
Signed-off-by: annakuzmenko <annakuzmenko@nebius.com>
Signed-off-by: annakuzmenko <annakuzmenko@nebius.com>
Signed-off-by: annakuzmenko <annakuzmenko@nebius.com>
ccd7585 to
32f906f
Compare
| func NewSDK(ctx context.Context, apiDomain string, caCertificate []byte) (*gosdk.SDK, error) { | ||
| tlsCfg := &tls.Config{MinVersion: tls.VersionTLS12} | ||
|
|
||
| if caCertificate != nil && len(caCertificate) > 0 { |
There was a problem hiding this comment.
Just a nit, len check already work with slices and nil. No need for the nil check here.
|
One last nit. :D Otherwise, this is now fine for me. @moolen Would you like to have one more round? |
Signed-off-by: annakuzmenko <annakuzmenko@nebius.com>
|
👍 It's fine for me to get it in, though i have concerns regarding the missing gRPC Close(). It would be great if we would be able to resolve this soon and have a dedicated regression test for this case. (e.g. SecretStore / domain churn). Though not a blocker, for me at least. |
|
Same here. I would ask you @greenmapc if you could monitor some memory usage of the controller while using this provider and then report back on any problems? :) But for now, I would say this is a strong enough alpha. |
|
@greenmapc can you please rebase / update? :) |
|
Of course! I will deploy it in all our regions (when it will be released) and will monitor :) In case of problems, I will open an issue here and will work on it. Btw, for the gRPC Close() issue, should I open an issue already rn? |
|
@greenmapc Yes please, let's not forget that that is something we really need to fix. |
Thanks, concerns have been addressed I believe.
…2.1.0 (#4491) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [external-secrets/external-secrets](https://github.com/external-secrets/external-secrets) | minor | `v2.0.1` → `v2.1.0` | --- ### Release Notes <details> <summary>external-secrets/external-secrets (external-secrets/external-secrets)</summary> ### [`v2.1.0`](https://github.com/external-secrets/external-secrets/releases/tag/v2.1.0) [Compare Source](external-secrets/external-secrets@v2.0.1...v2.1.0) Image: `ghcr.io/external-secrets/external-secrets:v2.1.0` Image: `ghcr.io/external-secrets/external-secrets:v2.1.0-ubi` Image: `ghcr.io/external-secrets/external-secrets:v2.1.0-ubi-boringssl` <!-- Release notes generated using configuration in .github/release.yml at main --> #### What's Changed ##### General - chore(release): Update helm chart by [@​evrardj-roche](https://github.com/evrardj-roche) in [#​5981](external-secrets/external-secrets#5981) - fix: cosign verify does not use signing config by [@​gusfcarvalho](https://github.com/gusfcarvalho) in [#​5982](external-secrets/external-secrets#5982) - docs: Update release process by [@​evrardj-roche](https://github.com/evrardj-roche) in [#​5980](external-secrets/external-secrets#5980) - fix: allow cross-namespace push with ClusterSecretStore objects by [@​Skarlso](https://github.com/Skarlso) in [#​5998](external-secrets/external-secrets#5998) - feat(charts): add new flag enable leader for cert-manager by [@​nutmos](https://github.com/nutmos) in [#​5863](external-secrets/external-secrets#5863) - feat(kubernetes): fall back to system CA roots when no CA is configured by [@​rajsinghtech](https://github.com/rajsinghtech) in [#​5961](external-secrets/external-secrets#5961) - feat: dedup sbom but keep it monolithic by [@​moolen](https://github.com/moolen) in [#​6004](external-secrets/external-secrets#6004) - fix: add missing metrics and fundamentally fix the caching logic by [@​Skarlso](https://github.com/Skarlso) in [#​5894](external-secrets/external-secrets#5894) - docs: designate Oracle Vault provider as 'stable' by [@​anders-swanson](https://github.com/anders-swanson) in [#​6020](external-secrets/external-secrets#6020) - docs: Oracle Vault provider capabilities by [@​anders-swanson](https://github.com/anders-swanson) in [#​6023](external-secrets/external-secrets#6023) - docs(azurekv): cert-manager pushsecret example and cleanups by [@​illrill](https://github.com/illrill) in [#​5972](external-secrets/external-secrets#5972) - feat(kubernetes): implement SecretExists by [@​Saku2](https://github.com/Saku2) in [#​5973](external-secrets/external-secrets#5973) - fix(charts): Fix wrongly set annotations for cert-controller metrics service by [@​josemaia](https://github.com/josemaia) in [#​6029](external-secrets/external-secrets#6029) - feat(providers): Nebius MysteryBox integration by [@​greenmapc](https://github.com/greenmapc) in [#​5868](external-secrets/external-secrets#5868) ##### Dependencies - chore(deps): bump aquasecurity/trivy-action from 0.34.0 to 0.34.1 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5986](external-secrets/external-secrets#5986) - chore(deps): bump mkdocs-material from 9.7.1 to 9.7.2 in /hack/api-docs by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5992](external-secrets/external-secrets#5992) - chore(deps): bump ubi9/ubi from `b8923f5` to `cecb1cd` by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5984](external-secrets/external-secrets#5984) - chore(deps): bump helm/kind-action from 1.13.0 to 1.14.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5985](external-secrets/external-secrets#5985) - chore(deps): bump actions/dependency-review-action from 4.8.2 to 4.8.3 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5990](external-secrets/external-secrets#5990) - chore(deps): bump github/codeql-action from 4.32.3 to 4.32.4 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5989](external-secrets/external-secrets#5989) - chore(deps): bump goreleaser/goreleaser-action from 6.4.0 to 7.0.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5987](external-secrets/external-secrets#5987) - chore(deps): bump regex from 2026.1.15 to 2026.2.19 in /hack/api-docs by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5991](external-secrets/external-secrets#5991) - chore(deps): bump actions/stale from 10.1.1 to 10.2.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5988](external-secrets/external-secrets#5988) - chore(deps): bump regex from 2026.2.19 to 2026.2.28 in /hack/api-docs by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6012](external-secrets/external-secrets#6012) - chore(deps): bump mkdocs-material from 9.7.2 to 9.7.3 in /hack/api-docs by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6014](external-secrets/external-secrets#6014) - chore(deps): bump step-security/harden-runner from 2.14.2 to 2.15.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6015](external-secrets/external-secrets#6015) - chore(deps): bump anchore/sbom-action from 0.22.2 to 0.23.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6016](external-secrets/external-secrets#6016) - chore(deps): bump certifi from 2026.1.4 to 2026.2.25 in /hack/api-docs by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6013](external-secrets/external-secrets#6013) - chore(deps): bump actions/setup-go from 6.2.0 to 6.3.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6010](external-secrets/external-secrets#6010) - chore(deps): bump hashicorp/setup-terraform from [`ce70bcf`](external-secrets/external-secrets@ce70bcf) to [`5e8dbf3`](external-secrets/external-secrets@5e8dbf3) by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6011](external-secrets/external-secrets#6011) - chore(deps): bump actions/attest-build-provenance from 3.2.0 to 4.1.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6009](external-secrets/external-secrets#6009) - chore(deps): bump distroless/static from `972618c` to `28efbe9` by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6008](external-secrets/external-secrets#6008) #### New Contributors - [@​nutmos](https://github.com/nutmos) made their first contribution in [#​5863](external-secrets/external-secrets#5863) - [@​rajsinghtech](https://github.com/rajsinghtech) made their first contribution in [#​5961](external-secrets/external-secrets#5961) - [@​illrill](https://github.com/illrill) made their first contribution in [#​5972](external-secrets/external-secrets#5972) - [@​Saku2](https://github.com/Saku2) made their first contribution in [#​5973](external-secrets/external-secrets#5973) - [@​greenmapc](https://github.com/greenmapc) made their first contribution in [#​5868](external-secrets/external-secrets#5868) **Full Changelog**: <external-secrets/external-secrets@v2.0.1...v2.1.0> </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My41MS4wIiwidXBkYXRlZEluVmVyIjoiNDMuNTEuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW1hZ2UiXX0=--> Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/4491 Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net> Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
This PR contains the following updates: | Package | Update | Change | |---|---|---| | [external-secrets](https://github.com/external-secrets/external-secrets) | minor | `2.0.1` → `2.1.0` | --- ### Release Notes <details> <summary>external-secrets/external-secrets (external-secrets)</summary> ### [`v2.1.0`](https://github.com/external-secrets/external-secrets/releases/tag/v2.1.0) [Compare Source](external-secrets/external-secrets@v2.0.1...v2.1.0) Image: `ghcr.io/external-secrets/external-secrets:v2.1.0` Image: `ghcr.io/external-secrets/external-secrets:v2.1.0-ubi` Image: `ghcr.io/external-secrets/external-secrets:v2.1.0-ubi-boringssl` <!-- Release notes generated using configuration in .github/release.yml at main --> #### What's Changed ##### General - chore(release): Update helm chart by [@​evrardj-roche](https://github.com/evrardj-roche) in [#​5981](external-secrets/external-secrets#5981) - fix: cosign verify does not use signing config by [@​gusfcarvalho](https://github.com/gusfcarvalho) in [#​5982](external-secrets/external-secrets#5982) - docs: Update release process by [@​evrardj-roche](https://github.com/evrardj-roche) in [#​5980](external-secrets/external-secrets#5980) - fix: allow cross-namespace push with ClusterSecretStore objects by [@​Skarlso](https://github.com/Skarlso) in [#​5998](external-secrets/external-secrets#5998) - feat(charts): add new flag enable leader for cert-manager by [@​nutmos](https://github.com/nutmos) in [#​5863](external-secrets/external-secrets#5863) - feat(kubernetes): fall back to system CA roots when no CA is configured by [@​rajsinghtech](https://github.com/rajsinghtech) in [#​5961](external-secrets/external-secrets#5961) - feat: dedup sbom but keep it monolithic by [@​moolen](https://github.com/moolen) in [#​6004](external-secrets/external-secrets#6004) - fix: add missing metrics and fundamentally fix the caching logic by [@​Skarlso](https://github.com/Skarlso) in [#​5894](external-secrets/external-secrets#5894) - docs: designate Oracle Vault provider as 'stable' by [@​anders-swanson](https://github.com/anders-swanson) in [#​6020](external-secrets/external-secrets#6020) - docs: Oracle Vault provider capabilities by [@​anders-swanson](https://github.com/anders-swanson) in [#​6023](external-secrets/external-secrets#6023) - docs(azurekv): cert-manager pushsecret example and cleanups by [@​illrill](https://github.com/illrill) in [#​5972](external-secrets/external-secrets#5972) - feat(kubernetes): implement SecretExists by [@​Saku2](https://github.com/Saku2) in [#​5973](external-secrets/external-secrets#5973) - fix(charts): Fix wrongly set annotations for cert-controller metrics service by [@​josemaia](https://github.com/josemaia) in [#​6029](external-secrets/external-secrets#6029) - feat(providers): Nebius MysteryBox integration by [@​greenmapc](https://github.com/greenmapc) in [#​5868](external-secrets/external-secrets#5868) ##### Dependencies - chore(deps): bump aquasecurity/trivy-action from 0.34.0 to 0.34.1 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5986](external-secrets/external-secrets#5986) - chore(deps): bump mkdocs-material from 9.7.1 to 9.7.2 in /hack/api-docs by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5992](external-secrets/external-secrets#5992) - chore(deps): bump ubi9/ubi from `b8923f5` to `cecb1cd` by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5984](external-secrets/external-secrets#5984) - chore(deps): bump helm/kind-action from 1.13.0 to 1.14.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5985](external-secrets/external-secrets#5985) - chore(deps): bump actions/dependency-review-action from 4.8.2 to 4.8.3 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5990](external-secrets/external-secrets#5990) - chore(deps): bump github/codeql-action from 4.32.3 to 4.32.4 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5989](external-secrets/external-secrets#5989) - chore(deps): bump goreleaser/goreleaser-action from 6.4.0 to 7.0.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5987](external-secrets/external-secrets#5987) - chore(deps): bump regex from 2026.1.15 to 2026.2.19 in /hack/api-docs by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5991](external-secrets/external-secrets#5991) - chore(deps): bump actions/stale from 10.1.1 to 10.2.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​5988](external-secrets/external-secrets#5988) - chore(deps): bump regex from 2026.2.19 to 2026.2.28 in /hack/api-docs by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6012](external-secrets/external-secrets#6012) - chore(deps): bump mkdocs-material from 9.7.2 to 9.7.3 in /hack/api-docs by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6014](external-secrets/external-secrets#6014) - chore(deps): bump step-security/harden-runner from 2.14.2 to 2.15.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6015](external-secrets/external-secrets#6015) - chore(deps): bump anchore/sbom-action from 0.22.2 to 0.23.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6016](external-secrets/external-secrets#6016) - chore(deps): bump certifi from 2026.1.4 to 2026.2.25 in /hack/api-docs by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6013](external-secrets/external-secrets#6013) - chore(deps): bump actions/setup-go from 6.2.0 to 6.3.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6010](external-secrets/external-secrets#6010) - chore(deps): bump hashicorp/setup-terraform from [`ce70bcf`](external-secrets/external-secrets@ce70bcf) to [`5e8dbf3`](external-secrets/external-secrets@5e8dbf3) by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6011](external-secrets/external-secrets#6011) - chore(deps): bump actions/attest-build-provenance from 3.2.0 to 4.1.0 by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6009](external-secrets/external-secrets#6009) - chore(deps): bump distroless/static from `972618c` to `28efbe9` by [@​dependabot](https://github.com/dependabot)\[bot] in [#​6008](external-secrets/external-secrets#6008) #### New Contributors - [@​nutmos](https://github.com/nutmos) made their first contribution in [#​5863](external-secrets/external-secrets#5863) - [@​rajsinghtech](https://github.com/rajsinghtech) made their first contribution in [#​5961](external-secrets/external-secrets#5961) - [@​illrill](https://github.com/illrill) made their first contribution in [#​5972](external-secrets/external-secrets#5972) - [@​Saku2](https://github.com/Saku2) made their first contribution in [#​5973](external-secrets/external-secrets#5973) - [@​greenmapc](https://github.com/greenmapc) made their first contribution in [#​5868](external-secrets/external-secrets#5868) **Full Changelog**: <external-secrets/external-secrets@v2.0.1...v2.1.0> </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My41OS4yIiwidXBkYXRlZEluVmVyIjoiNDMuNTkuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiY2hhcnQiXX0=--> Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/4516 Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net> Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
Signed-off-by: Evyatar Shtern <evyatar.shtern@gong.io>
Signed-off-by: AlexOQ <30403857+AlexOQ@users.noreply.github.com>



Problem Statement
Integration with Nebius Mysterybox Secret Service
Related Issue
Fixes #5852
Format
Please ensure that your PR follows the following format for the title:
Where
scopeis optionally one of:Checklist
git commit --signoffmake testmake reviewableSummary
Adds complete integration of Nebius Mysterybox Secret Service as an External Secrets Operator provider (addresses #5852). Implemented as a direct integration due to authorization constraints with the gRPC plugin bridge approach.
Core Implementation
API & CRD Types:
NebiusAuth(service account credentials or token),NebiusCAProvider,NebiusMysteryboxProviderSecretStoreProviderto includeNebiusMysteryboxfieldProvider (
providers/v1/nebius/mysterybox/):SecretsClientimplementing read-only operations:GetSecretandGetSecretMapSDK Components (
providers/v1/nebius/common/sdk/):Documentation & Configuration:
Testing:
Key Design Decisions