From e7f84249292cd13e30ae5484ed3b50424588728d Mon Sep 17 00:00:00 2001 From: Johannes Malsam Date: Tue, 2 Sep 2025 11:40:15 +0200 Subject: [PATCH 1/5] add Fallback to internal k8s svc for FM/FS k8s auth --- pkg/client/iam/config.go | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/pkg/client/iam/config.go b/pkg/client/iam/config.go index 01479987b0..13805525a9 100644 --- a/pkg/client/iam/config.go +++ b/pkg/client/iam/config.go @@ -291,11 +291,35 @@ func (i *KubernetesIssuer) fetchJwks() ([]byte, error) { if err != nil { return nil, fmt.Errorf("get JWKS URI for k8s issuer %s: %w", i.IssuerURI, err) } + resp, err := client.Get(jwksURI) + if err == nil { + defer resp.Body.Close() + return i.readAndReturnJWKS(resp) + } + + if strings.Contains(jwksURI, kubernetesIssuer) { + return nil, fmt.Errorf("failed to fetch JWKS from: %q, with error: %w", err) + } + + glog.Infof("failed to fetch JWKS from: %q with: %v", jwksURI, err) + glog.Info("trying internal JWKS endpoint instead") + + jwksURL, err := url.Parse(jwksURI) + if err != nil { + return nil, fmt.Errorf("failed to parse jwksURI: %q as URL: %w", jwksURI, err) + } + + internalJwksURI := i.overrideJwksURIForInternalCluster(jwksURL) + resp, err = client.Get(internalJwksURI) if err != nil { - return nil, fmt.Errorf("fetch jwks from k8s issuer %s: %w", i.IssuerURI, err) + return nil, fmt.Errorf("request to internal JWKS endpoint: %q failed: %w", internalJwksURI, err) } defer resp.Body.Close() + return i.readAndReturnJWKS(resp) +} + +func (i *KubernetesIssuer) readAndReturnJWKS(resp *http.Response) ([]byte, error) { bytes, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("reading JWKS response: %w", err) From e6d61d69c8db4067e0d28d4d06edf17e5d96143f Mon Sep 17 00:00:00 2001 From: Johannes Malsam Date: Tue, 2 Sep 2025 12:10:33 +0200 Subject: [PATCH 2/5] fix --- pkg/client/iam/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/client/iam/config.go b/pkg/client/iam/config.go index 13805525a9..60cf3b9b35 100644 --- a/pkg/client/iam/config.go +++ b/pkg/client/iam/config.go @@ -299,7 +299,7 @@ func (i *KubernetesIssuer) fetchJwks() ([]byte, error) { } if strings.Contains(jwksURI, kubernetesIssuer) { - return nil, fmt.Errorf("failed to fetch JWKS from: %q, with error: %w", err) + return nil, fmt.Errorf("failed to fetch JWKS from: %q, with error: %w", jwksURI, err) } glog.Infof("failed to fetch JWKS from: %q with: %v", jwksURI, err) From 5beab0ac0587652262d2a56d7c459d508f8d1b8f Mon Sep 17 00:00:00 2001 From: Johannes Malsam Date: Tue, 2 Sep 2025 13:35:57 +0200 Subject: [PATCH 3/5] fix and fallback also for IP in JWKS --- pkg/client/iam/config.go | 41 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/pkg/client/iam/config.go b/pkg/client/iam/config.go index 60cf3b9b35..e863c2944b 100644 --- a/pkg/client/iam/config.go +++ b/pkg/client/iam/config.go @@ -8,6 +8,7 @@ import ( "net/http" "net/url" "os" + "path" "path/filepath" "strings" "time" @@ -233,6 +234,7 @@ func (ic *IAMConfig) GetDataPlaneIssuerURIs() []string { const ( openidConfigurationPath = "/.well-known/openid-configuration" + defaultJwksPath = "/openid/v1/jwks" kubernetesIssuer = "https://kubernetes.default.svc" jwksTempFilePattern = "k8s-jwks-*.json" ) @@ -295,7 +297,7 @@ func (i *KubernetesIssuer) fetchJwks() ([]byte, error) { resp, err := client.Get(jwksURI) if err == nil { defer resp.Body.Close() - return i.readAndReturnJWKS(resp) + return i.readResponseAndReturnJWKS(resp) } if strings.Contains(jwksURI, kubernetesIssuer) { @@ -305,25 +307,25 @@ func (i *KubernetesIssuer) fetchJwks() ([]byte, error) { glog.Infof("failed to fetch JWKS from: %q with: %v", jwksURI, err) glog.Info("trying internal JWKS endpoint instead") - jwksURL, err := url.Parse(jwksURI) - if err != nil { - return nil, fmt.Errorf("failed to parse jwksURI: %q as URL: %w", jwksURI, err) - } - - internalJwksURI := i.overrideJwksURIForInternalCluster(jwksURL) + internalJwksURI := path.Join(kubernetesIssuer, defaultJwksPath) resp, err = client.Get(internalJwksURI) if err != nil { return nil, fmt.Errorf("request to internal JWKS endpoint: %q failed: %w", internalJwksURI, err) } defer resp.Body.Close() - return i.readAndReturnJWKS(resp) + return i.readResponseAndReturnJWKS(resp) } -func (i *KubernetesIssuer) readAndReturnJWKS(resp *http.Response) ([]byte, error) { +func (i *KubernetesIssuer) readResponseAndReturnJWKS(resp *http.Response) ([]byte, error) { bytes, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("reading JWKS response: %w", err) } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to get JWKS with status: %d, body: %q", resp.StatusCode, string(bytes)) + } + return bytes, nil } @@ -351,19 +353,6 @@ func (i *KubernetesIssuer) getJwksURI(client *http.Client) (string, error) { jwksURI = i.overrideJwksURIForLocalCluster(jwksURI) } - jwksURL, err := url.Parse(jwksURI) - if err != nil { - return "", errors.Wrapf(err, "failed to parse jwksURI as net/url: %s", jwksURI) - } - - if netutil.IsIPAddress(jwksURL.Hostname()) && i.IssuerURI == kubernetesIssuer { - // in some cases like infra OCP the cluster internal jwks_uri in the discovery document - // is a private IP address of the pod running the oidc server. This breaks tls validation. - // This override makes sure that in those cases kubernetes.default.svc is used instead of the IP - glog.V(5).Infof("Configured issuer is: %s and jwks_uri contains IP, replacing host with internal kubernetes svc", i.IssuerURI) - jwksURI = i.overrideJwksURIForInternalCluster(jwksURL) - } - if cfg.Issuer != i.IssuerURI { glog.V(5).Infof("Configured issuer URI does't match the issuer URI configured in the discovery document, overriding: [configured: %s, got: %s]", i.IssuerURI, cfg.Issuer) i.IssuerURI = cfg.Issuer @@ -386,14 +375,6 @@ func (i *KubernetesIssuer) overrideJwksURIForLocalCluster(jwksURI string) string return jwksURL.String() } -func (i *KubernetesIssuer) overrideJwksURIForInternalCluster(url *url.URL) string { - k8sSvcHost := strings.TrimPrefix(kubernetesIssuer, "https://") - k8sSvcHost = strings.TrimPrefix(k8sSvcHost, "http://") - url.Host = k8sSvcHost - - return url.String() -} - func (i *KubernetesIssuer) buildK8sConfig() (*rest.Config, error) { // Special case for local dev environments: Fleet Manager manages local cluster, assuming kubeconfig exists if i.isLocalCluster() { From 6d25be311c1d2bcd8e98e7809044f94de85a935e Mon Sep 17 00:00:00 2001 From: Johannes Malsam Date: Tue, 2 Sep 2025 13:48:44 +0200 Subject: [PATCH 4/5] fix jwks path join --- pkg/client/iam/config.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/client/iam/config.go b/pkg/client/iam/config.go index e863c2944b..22e369c799 100644 --- a/pkg/client/iam/config.go +++ b/pkg/client/iam/config.go @@ -8,7 +8,6 @@ import ( "net/http" "net/url" "os" - "path" "path/filepath" "strings" "time" @@ -307,7 +306,10 @@ func (i *KubernetesIssuer) fetchJwks() ([]byte, error) { glog.Infof("failed to fetch JWKS from: %q with: %v", jwksURI, err) glog.Info("trying internal JWKS endpoint instead") - internalJwksURI := path.Join(kubernetesIssuer, defaultJwksPath) + internalJwksURI, err := url.JoinPath(kubernetesIssuer, defaultJwksPath) + if err != nil { + return nil, fmt.Errorf("failed to join JWKS base %q and path %q with err: %w", kubernetesIssuer, defaultJwksPath, err) + } resp, err = client.Get(internalJwksURI) if err != nil { return nil, fmt.Errorf("request to internal JWKS endpoint: %q failed: %w", internalJwksURI, err) From ed9464758a7dd656910844202c31bd1fae201d7e Mon Sep 17 00:00:00 2001 From: Johannes Malsam Date: Tue, 2 Sep 2025 15:28:45 +0200 Subject: [PATCH 5/5] PR feedback --- pkg/client/iam/config.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/client/iam/config.go b/pkg/client/iam/config.go index 22e369c799..c33b176a25 100644 --- a/pkg/client/iam/config.go +++ b/pkg/client/iam/config.go @@ -303,8 +303,7 @@ func (i *KubernetesIssuer) fetchJwks() ([]byte, error) { return nil, fmt.Errorf("failed to fetch JWKS from: %q, with error: %w", jwksURI, err) } - glog.Infof("failed to fetch JWKS from: %q with: %v", jwksURI, err) - glog.Info("trying internal JWKS endpoint instead") + glog.V(5).Infof("failed to fetch JWKS from: %q with: %v\n trying internal JWKS endpoint instead", jwksURI, err) internalJwksURI, err := url.JoinPath(kubernetesIssuer, defaultJwksPath) if err != nil {