openidmetadata.go 4.0 KB

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