config.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  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. "fmt"
  17. "io/ioutil"
  18. "net"
  19. "net/http"
  20. "os"
  21. "path/filepath"
  22. gruntime "runtime"
  23. "strings"
  24. "time"
  25. "github.com/golang/glog"
  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/client-go/pkg/version"
  30. clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
  31. certutil "k8s.io/client-go/util/cert"
  32. "k8s.io/client-go/util/flowcontrol"
  33. )
  34. const (
  35. DefaultQPS float32 = 5.0
  36. DefaultBurst int = 10
  37. )
  38. // Config holds the common attributes that can be passed to a Kubernetes client on
  39. // initialization.
  40. type Config struct {
  41. // Host must be a host string, a host:port pair, or a URL to the base of the apiserver.
  42. // If a URL is given then the (optional) Path of that URL represents a prefix that must
  43. // be appended to all request URIs used to access the apiserver. This allows a frontend
  44. // proxy to easily relocate all of the apiserver endpoints.
  45. Host string
  46. // APIPath is a sub-path that points to an API root.
  47. APIPath string
  48. // ContentConfig contains settings that affect how objects are transformed when
  49. // sent to the server.
  50. ContentConfig
  51. // Server requires Basic authentication
  52. Username string
  53. Password string
  54. // Server requires Bearer authentication. This client will not attempt to use
  55. // refresh tokens for an OAuth2 flow.
  56. // TODO: demonstrate an OAuth2 compatible client.
  57. BearerToken string
  58. // Impersonate is the configuration that RESTClient will use for impersonation.
  59. Impersonate ImpersonationConfig
  60. // Server requires plugin-specified authentication.
  61. AuthProvider *clientcmdapi.AuthProviderConfig
  62. // Callback to persist config for AuthProvider.
  63. AuthConfigPersister AuthProviderConfigPersister
  64. // Exec-based authentication provider.
  65. ExecProvider *clientcmdapi.ExecConfig
  66. // TLSClientConfig contains settings to enable transport layer security
  67. TLSClientConfig
  68. // UserAgent is an optional field that specifies the caller of this request.
  69. UserAgent string
  70. // Transport may be used for custom HTTP behavior. This attribute may not
  71. // be specified with the TLS client certificate options. Use WrapTransport
  72. // for most client level operations.
  73. Transport http.RoundTripper
  74. // WrapTransport will be invoked for custom HTTP behavior after the underlying
  75. // transport is initialized (either the transport created from TLSClientConfig,
  76. // Transport, or http.DefaultTransport). The config may layer other RoundTrippers
  77. // on top of the returned RoundTripper.
  78. WrapTransport func(rt http.RoundTripper) http.RoundTripper
  79. // QPS indicates the maximum QPS to the master from this client.
  80. // If it's zero, the created RESTClient will use DefaultQPS: 5
  81. QPS float32
  82. // Maximum burst for throttle.
  83. // If it's zero, the created RESTClient will use DefaultBurst: 10.
  84. Burst int
  85. // Rate limiter for limiting connections to the master from this client. If present overwrites QPS/Burst
  86. RateLimiter flowcontrol.RateLimiter
  87. // The maximum length of time to wait before giving up on a server request. A value of zero means no timeout.
  88. Timeout time.Duration
  89. // Dial specifies the dial function for creating unencrypted TCP connections.
  90. Dial func(ctx context.Context, network, address string) (net.Conn, error)
  91. // Version forces a specific version to be used (if registered)
  92. // Do we need this?
  93. // Version string
  94. }
  95. // ImpersonationConfig has all the available impersonation options
  96. type ImpersonationConfig struct {
  97. // UserName is the username to impersonate on each request.
  98. UserName string
  99. // Groups are the groups to impersonate on each request.
  100. Groups []string
  101. // Extra is a free-form field which can be used to link some authentication information
  102. // to authorization information. This field allows you to impersonate it.
  103. Extra map[string][]string
  104. }
  105. // +k8s:deepcopy-gen=true
  106. // TLSClientConfig contains settings to enable transport layer security
  107. type TLSClientConfig struct {
  108. // Server should be accessed without verifying the TLS certificate. For testing only.
  109. Insecure bool
  110. // ServerName is passed to the server for SNI and is used in the client to check server
  111. // ceritificates against. If ServerName is empty, the hostname used to contact the
  112. // server is used.
  113. ServerName string
  114. // Server requires TLS client certificate authentication
  115. CertFile string
  116. // Server requires TLS client certificate authentication
  117. KeyFile string
  118. // Trusted root certificates for server
  119. CAFile string
  120. // CertData holds PEM-encoded bytes (typically read from a client certificate file).
  121. // CertData takes precedence over CertFile
  122. CertData []byte
  123. // KeyData holds PEM-encoded bytes (typically read from a client certificate key file).
  124. // KeyData takes precedence over KeyFile
  125. KeyData []byte
  126. // CAData holds PEM-encoded bytes (typically read from a root certificates bundle).
  127. // CAData takes precedence over CAFile
  128. CAData []byte
  129. }
  130. type ContentConfig struct {
  131. // AcceptContentTypes specifies the types the client will accept and is optional.
  132. // If not set, ContentType will be used to define the Accept header
  133. AcceptContentTypes string
  134. // ContentType specifies the wire format used to communicate with the server.
  135. // This value will be set as the Accept header on requests made to the server, and
  136. // as the default content type on any object sent to the server. If not set,
  137. // "application/json" is used.
  138. ContentType string
  139. // GroupVersion is the API version to talk to. Must be provided when initializing
  140. // a RESTClient directly. When initializing a Client, will be set with the default
  141. // code version.
  142. GroupVersion *schema.GroupVersion
  143. // NegotiatedSerializer is used for obtaining encoders and decoders for multiple
  144. // supported media types.
  145. NegotiatedSerializer runtime.NegotiatedSerializer
  146. }
  147. // RESTClientFor returns a RESTClient that satisfies the requested attributes on a client Config
  148. // object. Note that a RESTClient may require fields that are optional when initializing a Client.
  149. // A RESTClient created by this method is generic - it expects to operate on an API that follows
  150. // the Kubernetes conventions, but may not be the Kubernetes API.
  151. func RESTClientFor(config *Config) (*RESTClient, error) {
  152. if config.GroupVersion == nil {
  153. return nil, fmt.Errorf("GroupVersion is required when initializing a RESTClient")
  154. }
  155. if config.NegotiatedSerializer == nil {
  156. return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
  157. }
  158. qps := config.QPS
  159. if config.QPS == 0.0 {
  160. qps = DefaultQPS
  161. }
  162. burst := config.Burst
  163. if config.Burst == 0 {
  164. burst = DefaultBurst
  165. }
  166. baseURL, versionedAPIPath, err := defaultServerUrlFor(config)
  167. if err != nil {
  168. return nil, err
  169. }
  170. transport, err := TransportFor(config)
  171. if err != nil {
  172. return nil, err
  173. }
  174. var httpClient *http.Client
  175. if transport != http.DefaultTransport {
  176. httpClient = &http.Client{Transport: transport}
  177. if config.Timeout > 0 {
  178. httpClient.Timeout = config.Timeout
  179. }
  180. }
  181. return NewRESTClient(baseURL, versionedAPIPath, config.ContentConfig, qps, burst, config.RateLimiter, httpClient)
  182. }
  183. // UnversionedRESTClientFor is the same as RESTClientFor, except that it allows
  184. // the config.Version to be empty.
  185. func UnversionedRESTClientFor(config *Config) (*RESTClient, error) {
  186. if config.NegotiatedSerializer == nil {
  187. return nil, fmt.Errorf("NeogitatedSerializer is required when initializing a RESTClient")
  188. }
  189. baseURL, versionedAPIPath, err := defaultServerUrlFor(config)
  190. if err != nil {
  191. return nil, err
  192. }
  193. transport, err := TransportFor(config)
  194. if err != nil {
  195. return nil, err
  196. }
  197. var httpClient *http.Client
  198. if transport != http.DefaultTransport {
  199. httpClient = &http.Client{Transport: transport}
  200. if config.Timeout > 0 {
  201. httpClient.Timeout = config.Timeout
  202. }
  203. }
  204. versionConfig := config.ContentConfig
  205. if versionConfig.GroupVersion == nil {
  206. v := metav1.SchemeGroupVersion
  207. versionConfig.GroupVersion = &v
  208. }
  209. return NewRESTClient(baseURL, versionedAPIPath, versionConfig, config.QPS, config.Burst, config.RateLimiter, httpClient)
  210. }
  211. // SetKubernetesDefaults sets default values on the provided client config for accessing the
  212. // Kubernetes API or returns an error if any of the defaults are impossible or invalid.
  213. func SetKubernetesDefaults(config *Config) error {
  214. if len(config.UserAgent) == 0 {
  215. config.UserAgent = DefaultKubernetesUserAgent()
  216. }
  217. return nil
  218. }
  219. // adjustCommit returns sufficient significant figures of the commit's git hash.
  220. func adjustCommit(c string) string {
  221. if len(c) == 0 {
  222. return "unknown"
  223. }
  224. if len(c) > 7 {
  225. return c[:7]
  226. }
  227. return c
  228. }
  229. // adjustVersion strips "alpha", "beta", etc. from version in form
  230. // major.minor.patch-[alpha|beta|etc].
  231. func adjustVersion(v string) string {
  232. if len(v) == 0 {
  233. return "unknown"
  234. }
  235. seg := strings.SplitN(v, "-", 2)
  236. return seg[0]
  237. }
  238. // adjustCommand returns the last component of the
  239. // OS-specific command path for use in User-Agent.
  240. func adjustCommand(p string) string {
  241. // Unlikely, but better than returning "".
  242. if len(p) == 0 {
  243. return "unknown"
  244. }
  245. return filepath.Base(p)
  246. }
  247. // buildUserAgent builds a User-Agent string from given args.
  248. func buildUserAgent(command, version, os, arch, commit string) string {
  249. return fmt.Sprintf(
  250. "%s/%s (%s/%s) kubernetes/%s", command, version, os, arch, commit)
  251. }
  252. // DefaultKubernetesUserAgent returns a User-Agent string built from static global vars.
  253. func DefaultKubernetesUserAgent() string {
  254. return buildUserAgent(
  255. adjustCommand(os.Args[0]),
  256. adjustVersion(version.Get().GitVersion),
  257. gruntime.GOOS,
  258. gruntime.GOARCH,
  259. adjustCommit(version.Get().GitCommit))
  260. }
  261. // InClusterConfig returns a config object which uses the service account
  262. // kubernetes gives to pods. It's intended for clients that expect to be
  263. // running inside a pod running on kubernetes. It will return an error if
  264. // called from a process not running in a kubernetes environment.
  265. func InClusterConfig() (*Config, error) {
  266. host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
  267. if len(host) == 0 || len(port) == 0 {
  268. return nil, fmt.Errorf("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined")
  269. }
  270. token, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
  271. if err != nil {
  272. return nil, err
  273. }
  274. tlsClientConfig := TLSClientConfig{}
  275. rootCAFile := "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
  276. if _, err := certutil.NewPool(rootCAFile); err != nil {
  277. glog.Errorf("Expected to load root CA config from %s, but got err: %v", rootCAFile, err)
  278. } else {
  279. tlsClientConfig.CAFile = rootCAFile
  280. }
  281. return &Config{
  282. // TODO: switch to using cluster DNS.
  283. Host: "https://" + net.JoinHostPort(host, port),
  284. BearerToken: string(token),
  285. TLSClientConfig: tlsClientConfig,
  286. }, nil
  287. }
  288. // IsConfigTransportTLS returns true if and only if the provided
  289. // config will result in a protected connection to the server when it
  290. // is passed to restclient.RESTClientFor(). Use to determine when to
  291. // send credentials over the wire.
  292. //
  293. // Note: the Insecure flag is ignored when testing for this value, so MITM attacks are
  294. // still possible.
  295. func IsConfigTransportTLS(config Config) bool {
  296. baseURL, _, err := defaultServerUrlFor(&config)
  297. if err != nil {
  298. return false
  299. }
  300. return baseURL.Scheme == "https"
  301. }
  302. // LoadTLSFiles copies the data from the CertFile, KeyFile, and CAFile fields into the CertData,
  303. // KeyData, and CAFile fields, or returns an error. If no error is returned, all three fields are
  304. // either populated or were empty to start.
  305. func LoadTLSFiles(c *Config) error {
  306. var err error
  307. c.CAData, err = dataFromSliceOrFile(c.CAData, c.CAFile)
  308. if err != nil {
  309. return err
  310. }
  311. c.CertData, err = dataFromSliceOrFile(c.CertData, c.CertFile)
  312. if err != nil {
  313. return err
  314. }
  315. c.KeyData, err = dataFromSliceOrFile(c.KeyData, c.KeyFile)
  316. if err != nil {
  317. return err
  318. }
  319. return nil
  320. }
  321. // dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
  322. // or an error if an error occurred reading the file
  323. func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
  324. if len(data) > 0 {
  325. return data, nil
  326. }
  327. if len(file) > 0 {
  328. fileData, err := ioutil.ReadFile(file)
  329. if err != nil {
  330. return []byte{}, err
  331. }
  332. return fileData, nil
  333. }
  334. return nil, nil
  335. }
  336. func AddUserAgent(config *Config, userAgent string) *Config {
  337. fullUserAgent := DefaultKubernetesUserAgent() + "/" + userAgent
  338. config.UserAgent = fullUserAgent
  339. return config
  340. }
  341. // AnonymousClientConfig returns a copy of the given config with all user credentials (cert/key, bearer token, and username/password) removed
  342. func AnonymousClientConfig(config *Config) *Config {
  343. // copy only known safe fields
  344. return &Config{
  345. Host: config.Host,
  346. APIPath: config.APIPath,
  347. ContentConfig: config.ContentConfig,
  348. TLSClientConfig: TLSClientConfig{
  349. Insecure: config.Insecure,
  350. ServerName: config.ServerName,
  351. CAFile: config.TLSClientConfig.CAFile,
  352. CAData: config.TLSClientConfig.CAData,
  353. },
  354. RateLimiter: config.RateLimiter,
  355. UserAgent: config.UserAgent,
  356. Transport: config.Transport,
  357. WrapTransport: config.WrapTransport,
  358. QPS: config.QPS,
  359. Burst: config.Burst,
  360. Timeout: config.Timeout,
  361. Dial: config.Dial,
  362. }
  363. }
  364. // CopyConfig returns a copy of the given config
  365. func CopyConfig(config *Config) *Config {
  366. return &Config{
  367. Host: config.Host,
  368. APIPath: config.APIPath,
  369. ContentConfig: config.ContentConfig,
  370. Username: config.Username,
  371. Password: config.Password,
  372. BearerToken: config.BearerToken,
  373. Impersonate: ImpersonationConfig{
  374. Groups: config.Impersonate.Groups,
  375. Extra: config.Impersonate.Extra,
  376. UserName: config.Impersonate.UserName,
  377. },
  378. AuthProvider: config.AuthProvider,
  379. AuthConfigPersister: config.AuthConfigPersister,
  380. ExecProvider: config.ExecProvider,
  381. TLSClientConfig: TLSClientConfig{
  382. Insecure: config.TLSClientConfig.Insecure,
  383. ServerName: config.TLSClientConfig.ServerName,
  384. CertFile: config.TLSClientConfig.CertFile,
  385. KeyFile: config.TLSClientConfig.KeyFile,
  386. CAFile: config.TLSClientConfig.CAFile,
  387. CertData: config.TLSClientConfig.CertData,
  388. KeyData: config.TLSClientConfig.KeyData,
  389. CAData: config.TLSClientConfig.CAData,
  390. },
  391. UserAgent: config.UserAgent,
  392. Transport: config.Transport,
  393. WrapTransport: config.WrapTransport,
  394. QPS: config.QPS,
  395. Burst: config.Burst,
  396. RateLimiter: config.RateLimiter,
  397. Timeout: config.Timeout,
  398. Dial: config.Dial,
  399. }
  400. }