config.go 14 KB

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