config.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. /*
  2. Copyright 2014 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 authenticator
  14. import (
  15. "time"
  16. "github.com/go-openapi/spec"
  17. "k8s.io/apiserver/pkg/authentication/authenticator"
  18. "k8s.io/apiserver/pkg/authentication/authenticatorfactory"
  19. "k8s.io/apiserver/pkg/authentication/group"
  20. "k8s.io/apiserver/pkg/authentication/request/anonymous"
  21. "k8s.io/apiserver/pkg/authentication/request/bearertoken"
  22. "k8s.io/apiserver/pkg/authentication/request/headerrequest"
  23. "k8s.io/apiserver/pkg/authentication/request/union"
  24. "k8s.io/apiserver/pkg/authentication/request/websocket"
  25. "k8s.io/apiserver/pkg/authentication/request/x509"
  26. tokencache "k8s.io/apiserver/pkg/authentication/token/cache"
  27. "k8s.io/apiserver/pkg/authentication/token/tokenfile"
  28. tokenunion "k8s.io/apiserver/pkg/authentication/token/union"
  29. "k8s.io/apiserver/pkg/server/dynamiccertificates"
  30. utilfeature "k8s.io/apiserver/pkg/util/feature"
  31. "k8s.io/apiserver/plugin/pkg/authenticator/password/passwordfile"
  32. "k8s.io/apiserver/plugin/pkg/authenticator/request/basicauth"
  33. "k8s.io/apiserver/plugin/pkg/authenticator/token/oidc"
  34. "k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
  35. // Initialize all known client auth plugins.
  36. _ "k8s.io/client-go/plugin/pkg/client/auth"
  37. "k8s.io/client-go/util/keyutil"
  38. "k8s.io/kubernetes/pkg/features"
  39. "k8s.io/kubernetes/pkg/serviceaccount"
  40. )
  41. // Config contains the data on how to authenticate a request to the Kube API Server
  42. type Config struct {
  43. Anonymous bool
  44. BasicAuthFile string
  45. BootstrapToken bool
  46. TokenAuthFile string
  47. OIDCIssuerURL string
  48. OIDCClientID string
  49. OIDCCAFile string
  50. OIDCUsernameClaim string
  51. OIDCUsernamePrefix string
  52. OIDCGroupsClaim string
  53. OIDCGroupsPrefix string
  54. OIDCSigningAlgs []string
  55. OIDCRequiredClaims map[string]string
  56. ServiceAccountKeyFiles []string
  57. ServiceAccountLookup bool
  58. ServiceAccountIssuer string
  59. APIAudiences authenticator.Audiences
  60. WebhookTokenAuthnConfigFile string
  61. WebhookTokenAuthnVersion string
  62. WebhookTokenAuthnCacheTTL time.Duration
  63. TokenSuccessCacheTTL time.Duration
  64. TokenFailureCacheTTL time.Duration
  65. RequestHeaderConfig *authenticatorfactory.RequestHeaderConfig
  66. // TODO, this is the only non-serializable part of the entire config. Factor it out into a clientconfig
  67. ServiceAccountTokenGetter serviceaccount.ServiceAccountTokenGetter
  68. BootstrapTokenAuthenticator authenticator.Token
  69. // ClientCAContentProvider are the options for verifying incoming connections using mTLS and directly assigning to users.
  70. // Generally this is the CA bundle file used to authenticate client certificates
  71. // If this value is nil, then mutual TLS is disabled.
  72. ClientCAContentProvider dynamiccertificates.CAContentProvider
  73. }
  74. // New returns an authenticator.Request or an error that supports the standard
  75. // Kubernetes authentication mechanisms.
  76. func (config Config) New() (authenticator.Request, *spec.SecurityDefinitions, error) {
  77. var authenticators []authenticator.Request
  78. var tokenAuthenticators []authenticator.Token
  79. securityDefinitions := spec.SecurityDefinitions{}
  80. // front-proxy, BasicAuth methods, local first, then remote
  81. // Add the front proxy authenticator if requested
  82. if config.RequestHeaderConfig != nil {
  83. requestHeaderAuthenticator := headerrequest.NewDynamicVerifyOptionsSecure(
  84. config.RequestHeaderConfig.CAContentProvider.VerifyOptions,
  85. config.RequestHeaderConfig.AllowedClientNames,
  86. config.RequestHeaderConfig.UsernameHeaders,
  87. config.RequestHeaderConfig.GroupHeaders,
  88. config.RequestHeaderConfig.ExtraHeaderPrefixes,
  89. )
  90. authenticators = append(authenticators, authenticator.WrapAudienceAgnosticRequest(config.APIAudiences, requestHeaderAuthenticator))
  91. }
  92. // basic auth
  93. if len(config.BasicAuthFile) > 0 {
  94. basicAuth, err := newAuthenticatorFromBasicAuthFile(config.BasicAuthFile)
  95. if err != nil {
  96. return nil, nil, err
  97. }
  98. authenticators = append(authenticators, authenticator.WrapAudienceAgnosticRequest(config.APIAudiences, basicAuth))
  99. securityDefinitions["HTTPBasic"] = &spec.SecurityScheme{
  100. SecuritySchemeProps: spec.SecuritySchemeProps{
  101. Type: "basic",
  102. Description: "HTTP Basic authentication",
  103. },
  104. }
  105. }
  106. // X509 methods
  107. if config.ClientCAContentProvider != nil {
  108. certAuth := x509.NewDynamic(config.ClientCAContentProvider.VerifyOptions, x509.CommonNameUserConversion)
  109. authenticators = append(authenticators, certAuth)
  110. }
  111. // Bearer token methods, local first, then remote
  112. if len(config.TokenAuthFile) > 0 {
  113. tokenAuth, err := newAuthenticatorFromTokenFile(config.TokenAuthFile)
  114. if err != nil {
  115. return nil, nil, err
  116. }
  117. tokenAuthenticators = append(tokenAuthenticators, authenticator.WrapAudienceAgnosticToken(config.APIAudiences, tokenAuth))
  118. }
  119. if len(config.ServiceAccountKeyFiles) > 0 {
  120. serviceAccountAuth, err := newLegacyServiceAccountAuthenticator(config.ServiceAccountKeyFiles, config.ServiceAccountLookup, config.APIAudiences, config.ServiceAccountTokenGetter)
  121. if err != nil {
  122. return nil, nil, err
  123. }
  124. tokenAuthenticators = append(tokenAuthenticators, serviceAccountAuth)
  125. }
  126. if utilfeature.DefaultFeatureGate.Enabled(features.TokenRequest) && config.ServiceAccountIssuer != "" {
  127. serviceAccountAuth, err := newServiceAccountAuthenticator(config.ServiceAccountIssuer, config.ServiceAccountKeyFiles, config.APIAudiences, config.ServiceAccountTokenGetter)
  128. if err != nil {
  129. return nil, nil, err
  130. }
  131. tokenAuthenticators = append(tokenAuthenticators, serviceAccountAuth)
  132. }
  133. if config.BootstrapToken {
  134. if config.BootstrapTokenAuthenticator != nil {
  135. // TODO: This can sometimes be nil because of
  136. tokenAuthenticators = append(tokenAuthenticators, authenticator.WrapAudienceAgnosticToken(config.APIAudiences, config.BootstrapTokenAuthenticator))
  137. }
  138. }
  139. // NOTE(ericchiang): Keep the OpenID Connect after Service Accounts.
  140. //
  141. // Because both plugins verify JWTs whichever comes first in the union experiences
  142. // cache misses for all requests using the other. While the service account plugin
  143. // simply returns an error, the OpenID Connect plugin may query the provider to
  144. // update the keys, causing performance hits.
  145. if len(config.OIDCIssuerURL) > 0 && len(config.OIDCClientID) > 0 {
  146. oidcAuth, err := newAuthenticatorFromOIDCIssuerURL(oidc.Options{
  147. IssuerURL: config.OIDCIssuerURL,
  148. ClientID: config.OIDCClientID,
  149. CAFile: config.OIDCCAFile,
  150. UsernameClaim: config.OIDCUsernameClaim,
  151. UsernamePrefix: config.OIDCUsernamePrefix,
  152. GroupsClaim: config.OIDCGroupsClaim,
  153. GroupsPrefix: config.OIDCGroupsPrefix,
  154. SupportedSigningAlgs: config.OIDCSigningAlgs,
  155. RequiredClaims: config.OIDCRequiredClaims,
  156. })
  157. if err != nil {
  158. return nil, nil, err
  159. }
  160. tokenAuthenticators = append(tokenAuthenticators, authenticator.WrapAudienceAgnosticToken(config.APIAudiences, oidcAuth))
  161. }
  162. if len(config.WebhookTokenAuthnConfigFile) > 0 {
  163. webhookTokenAuth, err := newWebhookTokenAuthenticator(config.WebhookTokenAuthnConfigFile, config.WebhookTokenAuthnVersion, config.WebhookTokenAuthnCacheTTL, config.APIAudiences)
  164. if err != nil {
  165. return nil, nil, err
  166. }
  167. tokenAuthenticators = append(tokenAuthenticators, webhookTokenAuth)
  168. }
  169. if len(tokenAuthenticators) > 0 {
  170. // Union the token authenticators
  171. tokenAuth := tokenunion.New(tokenAuthenticators...)
  172. // Optionally cache authentication results
  173. if config.TokenSuccessCacheTTL > 0 || config.TokenFailureCacheTTL > 0 {
  174. tokenAuth = tokencache.New(tokenAuth, true, config.TokenSuccessCacheTTL, config.TokenFailureCacheTTL)
  175. }
  176. authenticators = append(authenticators, bearertoken.New(tokenAuth), websocket.NewProtocolAuthenticator(tokenAuth))
  177. securityDefinitions["BearerToken"] = &spec.SecurityScheme{
  178. SecuritySchemeProps: spec.SecuritySchemeProps{
  179. Type: "apiKey",
  180. Name: "authorization",
  181. In: "header",
  182. Description: "Bearer Token authentication",
  183. },
  184. }
  185. }
  186. if len(authenticators) == 0 {
  187. if config.Anonymous {
  188. return anonymous.NewAuthenticator(), &securityDefinitions, nil
  189. }
  190. return nil, &securityDefinitions, nil
  191. }
  192. authenticator := union.New(authenticators...)
  193. authenticator = group.NewAuthenticatedGroupAdder(authenticator)
  194. if config.Anonymous {
  195. // If the authenticator chain returns an error, return an error (don't consider a bad bearer token
  196. // or invalid username/password combination anonymous).
  197. authenticator = union.NewFailOnError(authenticator, anonymous.NewAuthenticator())
  198. }
  199. return authenticator, &securityDefinitions, nil
  200. }
  201. // IsValidServiceAccountKeyFile returns true if a valid public RSA key can be read from the given file
  202. func IsValidServiceAccountKeyFile(file string) bool {
  203. _, err := keyutil.PublicKeysFromFile(file)
  204. return err == nil
  205. }
  206. // newAuthenticatorFromBasicAuthFile returns an authenticator.Request or an error
  207. func newAuthenticatorFromBasicAuthFile(basicAuthFile string) (authenticator.Request, error) {
  208. basicAuthenticator, err := passwordfile.NewCSV(basicAuthFile)
  209. if err != nil {
  210. return nil, err
  211. }
  212. return basicauth.New(basicAuthenticator), nil
  213. }
  214. // newAuthenticatorFromTokenFile returns an authenticator.Token or an error
  215. func newAuthenticatorFromTokenFile(tokenAuthFile string) (authenticator.Token, error) {
  216. tokenAuthenticator, err := tokenfile.NewCSV(tokenAuthFile)
  217. if err != nil {
  218. return nil, err
  219. }
  220. return tokenAuthenticator, nil
  221. }
  222. // newAuthenticatorFromOIDCIssuerURL returns an authenticator.Token or an error.
  223. func newAuthenticatorFromOIDCIssuerURL(opts oidc.Options) (authenticator.Token, error) {
  224. const noUsernamePrefix = "-"
  225. if opts.UsernamePrefix == "" && opts.UsernameClaim != "email" {
  226. // Old behavior. If a usernamePrefix isn't provided, prefix all claims other than "email"
  227. // with the issuerURL.
  228. //
  229. // See https://github.com/kubernetes/kubernetes/issues/31380
  230. opts.UsernamePrefix = opts.IssuerURL + "#"
  231. }
  232. if opts.UsernamePrefix == noUsernamePrefix {
  233. // Special value indicating usernames shouldn't be prefixed.
  234. opts.UsernamePrefix = ""
  235. }
  236. tokenAuthenticator, err := oidc.New(opts)
  237. if err != nil {
  238. return nil, err
  239. }
  240. return tokenAuthenticator, nil
  241. }
  242. // newLegacyServiceAccountAuthenticator returns an authenticator.Token or an error
  243. func newLegacyServiceAccountAuthenticator(keyfiles []string, lookup bool, apiAudiences authenticator.Audiences, serviceAccountGetter serviceaccount.ServiceAccountTokenGetter) (authenticator.Token, error) {
  244. allPublicKeys := []interface{}{}
  245. for _, keyfile := range keyfiles {
  246. publicKeys, err := keyutil.PublicKeysFromFile(keyfile)
  247. if err != nil {
  248. return nil, err
  249. }
  250. allPublicKeys = append(allPublicKeys, publicKeys...)
  251. }
  252. tokenAuthenticator := serviceaccount.JWTTokenAuthenticator(serviceaccount.LegacyIssuer, allPublicKeys, apiAudiences, serviceaccount.NewLegacyValidator(lookup, serviceAccountGetter))
  253. return tokenAuthenticator, nil
  254. }
  255. // newServiceAccountAuthenticator returns an authenticator.Token or an error
  256. func newServiceAccountAuthenticator(iss string, keyfiles []string, apiAudiences authenticator.Audiences, serviceAccountGetter serviceaccount.ServiceAccountTokenGetter) (authenticator.Token, error) {
  257. allPublicKeys := []interface{}{}
  258. for _, keyfile := range keyfiles {
  259. publicKeys, err := keyutil.PublicKeysFromFile(keyfile)
  260. if err != nil {
  261. return nil, err
  262. }
  263. allPublicKeys = append(allPublicKeys, publicKeys...)
  264. }
  265. tokenAuthenticator := serviceaccount.JWTTokenAuthenticator(iss, allPublicKeys, apiAudiences, serviceaccount.NewValidator(serviceAccountGetter))
  266. return tokenAuthenticator, nil
  267. }
  268. func newWebhookTokenAuthenticator(webhookConfigFile string, version string, ttl time.Duration, implicitAuds authenticator.Audiences) (authenticator.Token, error) {
  269. webhookTokenAuthenticator, err := webhook.New(webhookConfigFile, version, implicitAuds)
  270. if err != nil {
  271. return nil, err
  272. }
  273. return tokencache.New(webhookTokenAuthenticator, false, ttl, ttl), nil
  274. }