-
Notifications
You must be signed in to change notification settings - Fork 583
RemoteMCPServer TLS + companion-Secret API + plaintext egress rewrite #1905
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ce995b7
9d90c17
031616d
0a5f8bf
0f263f6
c7707a2
eefa465
6a57b14
70d3a25
3b76c2c
2d45a9e
087d80b
80471bf
8e3db22
29411cc
c96ed03
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -274,9 +274,14 @@ type SAPAICoreConfig struct { | |
| AuthURL string `json:"authUrl,omitempty"` | ||
| } | ||
|
|
||
| // TLSConfig contains TLS/SSL configuration options for model provider connections. | ||
| // This enables agents to connect to internal LiteLLM gateways or other providers | ||
| // that use self-signed certificates or custom certificate authorities. | ||
| // TLSConfig contains TLS/SSL configuration options for outbound HTTPS | ||
| // connections from the agent (model provider, RemoteMCPServer). The | ||
| // XValidation rules below apply at admission to every CRD field that | ||
| // uses TLSConfig, so callers don't need to re-declare them per spec. | ||
| // | ||
| // +kubebuilder:validation:XValidation:message="caCertSecretKey requires caCertSecretRef",rule="!(has(self.caCertSecretKey) && size(self.caCertSecretKey) > 0 && (!has(self.caCertSecretRef) || size(self.caCertSecretRef) == 0))" | ||
| // +kubebuilder:validation:XValidation:message="caCertSecretRef requires caCertSecretKey",rule="!(has(self.caCertSecretRef) && size(self.caCertSecretRef) > 0 && (!has(self.caCertSecretKey) || size(self.caCertSecretKey) == 0))" | ||
| // +kubebuilder:validation:XValidation:message="disableSystemCAs requires caCertSecretRef or disableVerify (trust-nothing config rejects every upstream)",rule="!(has(self.disableSystemCAs) && self.disableSystemCAs && (!has(self.disableVerify) || !self.disableVerify) && (!has(self.caCertSecretRef) || size(self.caCertSecretRef) == 0))" | ||
| type TLSConfig struct { | ||
| // DisableVerify disables SSL certificate verification entirely. | ||
| // When false (default), SSL certificates are verified. | ||
|
|
@@ -289,15 +294,17 @@ type TLSConfig struct { | |
|
|
||
| // CACertSecretRef is a reference to a Kubernetes Secret containing | ||
| // CA certificate(s) in PEM format. The Secret must be in the same | ||
| // namespace as the ModelConfig. | ||
| // When set, the certificate will be used to verify the provider's SSL certificate. | ||
| // This field follows the same pattern as APIKeySecret. | ||
| // namespace as the resource referencing it (ModelConfig, | ||
| // RemoteMCPServer, or any future consumer of TLSConfig). | ||
| // When set, the certificate will be used to verify the upstream's | ||
| // SSL certificate. | ||
| // +optional | ||
| CACertSecretRef string `json:"caCertSecretRef,omitempty"` | ||
|
|
||
| // CACertSecretKey is the key within the Secret that contains the CA certificate data. | ||
| // This field follows the same pattern as APIKeySecretKey. | ||
| // Required when CACertSecretRef is set (unless DisableVerify is true). | ||
| // CACertSecretKey is the key within the Secret that contains the | ||
| // CA certificate data (PEM-encoded). Required when CACertSecretRef | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this required, can't we infer a single key if there is one? I see it's required today but not enforced via
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can definitely infer if there is only one key. This just keeps with the existing modelconfig approach for now. Also, this is more extensible. We can make sure that there's a default and it's not required, but I think it's just beneficial to have the ability to specify it here. For example, cert-manager-generated TLS secrets have |
||
| // is set — admission rejects ref-without-key regardless of | ||
| // DisableVerify (see the TLSConfig-level XValidation rules). | ||
| // +optional | ||
| CACertSecretKey string `json:"caCertSecretKey,omitempty"` | ||
|
|
||
|
|
@@ -310,6 +317,19 @@ type TLSConfig struct { | |
| DisableSystemCAs bool `json:"disableSystemCAs,omitempty"` | ||
| } | ||
|
|
||
| // IsEmpty reports whether the TLSConfig carries any opinion. A nil | ||
| // receiver and an all-zero struct are equivalent — both mean "no TLS | ||
| // config supplied" and the consumer should fall back to its default | ||
| // behavior (typically system trust store, default httpx client). The | ||
| // single helper keeps callers from re-listing fields, so adding a new | ||
| // field to TLSConfig only requires updating this method. | ||
| func (t *TLSConfig) IsEmpty() bool { | ||
| if t == nil { | ||
| return true | ||
| } | ||
| return !t.DisableVerify && t.CACertSecretRef == "" && t.CACertSecretKey == "" && !t.DisableSystemCAs | ||
| } | ||
|
|
||
| // ModelConfigSpec defines the desired state of ModelConfig. | ||
| // | ||
| // +kubebuilder:validation:XValidation:message="provider.openAI must be nil if the provider is not OpenAI",rule="!(has(self.openAI) && self.provider != 'OpenAI')" | ||
|
|
@@ -325,9 +345,6 @@ type TLSConfig struct { | |
| // +kubebuilder:validation:XValidation:message="apiKeySecretKey must be set if apiKeySecret is set (except for Bedrock and SAPAICore providers)",rule="!(has(self.apiKeySecret) && !has(self.apiKeySecretKey) && self.provider != 'Bedrock' && self.provider != 'SAPAICore')" | ||
| // +kubebuilder:validation:XValidation:message="apiKeyPassthrough and apiKeySecret are mutually exclusive",rule="!(has(self.apiKeyPassthrough) && self.apiKeyPassthrough && has(self.apiKeySecret) && size(self.apiKeySecret) > 0)" | ||
| // +kubebuilder:validation:XValidation:message="apiKeyPassthrough must be false if provider is Gemini;GeminiVertexAI;AnthropicVertexAI",rule="!(has(self.apiKeyPassthrough) && self.apiKeyPassthrough && (self.provider == 'Gemini' || self.provider == 'GeminiVertexAI' || self.provider == 'AnthropicVertexAI'))" | ||
| // +kubebuilder:validation:XValidation:message="caCertSecretKey requires caCertSecretRef",rule="!(has(self.tls) && has(self.tls.caCertSecretKey) && size(self.tls.caCertSecretKey) > 0 && (!has(self.tls.caCertSecretRef) || size(self.tls.caCertSecretRef) == 0))" | ||
| // +kubebuilder:validation:XValidation:message="caCertSecretKey requires caCertSecretRef (unless disableVerify is true)",rule="!(has(self.tls) && (!has(self.tls.disableVerify) || !self.tls.disableVerify) && has(self.tls.caCertSecretKey) && size(self.tls.caCertSecretKey) > 0 && (!has(self.tls.caCertSecretRef) || size(self.tls.caCertSecretRef) == 0))" | ||
| // +kubebuilder:validation:XValidation:message="caCertSecretRef requires caCertSecretKey (unless disableVerify is true)",rule="!(has(self.tls) && (!has(self.tls.disableVerify) || !self.tls.disableVerify) && has(self.tls.caCertSecretRef) && size(self.tls.caCertSecretRef) > 0 && (!has(self.tls.caCertSecretKey) || size(self.tls.caCertSecretKey) == 0))" | ||
| // +kubebuilder:validation:XValidation:message="openAI.tokenExchange requires apiKeySecret (the service account secret)",rule="!(has(self.openAI) && has(self.openAI.tokenExchange) && (!has(self.apiKeySecret) || size(self.apiKeySecret) == 0))" | ||
| // +kubebuilder:validation:XValidation:message="openAI.tokenExchange and apiKeyPassthrough are mutually exclusive",rule="!(has(self.openAI) && has(self.openAI.tokenExchange) && has(self.apiKeyPassthrough) && self.apiKeyPassthrough)" | ||
| // +kubebuilder:validation:XValidation:message="openAI.tokenExchange type GDCHServiceAccount requires openAI.tokenExchange.gdchServiceAccount",rule="!(has(self.openAI) && has(self.openAI.tokenExchange) && self.openAI.tokenExchange.type == 'GDCHServiceAccount' && !has(self.openAI.tokenExchange.gdchServiceAccount))" | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.