k8s.io/kubernetes@v1.29.3/pkg/routes/openidmetadata.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package routes 18 19 import ( 20 "net/http" 21 22 restful "github.com/emicklei/go-restful/v3" 23 24 "k8s.io/klog/v2" 25 "k8s.io/kubernetes/pkg/serviceaccount" 26 ) 27 28 // This code is in package routes because many controllers import 29 // pkg/serviceaccount, but are not allowed by import-boss to depend on 30 // go-restful. All logic that deals with keys is kept in pkg/serviceaccount, 31 // and only the rendered JSON is passed into this server. 32 33 const ( 34 // cacheControl is the value of the Cache-Control header. Overrides the 35 // global `private, no-cache` setting. 36 headerCacheControl = "Cache-Control" 37 cacheControl = "public, max-age=3600" // 1 hour 38 39 // mimeJWKS is the content type of the keyset response 40 mimeJWKS = "application/jwk-set+json" 41 ) 42 43 // OpenIDMetadataServer is an HTTP server for metadata of the KSA token issuer. 44 type OpenIDMetadataServer struct { 45 configJSON []byte 46 keysetJSON []byte 47 } 48 49 // NewOpenIDMetadataServer creates a new OpenIDMetadataServer. 50 // The issuer is the OIDC issuer; keys are the keys that may be used to sign 51 // KSA tokens. 52 func NewOpenIDMetadataServer(configJSON, keysetJSON []byte) *OpenIDMetadataServer { 53 return &OpenIDMetadataServer{ 54 configJSON: configJSON, 55 keysetJSON: keysetJSON, 56 } 57 } 58 59 // Install adds this server to the request router c. 60 func (s *OpenIDMetadataServer) Install(c *restful.Container) { 61 // Configuration WebService 62 // Container.Add "will detect duplicate root paths and exit in that case", 63 // so we need a root for /.well-known/openid-configuration to avoid conflicts. 64 cfg := new(restful.WebService). 65 Produces(restful.MIME_JSON) 66 67 cfg.Path(serviceaccount.OpenIDConfigPath).Route( 68 cfg.GET(""). 69 To(fromStandard(s.serveConfiguration)). 70 Doc("get service account issuer OpenID configuration, also known as the 'OIDC discovery doc'"). 71 Operation("getServiceAccountIssuerOpenIDConfiguration"). 72 // Just include the OK, doesn't look like we include Internal Error in our openapi-spec. 73 Returns(http.StatusOK, "OK", "")) 74 c.Add(cfg) 75 76 // JWKS WebService 77 jwks := new(restful.WebService). 78 Produces(mimeJWKS) 79 80 jwks.Path(serviceaccount.JWKSPath).Route( 81 jwks.GET(""). 82 To(fromStandard(s.serveKeys)). 83 Doc("get service account issuer OpenID JSON Web Key Set (contains public token verification keys)"). 84 Operation("getServiceAccountIssuerOpenIDKeyset"). 85 // Just include the OK, doesn't look like we include Internal Error in our openapi-spec. 86 Returns(http.StatusOK, "OK", "")) 87 c.Add(jwks) 88 } 89 90 // fromStandard provides compatibility between the standard (net/http) handler signature and the restful signature. 91 func fromStandard(h http.HandlerFunc) restful.RouteFunction { 92 return func(req *restful.Request, resp *restful.Response) { 93 h(resp, req.Request) 94 } 95 } 96 97 func (s *OpenIDMetadataServer) serveConfiguration(w http.ResponseWriter, req *http.Request) { 98 w.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) 99 w.Header().Set(headerCacheControl, cacheControl) 100 if _, err := w.Write(s.configJSON); err != nil { 101 klog.Errorf("failed to write service account issuer metadata response: %v", err) 102 return 103 } 104 } 105 106 func (s *OpenIDMetadataServer) serveKeys(w http.ResponseWriter, req *http.Request) { 107 // Per RFC7517 : https://tools.ietf.org/html/rfc7517#section-8.5.1 108 w.Header().Set(restful.HEADER_ContentType, mimeJWKS) 109 w.Header().Set(headerCacheControl, cacheControl) 110 if _, err := w.Write(s.keysetJSON); err != nil { 111 klog.Errorf("failed to write service account issuer JWKS response: %v", err) 112 return 113 } 114 }