config.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. /*
  2. Copyright 2016 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 rest
  14. import (
  15. "context"
  16. "errors"
  17. "fmt"
  18. "net"
  19. "net/http"
  20. "net/url"
  21. "os"
  22. "path/filepath"
  23. gruntime "runtime"
  24. "strings"
  25. "time"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. "k8s.io/apimachinery/pkg/runtime"
  28. "k8s.io/apimachinery/pkg/runtime/schema"
  29. "k8s.io/apimachinery/pkg/runtime/serializer"
  30. "k8s.io/apimachinery/pkg/runtime/serializer/cbor"
  31. "k8s.io/client-go/features"
  32. "k8s.io/client-go/pkg/version"
  33. clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
  34. "k8s.io/client-go/transport"
  35. certutil "k8s.io/client-go/util/cert"
  36. "k8s.io/client-go/util/flowcontrol"
  37. "k8s.io/klog/v2"
  38. )
  39. const (
  40. DefaultQPS float32 = 5.0
  41. DefaultBurst int = 10
  42. )
  43. var ErrNotInCluster = errors.New("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined")
  44. // Config holds the common attributes that can be passed to a Kubernetes client on
  45. // initialization.
  46. type Config struct {
  47. // Host must be a host string, a host:port pair, or a URL to the base of the apiserver.
  48. // If a URL is given then the (optional) Path of that URL represents a prefix that must
  49. // be appended to all request URIs used to access the apiserver. This allows a frontend
  50. // proxy to easily relocate all of the apiserver endpoints.
  51. Host string
  52. // APIPath is a sub-path that points to an API root.
  53. APIPath string
  54. // ContentConfig contains settings that affect how objects are transformed when
  55. // sent to the server.
  56. ContentConfig
  57. // Server requires Basic authentication
  58. Username string
  59. Password string `datapolicy:"password"`
  60. // Server requires Bearer authentication. This client will not attempt to use
  61. // refresh tokens for an OAuth2 flow.
  62. // TODO: demonstrate an OAuth2 compatible client.
  63. BearerToken string `datapolicy:"token"`
  64. // Path to a file containing a BearerToken.
  65. // If set, the contents are periodically read.
  66. // The last successfully read value takes precedence over BearerToken.
  67. BearerTokenFile string
  68. // Impersonate is the configuration that RESTClient will use for impersonation.
  69. Impersonate ImpersonationConfig
  70. // Server requires plugin-specified authentication.
  71. AuthProvider *clientcmdapi.AuthProviderConfig
  72. // Callback to persist config for AuthProvider.
  73. AuthConfigPersister AuthProviderConfigPersister
  74. // Exec-based authentication provider.
  75. ExecProvider *clientcmdapi.ExecConfig
  76. // TLSClientConfig contains settings to enable transport layer security
  77. TLSClientConfig
  78. // UserAgent is an optional field that specifies the caller of this request.
  79. UserAgent string
  80. // DisableCompression bypasses automatic GZip compression requests to the
  81. // server.
  82. DisableCompression bool
  83. // Transport may be used for custom HTTP behavior. This attribute may not
  84. // be specified with the TLS client certificate options. Use WrapTransport
  85. // to provide additional per-server middleware behavior.
  86. Transport http.RoundTripper
  87. // WrapTransport will be invoked for custom HTTP behavior after the underlying
  88. // transport is initialized (either the transport created from TLSClientConfig,
  89. // Transport, or http.DefaultTransport). The config may layer other RoundTrippers
  90. // on top of the returned RoundTripper.
  91. //
  92. // A future release will change this field to an array. Use config.Wrap()
  93. // instead of setting this value directly.
  94. WrapTransport transport.WrapperFunc
  95. // QPS indicates the maximum QPS to the master from this client.
  96. // If it's zero, the created RESTClient will use DefaultQPS: 5
  97. //
  98. // Setting this to a negative value will disable client-side ratelimiting
  99. // unless `Ratelimiter` is also set.
  100. QPS float32
  101. // Maximum burst for throttle.
  102. // If it's zero, the created RESTClient will use DefaultBurst: 10.
  103. Burst int
  104. // Rate limiter for limiting connections to the master from this client. If present overwrites QPS/Burst
  105. RateLimiter flowcontrol.RateLimiter
  106. // WarningHandler handles warnings in server responses.
  107. // If this and WarningHandlerWithContext are not set, the
  108. // default warning handler is used. If both are set,
  109. // WarningHandlerWithContext is used.
  110. //
  111. // See documentation for [SetDefaultWarningHandler] for details.
  112. //
  113. //logcheck:context // WarningHandlerWithContext should be used instead of WarningHandler in code which supports contextual logging.
  114. WarningHandler WarningHandler
  115. // WarningHandlerWithContext handles warnings in server responses.
  116. // If this and WarningHandler are not set, the
  117. // default warning handler is used. If both are set,
  118. // WarningHandlerWithContext is used.
  119. //
  120. // See documentation for [SetDefaultWarningHandler] for details.
  121. WarningHandlerWithContext WarningHandlerWithContext
  122. // The maximum length of time to wait before giving up on a server request. A value of zero means no timeout.
  123. Timeout time.Duration
  124. // Dial specifies the dial function for creating unencrypted TCP connections.
  125. Dial func(ctx context.Context, network, address string) (net.Conn, error)
  126. // Proxy is the proxy func to be used for all requests made by this
  127. // transport. If Proxy is nil, http.ProxyFromEnvironment is used. If Proxy
  128. // returns a nil *URL, no proxy is used.
  129. //
  130. // socks5 proxying does not currently support spdy streaming endpoints.
  131. Proxy func(*http.Request) (*url.URL, error)
  132. // Version forces a specific version to be used (if registered)
  133. // Do we need this?
  134. // Version string
  135. }
  136. var _ fmt.Stringer = new(Config)
  137. var _ fmt.GoStringer = new(Config)
  138. type sanitizedConfig *Config
  139. type sanitizedAuthConfigPersister struct{ AuthProviderConfigPersister }
  140. func (sanitizedAuthConfigPersister) GoString() string {
  141. return "rest.AuthProviderConfigPersister(--- REDACTED ---)"
  142. }
  143. func (sanitizedAuthConfigPersister) String() string {
  144. return "rest.AuthProviderConfigPersister(--- REDACTED ---)"
  145. }
  146. type sanitizedObject struct{ runtime.Object }
  147. func (sanitizedObject) GoString() string {
  148. return "runtime.Object(--- REDACTED ---)"
  149. }
  150. func (sanitizedObject) String() string {
  151. return "runtime.Object(--- REDACTED ---)"
  152. }
  153. // GoString implements fmt.GoStringer and sanitizes sensitive fields of Config
  154. // to prevent accidental leaking via logs.
  155. func (c *Config) GoString() string {
  156. return c.String()
  157. }
  158. // String implements fmt.Stringer and sanitizes sensitive fields of Config to
  159. // prevent accidental leaking via logs.
  160. func (c *Config) String() string {
  161. if c == nil {
  162. return "<nil>"
  163. }
  164. cc := sanitizedConfig(CopyConfig(c))
  165. // Explicitly mark non-empty credential fields as redacted.
  166. if cc.Password != "" {
  167. cc.Password = "--- REDACTED ---"
  168. }
  169. if cc.BearerToken != "" {
  170. cc.BearerToken = "--- REDACTED ---"
  171. }
  172. if cc.AuthConfigPersister != nil {
  173. cc.AuthConfigPersister = sanitizedAuthConfigPersister{cc.AuthConfigPersister}
  174. }
  175. if cc.ExecProvider != nil && cc.ExecProvider.Config != nil {
  176. cc.ExecProvider.Config = sanitizedObject{Object: cc.ExecProvider.Config}
  177. }
  178. return fmt.Sprintf("%#v", cc)
  179. }
  180. // ImpersonationConfig has all the available impersonation options
  181. type ImpersonationConfig struct {
  182. // UserName is the username to impersonate on each request.
  183. UserName string
  184. // UID is a unique value that identifies the user.
  185. UID string
  186. // Groups are the groups to impersonate on each request.
  187. Groups []string
  188. // Extra is a free-form field which can be used to link some authentication information
  189. // to authorization information. This field allows you to impersonate it.
  190. Extra map[string][]string
  191. }
  192. // +k8s:deepcopy-gen=true
  193. // TLSClientConfig contains settings to enable transport layer security
  194. type TLSClientConfig struct {
  195. // Server should be accessed without verifying the TLS certificate. For testing only.
  196. Insecure bool
  197. // ServerName is passed to the server for SNI and is used in the client to check server
  198. // certificates against. If ServerName is empty, the hostname used to contact the
  199. // server is used.
  200. ServerName string
  201. // Server requires TLS client certificate authentication
  202. CertFile string
  203. // Server requires TLS client certificate authentication
  204. KeyFile string
  205. // Trusted root certificates for server
  206. CAFile string
  207. // CertData holds PEM-encoded bytes (typically read from a client certificate file).
  208. // CertData takes precedence over CertFile
  209. CertData []byte
  210. // KeyData holds PEM-encoded bytes (typically read from a client certificate key file).
  211. // KeyData takes precedence over KeyFile
  212. KeyData []byte `datapolicy:"security-key"`
  213. // CAData holds PEM-encoded bytes (typically read from a root certificates bundle).
  214. // CAData takes precedence over CAFile
  215. CAData []byte
  216. // NextProtos is a list of supported application level protocols, in order of preference.
  217. // Used to populate tls.Config.NextProtos.
  218. // To indicate to the server http/1.1 is preferred over http/2, set to ["http/1.1", "h2"] (though the server is free to ignore that preference).
  219. // To use only http/1.1, set to ["http/1.1"].
  220. NextProtos []string
  221. }
  222. var _ fmt.Stringer = TLSClientConfig{}
  223. var _ fmt.GoStringer = TLSClientConfig{}
  224. type sanitizedTLSClientConfig TLSClientConfig
  225. // GoString implements fmt.GoStringer and sanitizes sensitive fields of
  226. // TLSClientConfig to prevent accidental leaking via logs.
  227. func (c TLSClientConfig) GoString() string {
  228. return c.String()
  229. }
  230. // String implements fmt.Stringer and sanitizes sensitive fields of
  231. // TLSClientConfig to prevent accidental leaking via logs.
  232. func (c TLSClientConfig) String() string {
  233. cc := sanitizedTLSClientConfig{
  234. Insecure: c.Insecure,
  235. ServerName: c.ServerName,
  236. CertFile: c.CertFile,
  237. KeyFile: c.KeyFile,
  238. CAFile: c.CAFile,
  239. CertData: c.CertData,
  240. KeyData: c.KeyData,
  241. CAData: c.CAData,
  242. NextProtos: c.NextProtos,
  243. }
  244. // Explicitly mark non-empty credential fields as redacted.
  245. if len(cc.CertData) != 0 {
  246. cc.CertData = []byte("--- TRUNCATED ---")
  247. }
  248. if len(cc.KeyData) != 0 {
  249. cc.KeyData = []byte("--- REDACTED ---")
  250. }
  251. return fmt.Sprintf("%#v", cc)
  252. }
  253. type ContentConfig struct {
  254. // AcceptContentTypes specifies the types the client will accept and is optional.
  255. // If not set, ContentType will be used to define the Accept header
  256. AcceptContentTypes string
  257. // ContentType specifies the wire format used to communicate with the server.
  258. // This value will be set as the Accept header on requests made to the server, and
  259. // as the default content type on any object sent to the server. If not set,
  260. // "application/json" is used.
  261. ContentType string
  262. // GroupVersion is the API version to talk to. Must be provided when initializing
  263. // a RESTClient directly. When initializing a Client, will be set with the default
  264. // code version.
  265. GroupVersion *schema.GroupVersion
  266. // NegotiatedSerializer is used for obtaining encoders and decoders for multiple
  267. // supported media types.
  268. //
  269. // TODO: NegotiatedSerializer will be phased out as internal clients are removed
  270. // from Kubernetes.
  271. NegotiatedSerializer runtime.NegotiatedSerializer
  272. }
  273. // RESTClientFor returns a RESTClient that satisfies the requested attributes on a client Config
  274. // object. Note that a RESTClient may require fields that are optional when initializing a Client.
  275. // A RESTClient created by this method is generic - it expects to operate on an API that follows
  276. // the Kubernetes conventions, but may not be the Kubernetes API.
  277. // RESTClientFor is equivalent to calling RESTClientForConfigAndClient(config, httpClient),
  278. // where httpClient was generated with HTTPClientFor(config).
  279. func RESTClientFor(config *Config) (*RESTClient, error) {
  280. if config.GroupVersion == nil {
  281. return nil, fmt.Errorf("GroupVersion is required when initializing a RESTClient")
  282. }
  283. if config.NegotiatedSerializer == nil {
  284. return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
  285. }
  286. // Validate config.Host before constructing the transport/client so we can fail fast.
  287. // ServerURL will be obtained later in RESTClientForConfigAndClient()
  288. _, _, err := DefaultServerUrlFor(config)
  289. if err != nil {
  290. return nil, err
  291. }
  292. httpClient, err := HTTPClientFor(config)
  293. if err != nil {
  294. return nil, err
  295. }
  296. return RESTClientForConfigAndClient(config, httpClient)
  297. }
  298. // RESTClientForConfigAndClient returns a RESTClient that satisfies the requested attributes on a
  299. // client Config object.
  300. // Unlike RESTClientFor, RESTClientForConfigAndClient allows to pass an http.Client that is shared
  301. // between all the API Groups and Versions.
  302. // Note that the http client takes precedence over the transport values configured.
  303. // The http client defaults to the `http.DefaultClient` if nil.
  304. func RESTClientForConfigAndClient(config *Config, httpClient *http.Client) (*RESTClient, error) {
  305. if config.GroupVersion == nil {
  306. return nil, fmt.Errorf("GroupVersion is required when initializing a RESTClient")
  307. }
  308. if config.NegotiatedSerializer == nil {
  309. return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
  310. }
  311. baseURL, versionedAPIPath, err := DefaultServerUrlFor(config)
  312. if err != nil {
  313. return nil, err
  314. }
  315. rateLimiter := config.RateLimiter
  316. if rateLimiter == nil {
  317. qps := config.QPS
  318. if config.QPS == 0.0 {
  319. qps = DefaultQPS
  320. }
  321. burst := config.Burst
  322. if config.Burst == 0 {
  323. burst = DefaultBurst
  324. }
  325. if qps > 0 {
  326. rateLimiter = flowcontrol.NewTokenBucketRateLimiter(qps, burst)
  327. }
  328. }
  329. var gv schema.GroupVersion
  330. if config.GroupVersion != nil {
  331. gv = *config.GroupVersion
  332. }
  333. clientContent := ClientContentConfig{
  334. AcceptContentTypes: config.AcceptContentTypes,
  335. ContentType: config.ContentType,
  336. GroupVersion: gv,
  337. Negotiator: runtime.NewClientNegotiator(config.NegotiatedSerializer, gv),
  338. }
  339. restClient, err := NewRESTClient(baseURL, versionedAPIPath, clientContent, rateLimiter, httpClient)
  340. maybeSetWarningHandler(restClient, config.WarningHandler, config.WarningHandlerWithContext)
  341. return restClient, err
  342. }
  343. // maybeSetWarningHandler sets the handlerWithContext if non-nil,
  344. // otherwise the handler with a wrapper if non-nil,
  345. // and does nothing if both are nil.
  346. //
  347. // May be called for a nil client.
  348. func maybeSetWarningHandler(c *RESTClient, handler WarningHandler, handlerWithContext WarningHandlerWithContext) {
  349. if c == nil {
  350. return
  351. }
  352. switch {
  353. case handlerWithContext != nil:
  354. c.warningHandler = handlerWithContext
  355. case handler != nil:
  356. c.warningHandler = warningLoggerNopContext{l: handler}
  357. }
  358. }
  359. // UnversionedRESTClientFor is the same as RESTClientFor, except that it allows
  360. // the config.Version to be empty.
  361. func UnversionedRESTClientFor(config *Config) (*RESTClient, error) {
  362. if config.NegotiatedSerializer == nil {
  363. return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
  364. }
  365. // Validate config.Host before constructing the transport/client so we can fail fast.
  366. // ServerURL will be obtained later in UnversionedRESTClientForConfigAndClient()
  367. _, _, err := DefaultServerUrlFor(config)
  368. if err != nil {
  369. return nil, err
  370. }
  371. httpClient, err := HTTPClientFor(config)
  372. if err != nil {
  373. return nil, err
  374. }
  375. return UnversionedRESTClientForConfigAndClient(config, httpClient)
  376. }
  377. // UnversionedRESTClientForConfigAndClient is the same as RESTClientForConfigAndClient,
  378. // except that it allows the config.Version to be empty.
  379. func UnversionedRESTClientForConfigAndClient(config *Config, httpClient *http.Client) (*RESTClient, error) {
  380. if config.NegotiatedSerializer == nil {
  381. return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
  382. }
  383. baseURL, versionedAPIPath, err := DefaultServerUrlFor(config)
  384. if err != nil {
  385. return nil, err
  386. }
  387. rateLimiter := config.RateLimiter
  388. if rateLimiter == nil {
  389. qps := config.QPS
  390. if config.QPS == 0.0 {
  391. qps = DefaultQPS
  392. }
  393. burst := config.Burst
  394. if config.Burst == 0 {
  395. burst = DefaultBurst
  396. }
  397. if qps > 0 {
  398. rateLimiter = flowcontrol.NewTokenBucketRateLimiter(qps, burst)
  399. }
  400. }
  401. gv := metav1.SchemeGroupVersion
  402. if config.GroupVersion != nil {
  403. gv = *config.GroupVersion
  404. }
  405. clientContent := ClientContentConfig{
  406. AcceptContentTypes: config.AcceptContentTypes,
  407. ContentType: config.ContentType,
  408. GroupVersion: gv,
  409. Negotiator: runtime.NewClientNegotiator(config.NegotiatedSerializer, gv),
  410. }
  411. restClient, err := NewRESTClient(baseURL, versionedAPIPath, clientContent, rateLimiter, httpClient)
  412. maybeSetWarningHandler(restClient, config.WarningHandler, config.WarningHandlerWithContext)
  413. return restClient, err
  414. }
  415. // SetKubernetesDefaults sets default values on the provided client config for accessing the
  416. // Kubernetes API or returns an error if any of the defaults are impossible or invalid.
  417. func SetKubernetesDefaults(config *Config) error {
  418. if len(config.UserAgent) == 0 {
  419. config.UserAgent = DefaultKubernetesUserAgent()
  420. }
  421. return nil
  422. }
  423. // adjustCommit returns sufficient significant figures of the commit's git hash.
  424. func adjustCommit(c string) string {
  425. if len(c) == 0 {
  426. return "unknown"
  427. }
  428. if len(c) > 7 {
  429. return c[:7]
  430. }
  431. return c
  432. }
  433. // adjustVersion strips "alpha", "beta", etc. from version in form
  434. // major.minor.patch-[alpha|beta|etc].
  435. func adjustVersion(v string) string {
  436. if len(v) == 0 {
  437. return "unknown"
  438. }
  439. seg := strings.SplitN(v, "-", 2)
  440. return seg[0]
  441. }
  442. // adjustCommand returns the last component of the
  443. // OS-specific command path for use in User-Agent.
  444. func adjustCommand(p string) string {
  445. // Unlikely, but better than returning "".
  446. if len(p) == 0 {
  447. return "unknown"
  448. }
  449. return filepath.Base(p)
  450. }
  451. // buildUserAgent builds a User-Agent string from given args.
  452. func buildUserAgent(command, version, os, arch, commit string) string {
  453. return fmt.Sprintf(
  454. "%s/%s (%s/%s) kubernetes/%s", command, version, os, arch, commit)
  455. }
  456. // DefaultKubernetesUserAgent returns a User-Agent string built from static global vars.
  457. func DefaultKubernetesUserAgent() string {
  458. return buildUserAgent(
  459. adjustCommand(os.Args[0]),
  460. adjustVersion(version.Get().GitVersion),
  461. gruntime.GOOS,
  462. gruntime.GOARCH,
  463. adjustCommit(version.Get().GitCommit))
  464. }
  465. // InClusterConfig returns a config object which uses the service account
  466. // kubernetes gives to pods. It's intended for clients that expect to be
  467. // running inside a pod running on kubernetes. It will return ErrNotInCluster
  468. // if called from a process not running in a kubernetes environment.
  469. func InClusterConfig() (*Config, error) {
  470. const (
  471. tokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"
  472. rootCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
  473. )
  474. host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
  475. if len(host) == 0 || len(port) == 0 {
  476. return nil, ErrNotInCluster
  477. }
  478. token, err := os.ReadFile(tokenFile)
  479. if err != nil {
  480. return nil, err
  481. }
  482. tlsClientConfig := TLSClientConfig{}
  483. if _, err := certutil.NewPool(rootCAFile); err != nil {
  484. //nolint:logcheck // The decision to log this instead of returning an error goes back to ~2016. It's part of the client-go API now, so not changing it just to support contextual logging.
  485. klog.Errorf("Expected to load root CA config from %s, but got err: %v", rootCAFile, err)
  486. } else {
  487. tlsClientConfig.CAFile = rootCAFile
  488. }
  489. return &Config{
  490. // TODO: switch to using cluster DNS.
  491. Host: "https://" + net.JoinHostPort(host, port),
  492. TLSClientConfig: tlsClientConfig,
  493. BearerToken: string(token),
  494. BearerTokenFile: tokenFile,
  495. }, nil
  496. }
  497. // IsConfigTransportTLS returns true if and only if the provided
  498. // config will result in a protected connection to the server when it
  499. // is passed to restclient.RESTClientFor(). Use to determine when to
  500. // send credentials over the wire.
  501. //
  502. // Note: the Insecure flag is ignored when testing for this value, so MITM attacks are
  503. // still possible.
  504. func IsConfigTransportTLS(config Config) bool {
  505. baseURL, _, err := DefaultServerUrlFor(&config)
  506. if err != nil {
  507. return false
  508. }
  509. return baseURL.Scheme == "https"
  510. }
  511. // LoadTLSFiles copies the data from the CertFile, KeyFile, and CAFile fields into the CertData,
  512. // KeyData, and CAFile fields, or returns an error. If no error is returned, all three fields are
  513. // either populated or were empty to start.
  514. func LoadTLSFiles(c *Config) error {
  515. var err error
  516. c.CAData, err = dataFromSliceOrFile(c.CAData, c.CAFile)
  517. if err != nil {
  518. return err
  519. }
  520. c.CertData, err = dataFromSliceOrFile(c.CertData, c.CertFile)
  521. if err != nil {
  522. return err
  523. }
  524. c.KeyData, err = dataFromSliceOrFile(c.KeyData, c.KeyFile)
  525. return err
  526. }
  527. // dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
  528. // or an error if an error occurred reading the file
  529. func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
  530. if len(data) > 0 {
  531. return data, nil
  532. }
  533. if len(file) > 0 {
  534. fileData, err := os.ReadFile(file)
  535. if err != nil {
  536. return []byte{}, err
  537. }
  538. return fileData, nil
  539. }
  540. return nil, nil
  541. }
  542. func AddUserAgent(config *Config, userAgent string) *Config {
  543. fullUserAgent := DefaultKubernetesUserAgent() + "/" + userAgent
  544. config.UserAgent = fullUserAgent
  545. return config
  546. }
  547. // AnonymousClientConfig returns a copy of the given config with all user credentials (cert/key, bearer token, and username/password) and custom transports (WrapTransport, Transport) removed
  548. func AnonymousClientConfig(config *Config) *Config {
  549. // copy only known safe fields
  550. return &Config{
  551. Host: config.Host,
  552. APIPath: config.APIPath,
  553. ContentConfig: config.ContentConfig,
  554. TLSClientConfig: TLSClientConfig{
  555. Insecure: config.Insecure,
  556. ServerName: config.ServerName,
  557. CAFile: config.TLSClientConfig.CAFile,
  558. CAData: config.TLSClientConfig.CAData,
  559. NextProtos: config.TLSClientConfig.NextProtos,
  560. },
  561. RateLimiter: config.RateLimiter,
  562. WarningHandler: config.WarningHandler,
  563. WarningHandlerWithContext: config.WarningHandlerWithContext,
  564. UserAgent: config.UserAgent,
  565. DisableCompression: config.DisableCompression,
  566. QPS: config.QPS,
  567. Burst: config.Burst,
  568. Timeout: config.Timeout,
  569. Dial: config.Dial,
  570. Proxy: config.Proxy,
  571. }
  572. }
  573. // CopyConfig returns a copy of the given config
  574. func CopyConfig(config *Config) *Config {
  575. c := &Config{
  576. Host: config.Host,
  577. APIPath: config.APIPath,
  578. ContentConfig: config.ContentConfig,
  579. Username: config.Username,
  580. Password: config.Password,
  581. BearerToken: config.BearerToken,
  582. BearerTokenFile: config.BearerTokenFile,
  583. Impersonate: ImpersonationConfig{
  584. UserName: config.Impersonate.UserName,
  585. UID: config.Impersonate.UID,
  586. Groups: config.Impersonate.Groups,
  587. Extra: config.Impersonate.Extra,
  588. },
  589. AuthProvider: config.AuthProvider,
  590. AuthConfigPersister: config.AuthConfigPersister,
  591. ExecProvider: config.ExecProvider,
  592. TLSClientConfig: TLSClientConfig{
  593. Insecure: config.TLSClientConfig.Insecure,
  594. ServerName: config.TLSClientConfig.ServerName,
  595. CertFile: config.TLSClientConfig.CertFile,
  596. KeyFile: config.TLSClientConfig.KeyFile,
  597. CAFile: config.TLSClientConfig.CAFile,
  598. CertData: config.TLSClientConfig.CertData,
  599. KeyData: config.TLSClientConfig.KeyData,
  600. CAData: config.TLSClientConfig.CAData,
  601. NextProtos: config.TLSClientConfig.NextProtos,
  602. },
  603. UserAgent: config.UserAgent,
  604. DisableCompression: config.DisableCompression,
  605. Transport: config.Transport,
  606. WrapTransport: config.WrapTransport,
  607. QPS: config.QPS,
  608. Burst: config.Burst,
  609. RateLimiter: config.RateLimiter,
  610. WarningHandler: config.WarningHandler,
  611. WarningHandlerWithContext: config.WarningHandlerWithContext,
  612. Timeout: config.Timeout,
  613. Dial: config.Dial,
  614. Proxy: config.Proxy,
  615. }
  616. if config.ExecProvider != nil && config.ExecProvider.Config != nil {
  617. c.ExecProvider.Config = config.ExecProvider.Config.DeepCopyObject()
  618. }
  619. return c
  620. }
  621. // CodecFactoryForGeneratedClient returns the provided CodecFactory if there are no enabled client
  622. // feature gates affecting serialization. Otherwise, it constructs and returns a new CodecFactory
  623. // from the provided Scheme.
  624. //
  625. // This is supported ONLY for use by clients generated with client-gen. The caller is responsible
  626. // for ensuring that the CodecFactory argument was constructed using the Scheme argument.
  627. func CodecFactoryForGeneratedClient(scheme *runtime.Scheme, codecs serializer.CodecFactory) serializer.CodecFactory {
  628. if !features.FeatureGates().Enabled(features.ClientsAllowCBOR) {
  629. // NOTE: This assumes client-gen will not generate CBOR-enabled Codecs as long as
  630. // the feature gate exists.
  631. return codecs
  632. }
  633. return serializer.NewCodecFactory(scheme, serializer.WithSerializer(cbor.NewSerializerInfo))
  634. }