2 Copyright 2016 The Kubernetes Authors.
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33 "k8s.io/apimachinery/pkg/runtime"
34 "k8s.io/apimachinery/pkg/runtime/schema"
35 "k8s.io/client-go/pkg/version"
36 clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
37 certutil "k8s.io/client-go/util/cert"
38 "k8s.io/client-go/util/flowcontrol"
43 DefaultQPS float32 = 5.0
47 var ErrNotInCluster = errors.New("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined")
49 // Config holds the common attributes that can be passed to a Kubernetes client on
52 // Host must be a host string, a host:port pair, or a URL to the base of the apiserver.
53 // If a URL is given then the (optional) Path of that URL represents a prefix that must
54 // be appended to all request URIs used to access the apiserver. This allows a frontend
55 // proxy to easily relocate all of the apiserver endpoints.
57 // APIPath is a sub-path that points to an API root.
60 // ContentConfig contains settings that affect how objects are transformed when
61 // sent to the server.
64 // Server requires Basic authentication
68 // Server requires Bearer authentication. This client will not attempt to use
69 // refresh tokens for an OAuth2 flow.
70 // TODO: demonstrate an OAuth2 compatible client.
73 // Path to a file containing a BearerToken.
74 // If set, the contents are periodically read.
75 // The last successfully read value takes precedence over BearerToken.
76 BearerTokenFile string
78 // Impersonate is the configuration that RESTClient will use for impersonation.
79 Impersonate ImpersonationConfig
81 // Server requires plugin-specified authentication.
82 AuthProvider *clientcmdapi.AuthProviderConfig
84 // Callback to persist config for AuthProvider.
85 AuthConfigPersister AuthProviderConfigPersister
87 // Exec-based authentication provider.
88 ExecProvider *clientcmdapi.ExecConfig
90 // TLSClientConfig contains settings to enable transport layer security
93 // UserAgent is an optional field that specifies the caller of this request.
96 // Transport may be used for custom HTTP behavior. This attribute may not
97 // be specified with the TLS client certificate options. Use WrapTransport
98 // for most client level operations.
99 Transport http.RoundTripper
100 // WrapTransport will be invoked for custom HTTP behavior after the underlying
101 // transport is initialized (either the transport created from TLSClientConfig,
102 // Transport, or http.DefaultTransport). The config may layer other RoundTrippers
103 // on top of the returned RoundTripper.
104 WrapTransport func(rt http.RoundTripper) http.RoundTripper
106 // QPS indicates the maximum QPS to the master from this client.
107 // If it's zero, the created RESTClient will use DefaultQPS: 5
110 // Maximum burst for throttle.
111 // If it's zero, the created RESTClient will use DefaultBurst: 10.
114 // Rate limiter for limiting connections to the master from this client. If present overwrites QPS/Burst
115 RateLimiter flowcontrol.RateLimiter
117 // The maximum length of time to wait before giving up on a server request. A value of zero means no timeout.
118 Timeout time.Duration
120 // Dial specifies the dial function for creating unencrypted TCP connections.
121 Dial func(ctx context.Context, network, address string) (net.Conn, error)
123 // Version forces a specific version to be used (if registered)
128 // ImpersonationConfig has all the available impersonation options
129 type ImpersonationConfig struct {
130 // UserName is the username to impersonate on each request.
132 // Groups are the groups to impersonate on each request.
134 // Extra is a free-form field which can be used to link some authentication information
135 // to authorization information. This field allows you to impersonate it.
136 Extra map[string][]string
139 // +k8s:deepcopy-gen=true
140 // TLSClientConfig contains settings to enable transport layer security
141 type TLSClientConfig struct {
142 // Server should be accessed without verifying the TLS certificate. For testing only.
144 // ServerName is passed to the server for SNI and is used in the client to check server
145 // ceritificates against. If ServerName is empty, the hostname used to contact the
149 // Server requires TLS client certificate authentication
151 // Server requires TLS client certificate authentication
153 // Trusted root certificates for server
156 // CertData holds PEM-encoded bytes (typically read from a client certificate file).
157 // CertData takes precedence over CertFile
159 // KeyData holds PEM-encoded bytes (typically read from a client certificate key file).
160 // KeyData takes precedence over KeyFile
162 // CAData holds PEM-encoded bytes (typically read from a root certificates bundle).
163 // CAData takes precedence over CAFile
167 type ContentConfig struct {
168 // AcceptContentTypes specifies the types the client will accept and is optional.
169 // If not set, ContentType will be used to define the Accept header
170 AcceptContentTypes string
171 // ContentType specifies the wire format used to communicate with the server.
172 // This value will be set as the Accept header on requests made to the server, and
173 // as the default content type on any object sent to the server. If not set,
174 // "application/json" is used.
176 // GroupVersion is the API version to talk to. Must be provided when initializing
177 // a RESTClient directly. When initializing a Client, will be set with the default
179 GroupVersion *schema.GroupVersion
180 // NegotiatedSerializer is used for obtaining encoders and decoders for multiple
181 // supported media types.
182 NegotiatedSerializer runtime.NegotiatedSerializer
185 // RESTClientFor returns a RESTClient that satisfies the requested attributes on a client Config
186 // object. Note that a RESTClient may require fields that are optional when initializing a Client.
187 // A RESTClient created by this method is generic - it expects to operate on an API that follows
188 // the Kubernetes conventions, but may not be the Kubernetes API.
189 func RESTClientFor(config *Config) (*RESTClient, error) {
190 if config.GroupVersion == nil {
191 return nil, fmt.Errorf("GroupVersion is required when initializing a RESTClient")
193 if config.NegotiatedSerializer == nil {
194 return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
197 if config.QPS == 0.0 {
200 burst := config.Burst
201 if config.Burst == 0 {
205 baseURL, versionedAPIPath, err := defaultServerUrlFor(config)
210 transport, err := TransportFor(config)
215 var httpClient *http.Client
216 if transport != http.DefaultTransport {
217 httpClient = &http.Client{Transport: transport}
218 if config.Timeout > 0 {
219 httpClient.Timeout = config.Timeout
223 return NewRESTClient(baseURL, versionedAPIPath, config.ContentConfig, qps, burst, config.RateLimiter, httpClient)
226 // UnversionedRESTClientFor is the same as RESTClientFor, except that it allows
227 // the config.Version to be empty.
228 func UnversionedRESTClientFor(config *Config) (*RESTClient, error) {
229 if config.NegotiatedSerializer == nil {
230 return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
233 baseURL, versionedAPIPath, err := defaultServerUrlFor(config)
238 transport, err := TransportFor(config)
243 var httpClient *http.Client
244 if transport != http.DefaultTransport {
245 httpClient = &http.Client{Transport: transport}
246 if config.Timeout > 0 {
247 httpClient.Timeout = config.Timeout
251 versionConfig := config.ContentConfig
252 if versionConfig.GroupVersion == nil {
253 v := metav1.SchemeGroupVersion
254 versionConfig.GroupVersion = &v
257 return NewRESTClient(baseURL, versionedAPIPath, versionConfig, config.QPS, config.Burst, config.RateLimiter, httpClient)
260 // SetKubernetesDefaults sets default values on the provided client config for accessing the
261 // Kubernetes API or returns an error if any of the defaults are impossible or invalid.
262 func SetKubernetesDefaults(config *Config) error {
263 if len(config.UserAgent) == 0 {
264 config.UserAgent = DefaultKubernetesUserAgent()
269 // adjustCommit returns sufficient significant figures of the commit's git hash.
270 func adjustCommit(c string) string {
280 // adjustVersion strips "alpha", "beta", etc. from version in form
281 // major.minor.patch-[alpha|beta|etc].
282 func adjustVersion(v string) string {
286 seg := strings.SplitN(v, "-", 2)
290 // adjustCommand returns the last component of the
291 // OS-specific command path for use in User-Agent.
292 func adjustCommand(p string) string {
293 // Unlikely, but better than returning "".
297 return filepath.Base(p)
300 // buildUserAgent builds a User-Agent string from given args.
301 func buildUserAgent(command, version, os, arch, commit string) string {
303 "%s/%s (%s/%s) kubernetes/%s", command, version, os, arch, commit)
306 // DefaultKubernetesUserAgent returns a User-Agent string built from static global vars.
307 func DefaultKubernetesUserAgent() string {
308 return buildUserAgent(
309 adjustCommand(os.Args[0]),
310 adjustVersion(version.Get().GitVersion),
313 adjustCommit(version.Get().GitCommit))
316 // InClusterConfig returns a config object which uses the service account
317 // kubernetes gives to pods. It's intended for clients that expect to be
318 // running inside a pod running on kubernetes. It will return ErrNotInCluster
319 // if called from a process not running in a kubernetes environment.
320 func InClusterConfig() (*Config, error) {
322 tokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"
323 rootCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
325 host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
326 if len(host) == 0 || len(port) == 0 {
327 return nil, ErrNotInCluster
330 token, err := ioutil.ReadFile(tokenFile)
335 tlsClientConfig := TLSClientConfig{}
337 if _, err := certutil.NewPool(rootCAFile); err != nil {
338 klog.Errorf("Expected to load root CA config from %s, but got err: %v", rootCAFile, err)
340 tlsClientConfig.CAFile = rootCAFile
344 // TODO: switch to using cluster DNS.
345 Host: "https://" + net.JoinHostPort(host, port),
346 TLSClientConfig: tlsClientConfig,
347 BearerToken: string(token),
348 BearerTokenFile: tokenFile,
352 // IsConfigTransportTLS returns true if and only if the provided
353 // config will result in a protected connection to the server when it
354 // is passed to restclient.RESTClientFor(). Use to determine when to
355 // send credentials over the wire.
357 // Note: the Insecure flag is ignored when testing for this value, so MITM attacks are
359 func IsConfigTransportTLS(config Config) bool {
360 baseURL, _, err := defaultServerUrlFor(&config)
364 return baseURL.Scheme == "https"
367 // LoadTLSFiles copies the data from the CertFile, KeyFile, and CAFile fields into the CertData,
368 // KeyData, and CAFile fields, or returns an error. If no error is returned, all three fields are
369 // either populated or were empty to start.
370 func LoadTLSFiles(c *Config) error {
372 c.CAData, err = dataFromSliceOrFile(c.CAData, c.CAFile)
377 c.CertData, err = dataFromSliceOrFile(c.CertData, c.CertFile)
382 c.KeyData, err = dataFromSliceOrFile(c.KeyData, c.KeyFile)
389 // dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
390 // or an error if an error occurred reading the file
391 func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
396 fileData, err := ioutil.ReadFile(file)
405 func AddUserAgent(config *Config, userAgent string) *Config {
406 fullUserAgent := DefaultKubernetesUserAgent() + "/" + userAgent
407 config.UserAgent = fullUserAgent
411 // AnonymousClientConfig returns a copy of the given config with all user credentials (cert/key, bearer token, and username/password) removed
412 func AnonymousClientConfig(config *Config) *Config {
413 // copy only known safe fields
416 APIPath: config.APIPath,
417 ContentConfig: config.ContentConfig,
418 TLSClientConfig: TLSClientConfig{
419 Insecure: config.Insecure,
420 ServerName: config.ServerName,
421 CAFile: config.TLSClientConfig.CAFile,
422 CAData: config.TLSClientConfig.CAData,
424 RateLimiter: config.RateLimiter,
425 UserAgent: config.UserAgent,
426 Transport: config.Transport,
427 WrapTransport: config.WrapTransport,
430 Timeout: config.Timeout,
435 // CopyConfig returns a copy of the given config
436 func CopyConfig(config *Config) *Config {
439 APIPath: config.APIPath,
440 ContentConfig: config.ContentConfig,
441 Username: config.Username,
442 Password: config.Password,
443 BearerToken: config.BearerToken,
444 BearerTokenFile: config.BearerTokenFile,
445 Impersonate: ImpersonationConfig{
446 Groups: config.Impersonate.Groups,
447 Extra: config.Impersonate.Extra,
448 UserName: config.Impersonate.UserName,
450 AuthProvider: config.AuthProvider,
451 AuthConfigPersister: config.AuthConfigPersister,
452 ExecProvider: config.ExecProvider,
453 TLSClientConfig: TLSClientConfig{
454 Insecure: config.TLSClientConfig.Insecure,
455 ServerName: config.TLSClientConfig.ServerName,
456 CertFile: config.TLSClientConfig.CertFile,
457 KeyFile: config.TLSClientConfig.KeyFile,
458 CAFile: config.TLSClientConfig.CAFile,
459 CertData: config.TLSClientConfig.CertData,
460 KeyData: config.TLSClientConfig.KeyData,
461 CAData: config.TLSClientConfig.CAData,
463 UserAgent: config.UserAgent,
464 Transport: config.Transport,
465 WrapTransport: config.WrapTransport,
468 RateLimiter: config.RateLimiter,
469 Timeout: config.Timeout,