config.go 21 KB

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