-
Notifications
You must be signed in to change notification settings - Fork 605
Add custom API server endpoint configuration for proxy scenarios #4635
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 16 commits
a805367
01db26c
7051d8f
ef33855
55cc354
bd2ff57
3ca5e5b
af843d9
b01a0ea
0e82afc
e38dcfb
84a51f7
940030a
5042459
caad2ff
2007e3d
738c6e0
8d7a767
28eef5f
8573a16
2b0d627
99de9bc
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 |
|---|---|---|
|
|
@@ -1001,6 +1001,82 @@ func splitKubeConfigPath(path string) []string { | |
| return strings.Split(path, delimiter) | ||
| } | ||
|
|
||
| // validateAPIServerEndpoint validates and returns a trimmed API server endpoint. | ||
| // Returns empty string if endpoint is empty, or an error if endpoint is invalid. | ||
| func validateAPIServerEndpoint(endpoint string) (string, error) { | ||
| trimmed := strings.TrimSpace(endpoint) | ||
| if trimmed == "" { | ||
| return "", nil | ||
| } | ||
|
|
||
| parsedURL, err := url.Parse(trimmed) | ||
| if err != nil || !parsedURL.IsAbs() || parsedURL.Host == "" || parsedURL.Hostname() == "" { | ||
| return "", fmt.Errorf( | ||
| "invalid custom API server endpoint %q: must be an absolute URL with scheme and host", | ||
| trimmed, | ||
| ) | ||
| } | ||
|
Comment on lines
+1013
to
+1019
|
||
|
|
||
| if parsedURL.Scheme != "https" { | ||
| return "", fmt.Errorf( | ||
| "invalid custom API server endpoint %q: must be a full https:// URL", | ||
| trimmed, | ||
| ) | ||
| } | ||
|
|
||
illume marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // Disallow embedded credentials, query strings, fragments, and non-root paths | ||
| if parsedURL.User != nil { | ||
| return "", fmt.Errorf( | ||
| "invalid custom API server endpoint %q: must not include user info (credentials)", | ||
| trimmed, | ||
| ) | ||
| } | ||
|
|
||
| if parsedURL.RawQuery != "" { | ||
| return "", fmt.Errorf( | ||
| "invalid custom API server endpoint %q: must not include a query string", | ||
| trimmed, | ||
| ) | ||
| } | ||
|
|
||
| if parsedURL.Fragment != "" { | ||
| return "", fmt.Errorf( | ||
| "invalid custom API server endpoint %q: must not include a fragment", | ||
| trimmed, | ||
| ) | ||
| } | ||
|
|
||
| if parsedURL.Path != "" && parsedURL.Path != "/" { | ||
| return "", fmt.Errorf( | ||
| "invalid custom API server endpoint %q: path must be empty or '/' (scheme+host[:port] only)", | ||
| trimmed, | ||
illume marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ) | ||
| } | ||
|
|
||
| return trimmed, nil | ||
| } | ||
illume marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // buildOIDCConfig creates an OIDC configuration if the required parameters are provided. | ||
| func buildOIDCConfig( | ||
| clientID, issuerURL, scopes string, | ||
| clientSecret string, | ||
| skipTLSVerify bool, | ||
| caCert string, | ||
| ) *OidcConfig { | ||
| if clientID == "" || issuerURL == "" || scopes == "" { | ||
| return nil | ||
| } | ||
|
|
||
| return &OidcConfig{ | ||
| ClientID: clientID, | ||
| ClientSecret: clientSecret, | ||
| IdpIssuerURL: issuerURL, | ||
| Scopes: strings.Split(scopes, ","), | ||
| SkipTLSVerify: &skipTLSVerify, | ||
| CACert: &caCert, | ||
| } | ||
| } | ||
|
|
||
| // GetInClusterContext returns the in-cluster context. | ||
| func GetInClusterContext( | ||
| contextName string, | ||
|
|
@@ -1009,14 +1085,28 @@ func GetInClusterContext( | |
| oidcScopes string, | ||
| oidcSkipTLSVerify bool, | ||
| oidcCACert string, | ||
| customAPIServerEndpoint string, | ||
| ) (*Context, error) { | ||
| // Validate custom endpoint first, before attempting to load in-cluster config | ||
| customEndpoint, err := validateAPIServerEndpoint(customAPIServerEndpoint) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| clusterConfig, err := rest.InClusterConfig() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| // Use custom API server endpoint if provided, otherwise use default from in-cluster config | ||
| apiServerHost := clusterConfig.Host | ||
|
|
||
| if customEndpoint != "" { | ||
| apiServerHost = customEndpoint | ||
| } | ||
|
|
||
| cluster := &api.Cluster{ | ||
| Server: clusterConfig.Host, | ||
| Server: apiServerHost, | ||
| CertificateAuthority: clusterConfig.CAFile, | ||
| CertificateAuthorityData: clusterConfig.CAData, | ||
| } | ||
|
|
@@ -1033,19 +1123,14 @@ func GetInClusterContext( | |
|
|
||
| inClusterAuthInfo := &api.AuthInfo{} | ||
|
|
||
| var oidcConf *OidcConfig | ||
|
|
||
| if oidcClientID != "" && oidcIssuerURL != "" && oidcScopes != "" { | ||
| // client secret is optional for in-cluster OIDC configuration | ||
| oidcConf = &OidcConfig{ | ||
| ClientID: oidcClientID, | ||
| ClientSecret: oidcClientSecret, | ||
| IdpIssuerURL: oidcIssuerURL, | ||
| Scopes: strings.Split(oidcScopes, ","), | ||
| SkipTLSVerify: &oidcSkipTLSVerify, | ||
| CACert: &oidcCACert, | ||
| } | ||
| } | ||
| oidcConf := buildOIDCConfig( | ||
| oidcClientID, | ||
| oidcIssuerURL, | ||
| oidcScopes, | ||
| oidcClientSecret, | ||
| oidcSkipTLSVerify, | ||
| oidcCACert, | ||
| ) | ||
|
|
||
| return &Context{ | ||
| Name: contextName, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.