diff --git a/pkg/server/options/OWNERS b/pkg/server/options/OWNERS new file mode 100755 index 000000000..c9e6b138e --- /dev/null +++ b/pkg/server/options/OWNERS @@ -0,0 +1,14 @@ +reviewers: +- smarterclayton +- wojtek-t +- deads2k +- liggitt +- nikhiljindal +- sttts +- jlowdermilk +- soltysh +- dims +- cjcullen +- ericchiang +- ping035627 +- xiangpengzhao diff --git a/pkg/server/options/authentication.go b/pkg/server/options/authentication.go new file mode 100644 index 000000000..0557db428 --- /dev/null +++ b/pkg/server/options/authentication.go @@ -0,0 +1,175 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package options + +import ( + "time" + + "github.com/spf13/pflag" + + "k8s.io/apiserver/pkg/authentication/authenticatorfactory" + authenticationclient "k8s.io/client-go/kubernetes/typed/authentication/v1beta1" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +type RequestHeaderAuthenticationOptions struct { + UsernameHeaders []string + GroupHeaders []string + ExtraHeaderPrefixes []string + ClientCAFile string + AllowedNames []string +} + +func (s *RequestHeaderAuthenticationOptions) AddFlags(fs *pflag.FlagSet) { + fs.StringSliceVar(&s.UsernameHeaders, "requestheader-username-headers", s.UsernameHeaders, ""+ + "List of request headers to inspect for usernames. X-Remote-User is common.") + + fs.StringSliceVar(&s.GroupHeaders, "requestheader-group-headers", s.GroupHeaders, ""+ + "List of request headers to inspect for groups. X-Remote-Group is suggested.") + + fs.StringSliceVar(&s.ExtraHeaderPrefixes, "requestheader-extra-headers-prefix", s.ExtraHeaderPrefixes, ""+ + "List of request header prefixes to inspect. X-Remote-Extra- is suggested.") + + fs.StringVar(&s.ClientCAFile, "requestheader-client-ca-file", s.ClientCAFile, ""+ + "Root certificate bundle to use to verify client certificates on incoming requests "+ + "before trusting usernames in headers specified by --requestheader-username-headers") + + fs.StringSliceVar(&s.AllowedNames, "requestheader-allowed-names", s.AllowedNames, ""+ + "List of client certificate common names to allow to provide usernames in headers "+ + "specified by --requestheader-username-headers. If empty, any client certificate validated "+ + "by the authorities in --requestheader-client-ca-file is allowed.") +} + +// ToAuthenticationRequestHeaderConfig returns a RequestHeaderConfig config object for these options +// if necessary, nil otherwise. +func (s *RequestHeaderAuthenticationOptions) ToAuthenticationRequestHeaderConfig() *authenticatorfactory.RequestHeaderConfig { + if len(s.UsernameHeaders) == 0 { + return nil + } + + return &authenticatorfactory.RequestHeaderConfig{ + UsernameHeaders: s.UsernameHeaders, + GroupHeaders: s.GroupHeaders, + ExtraHeaderPrefixes: s.ExtraHeaderPrefixes, + ClientCA: s.ClientCAFile, + AllowedClientNames: s.AllowedNames, + } +} + +type ClientCertAuthenticationOptions struct { + // ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates + ClientCA string +} + +func (s *ClientCertAuthenticationOptions) AddFlags(fs *pflag.FlagSet) { + fs.StringVar(&s.ClientCA, "client-ca-file", s.ClientCA, ""+ + "If set, any request presenting a client certificate signed by one of "+ + "the authorities in the client-ca-file is authenticated with an identity "+ + "corresponding to the CommonName of the client certificate.") +} + +// DelegatingAuthenticationOptions provides an easy way for composing API servers to delegate their authentication to +// the root kube API server. The API federator will act as +// a front proxy and direction connections will be able to delegate to the core kube API server +type DelegatingAuthenticationOptions struct { + // RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server which hosts the + // TokenAccessReview.authentication.k8s.io endpoint for checking tokens. + RemoteKubeConfigFile string + + // CacheTTL is the length of time that a token authentication answer will be cached. + CacheTTL time.Duration + + ClientCert ClientCertAuthenticationOptions + RequestHeader RequestHeaderAuthenticationOptions +} + +func NewDelegatingAuthenticationOptions() *DelegatingAuthenticationOptions { + return &DelegatingAuthenticationOptions{ + // very low for responsiveness, but high enough to handle storms + CacheTTL: 10 * time.Second, + ClientCert: ClientCertAuthenticationOptions{}, + RequestHeader: RequestHeaderAuthenticationOptions{ + UsernameHeaders: []string{"x-remote-user"}, + GroupHeaders: []string{"x-remote-group"}, + ExtraHeaderPrefixes: []string{"x-remote-extra-"}, + }, + } +} + +func (s *DelegatingAuthenticationOptions) Validate() []error { + allErrors := []error{} + return allErrors +} + +func (s *DelegatingAuthenticationOptions) AddFlags(fs *pflag.FlagSet) { + fs.StringVar(&s.RemoteKubeConfigFile, "authentication-kubeconfig", s.RemoteKubeConfigFile, ""+ + "kubeconfig file pointing at the 'core' kubernetes server with enough rights to create "+ + "tokenaccessreviews.authentication.k8s.io.") + + fs.DurationVar(&s.CacheTTL, "authentication-token-webhook-cache-ttl", s.CacheTTL, + "The duration to cache responses from the webhook token authenticator.") + + s.ClientCert.AddFlags(fs) + s.RequestHeader.AddFlags(fs) +} + +func (s *DelegatingAuthenticationOptions) ToAuthenticationConfig() (authenticatorfactory.DelegatingAuthenticatorConfig, error) { + tokenClient, err := s.newTokenAccessReview() + if err != nil { + return authenticatorfactory.DelegatingAuthenticatorConfig{}, err + } + + ret := authenticatorfactory.DelegatingAuthenticatorConfig{ + Anonymous: true, + TokenAccessReviewClient: tokenClient, + CacheTTL: s.CacheTTL, + ClientCAFile: s.ClientCert.ClientCA, + RequestHeaderConfig: s.RequestHeader.ToAuthenticationRequestHeaderConfig(), + } + return ret, nil +} + +func (s *DelegatingAuthenticationOptions) newTokenAccessReview() (authenticationclient.TokenReviewInterface, error) { + var clientConfig *rest.Config + var err error + if len(s.RemoteKubeConfigFile) > 0 { + loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: s.RemoteKubeConfigFile} + loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{}) + + clientConfig, err = loader.ClientConfig() + + } else { + // without the remote kubeconfig file, try to use the in-cluster config. Most addon API servers will + // use this path + clientConfig, err = rest.InClusterConfig() + } + if err != nil { + return nil, err + } + + // set high qps/burst limits since this will effectively limit API server responsiveness + clientConfig.QPS = 200 + clientConfig.Burst = 400 + + client, err := authenticationclient.NewForConfig(clientConfig) + if err != nil { + return nil, err + } + + return client.TokenReviews(), nil +} diff --git a/pkg/server/options/authorization.go b/pkg/server/options/authorization.go new file mode 100644 index 000000000..5ecdacf92 --- /dev/null +++ b/pkg/server/options/authorization.go @@ -0,0 +1,114 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package options + +import ( + "time" + + "github.com/spf13/pflag" + + "k8s.io/apiserver/pkg/authorization/authorizerfactory" + authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1beta1" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +// DelegatingAuthorizationOptions provides an easy way for composing API servers to delegate their authorization to +// the root kube API server +type DelegatingAuthorizationOptions struct { + // RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server which hosts the + // SubjectAccessReview.authorization.k8s.io endpoint for checking tokens. + RemoteKubeConfigFile string + + // AllowCacheTTL is the length of time that a successful authorization response will be cached + AllowCacheTTL time.Duration + + // DenyCacheTTL is the length of time that an unsuccessful authorization response will be cached. + // You generally want more responsive, "deny, try again" flows. + DenyCacheTTL time.Duration +} + +func NewDelegatingAuthorizationOptions() *DelegatingAuthorizationOptions { + return &DelegatingAuthorizationOptions{ + // very low for responsiveness, but high enough to handle storms + AllowCacheTTL: 10 * time.Second, + DenyCacheTTL: 10 * time.Second, + } +} + +func (s *DelegatingAuthorizationOptions) Validate() []error { + allErrors := []error{} + return allErrors +} + +func (s *DelegatingAuthorizationOptions) AddFlags(fs *pflag.FlagSet) { + fs.StringVar(&s.RemoteKubeConfigFile, "authorization-kubeconfig", s.RemoteKubeConfigFile, ""+ + "kubeconfig file pointing at the 'core' kubernetes server with enough rights to create "+ + " subjectaccessreviews.authorization.k8s.io.") + + fs.DurationVar(&s.AllowCacheTTL, "authorization-webhook-cache-authorized-ttl", + s.AllowCacheTTL, + "The duration to cache 'authorized' responses from the webhook authorizer.") + + fs.DurationVar(&s.DenyCacheTTL, + "authorization-webhook-cache-unauthorized-ttl", s.DenyCacheTTL, + "The duration to cache 'unauthorized' responses from the webhook authorizer.") +} + +func (s *DelegatingAuthorizationOptions) ToAuthorizationConfig() (authorizerfactory.DelegatingAuthorizerConfig, error) { + sarClient, err := s.newSubjectAccessReview() + if err != nil { + return authorizerfactory.DelegatingAuthorizerConfig{}, err + } + + ret := authorizerfactory.DelegatingAuthorizerConfig{ + SubjectAccessReviewClient: sarClient, + AllowCacheTTL: s.AllowCacheTTL, + DenyCacheTTL: s.DenyCacheTTL, + } + return ret, nil +} + +func (s *DelegatingAuthorizationOptions) newSubjectAccessReview() (authorizationclient.SubjectAccessReviewInterface, error) { + var clientConfig *rest.Config + var err error + if len(s.RemoteKubeConfigFile) > 0 { + loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: s.RemoteKubeConfigFile} + loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{}) + + clientConfig, err = loader.ClientConfig() + + } else { + // without the remote kubeconfig file, try to use the in-cluster config. Most addon API servers will + // use this path + clientConfig, err = rest.InClusterConfig() + } + if err != nil { + return nil, err + } + + // set high qps/burst limits since this will effectively limit API server responsiveness + clientConfig.QPS = 200 + clientConfig.Burst = 400 + + client, err := authorizationclient.NewForConfig(clientConfig) + if err != nil { + return nil, err + } + + return client.SubjectAccessReviews(), nil +} diff --git a/pkg/server/options/doc.go b/pkg/server/options/doc.go new file mode 100644 index 000000000..426336be0 --- /dev/null +++ b/pkg/server/options/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// package options is the public flags and options used by a generic api +// server. It takes a minimal set of dependencies and does not reference +// implementations, in order to ensure it may be reused by multiple components +// (such as CLI commands that wish to generate or validate config). +package options // import "k8s.io/apiserver/pkg/server/options" diff --git a/pkg/server/options/etcd.go b/pkg/server/options/etcd.go new file mode 100644 index 000000000..4cbdbece0 --- /dev/null +++ b/pkg/server/options/etcd.go @@ -0,0 +1,86 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package options + +import ( + "fmt" + + "github.com/spf13/pflag" + + "k8s.io/apiserver/pkg/storage/storagebackend" +) + +const ( + DefaultEtcdPathPrefix = "/registry" +) + +type EtcdOptions struct { + StorageConfig storagebackend.Config + + EtcdServersOverrides []string +} + +func NewEtcdOptions() *EtcdOptions { + return &EtcdOptions{ + StorageConfig: storagebackend.Config{ + Prefix: DefaultEtcdPathPrefix, + // Default cache size to 0 - if unset, its size will be set based on target + // memory usage. + DeserializationCacheSize: 0, + }, + } +} + +func (s *EtcdOptions) Validate() []error { + allErrors := []error{} + if len(s.StorageConfig.ServerList) == 0 { + allErrors = append(allErrors, fmt.Errorf("--etcd-servers must be specified")) + } + + return allErrors +} + +// AddEtcdFlags adds flags related to etcd storage for a specific APIServer to the specified FlagSet +func (s *EtcdOptions) AddFlags(fs *pflag.FlagSet) { + fs.StringSliceVar(&s.EtcdServersOverrides, "etcd-servers-overrides", s.EtcdServersOverrides, ""+ + "Per-resource etcd servers overrides, comma separated. The individual override "+ + "format: group/resource#servers, where servers are http://ip:port, semicolon separated.") + + fs.StringVar(&s.StorageConfig.Type, "storage-backend", s.StorageConfig.Type, + "The storage backend for persistence. Options: 'etcd3' (default), 'etcd2'.") + + fs.IntVar(&s.StorageConfig.DeserializationCacheSize, "deserialization-cache-size", s.StorageConfig.DeserializationCacheSize, + "Number of deserialized json objects to cache in memory.") + + fs.StringSliceVar(&s.StorageConfig.ServerList, "etcd-servers", s.StorageConfig.ServerList, + "List of etcd servers to connect with (scheme://ip:port), comma separated.") + + fs.StringVar(&s.StorageConfig.Prefix, "etcd-prefix", s.StorageConfig.Prefix, + "The prefix to prepend to all resource paths in etcd.") + + fs.StringVar(&s.StorageConfig.KeyFile, "etcd-keyfile", s.StorageConfig.KeyFile, + "SSL key file used to secure etcd communication.") + + fs.StringVar(&s.StorageConfig.CertFile, "etcd-certfile", s.StorageConfig.CertFile, + "SSL certification file used to secure etcd communication.") + + fs.StringVar(&s.StorageConfig.CAFile, "etcd-cafile", s.StorageConfig.CAFile, + "SSL Certificate Authority file used to secure etcd communication.") + + fs.BoolVar(&s.StorageConfig.Quorum, "etcd-quorum-read", s.StorageConfig.Quorum, + "If true, enable quorum read.") +} diff --git a/pkg/server/options/server_run_options.go b/pkg/server/options/server_run_options.go new file mode 100644 index 000000000..dcdebd83e --- /dev/null +++ b/pkg/server/options/server_run_options.go @@ -0,0 +1,204 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package options + +import ( + "fmt" + "net" + "strings" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apiserver/pkg/admission" + utilfeature "k8s.io/apiserver/pkg/util/feature" + utilflag "k8s.io/apiserver/pkg/util/flag" + + // add the generic feature gates + _ "k8s.io/apiserver/pkg/features" + + "github.com/spf13/pflag" +) + +// ServerRunOptions contains the options while running a generic api server. +type ServerRunOptions struct { + AdmissionControl string + AdmissionControlConfigFile string + AdvertiseAddress net.IP + + CorsAllowedOriginList []string + // To enable protobuf as storage format, it is enough + // to set it to "application/vnd.kubernetes.protobuf". + DefaultStorageMediaType string + DeleteCollectionWorkers int + AuditLogPath string + AuditLogMaxAge int + AuditLogMaxBackups int + AuditLogMaxSize int + EnableGarbageCollection bool + EnableProfiling bool + EnableContentionProfiling bool + EnableSwaggerUI bool + EnableWatchCache bool + ExternalHost string + MaxRequestsInFlight int + MaxMutatingRequestsInFlight int + MinRequestTimeout int + RuntimeConfig utilflag.ConfigurationMap + TargetRAMMB int + WatchCacheSizes []string +} + +func NewServerRunOptions() *ServerRunOptions { + return &ServerRunOptions{ + AdmissionControl: "AlwaysAdmit", + DefaultStorageMediaType: "application/json", + DeleteCollectionWorkers: 1, + EnableGarbageCollection: true, + EnableProfiling: true, + EnableContentionProfiling: false, + EnableWatchCache: true, + MaxRequestsInFlight: 400, + MaxMutatingRequestsInFlight: 200, + MinRequestTimeout: 1800, + RuntimeConfig: make(utilflag.ConfigurationMap), + } +} + +// DefaultAdvertiseAddress sets the field AdvertiseAddress if +// unset. The field will be set based on the SecureServingOptions. If +// the SecureServingOptions is not present, DefaultExternalAddress +// will fall back to the insecure ServingOptions. +func (s *ServerRunOptions) DefaultAdvertiseAddress(secure *SecureServingOptions, insecure *ServingOptions) error { + if s.AdvertiseAddress == nil || s.AdvertiseAddress.IsUnspecified() { + switch { + case secure != nil: + hostIP, err := secure.ServingOptions.DefaultExternalAddress() + if err != nil { + return fmt.Errorf("Unable to find suitable network address.error='%v'. "+ + "Try to set the AdvertiseAddress directly or provide a valid BindAddress to fix this.", err) + } + s.AdvertiseAddress = hostIP + + case insecure != nil: + hostIP, err := insecure.DefaultExternalAddress() + if err != nil { + return fmt.Errorf("Unable to find suitable network address.error='%v'. "+ + "Try to set the AdvertiseAddress directly or provide a valid BindAddress to fix this.", err) + } + s.AdvertiseAddress = hostIP + } + } + + return nil +} + +// AddFlags adds flags for a specific APIServer to the specified FlagSet +func (s *ServerRunOptions) AddUniversalFlags(fs *pflag.FlagSet) { + // Note: the weird ""+ in below lines seems to be the only way to get gofmt to + // arrange these text blocks sensibly. Grrr. + + fs.StringVar(&s.AdmissionControl, "admission-control", s.AdmissionControl, ""+ + "Ordered list of plug-ins to do admission control of resources into cluster. "+ + "Comma-delimited list of: "+strings.Join(admission.GetPlugins(), ", ")+".") + + fs.StringVar(&s.AdmissionControlConfigFile, "admission-control-config-file", s.AdmissionControlConfigFile, + "File with admission control configuration.") + + fs.IPVar(&s.AdvertiseAddress, "advertise-address", s.AdvertiseAddress, ""+ + "The IP address on which to advertise the apiserver to members of the cluster. This "+ + "address must be reachable by the rest of the cluster. If blank, the --bind-address "+ + "will be used. If --bind-address is unspecified, the host's default interface will "+ + "be used.") + + fs.StringSliceVar(&s.CorsAllowedOriginList, "cors-allowed-origins", s.CorsAllowedOriginList, ""+ + "List of allowed origins for CORS, comma separated. An allowed origin can be a regular "+ + "expression to support subdomain matching. If this list is empty CORS will not be enabled.") + + fs.StringVar(&s.DefaultStorageMediaType, "storage-media-type", s.DefaultStorageMediaType, ""+ + "The media type to use to store objects in storage. Defaults to application/json. "+ + "Some resources may only support a specific media type and will ignore this setting.") + + fs.IntVar(&s.DeleteCollectionWorkers, "delete-collection-workers", s.DeleteCollectionWorkers, + "Number of workers spawned for DeleteCollection call. These are used to speed up namespace cleanup.") + + fs.StringVar(&s.AuditLogPath, "audit-log-path", s.AuditLogPath, + "If set, all requests coming to the apiserver will be logged to this file.") + fs.IntVar(&s.AuditLogMaxAge, "audit-log-maxage", s.AuditLogMaxBackups, + "The maximum number of days to retain old audit log files based on the timestamp encoded in their filename.") + fs.IntVar(&s.AuditLogMaxBackups, "audit-log-maxbackup", s.AuditLogMaxBackups, + "The maximum number of old audit log files to retain.") + fs.IntVar(&s.AuditLogMaxSize, "audit-log-maxsize", s.AuditLogMaxSize, + "The maximum size in megabytes of the audit log file before it gets rotated. Defaults to 100MB.") + + fs.BoolVar(&s.EnableGarbageCollection, "enable-garbage-collector", s.EnableGarbageCollection, ""+ + "Enables the generic garbage collector. MUST be synced with the corresponding flag "+ + "of the kube-controller-manager.") + + fs.BoolVar(&s.EnableProfiling, "profiling", s.EnableProfiling, + "Enable profiling via web interface host:port/debug/pprof/") + fs.BoolVar(&s.EnableContentionProfiling, "contention-profiling", s.EnableContentionProfiling, + "Enable contention profiling. Requires --profiling to be set to work.") + + fs.BoolVar(&s.EnableSwaggerUI, "enable-swagger-ui", s.EnableSwaggerUI, + "Enables swagger ui on the apiserver at /swagger-ui") + + // TODO: enable cache in integration tests. + fs.BoolVar(&s.EnableWatchCache, "watch-cache", s.EnableWatchCache, + "Enable watch caching in the apiserver") + + fs.IntVar(&s.TargetRAMMB, "target-ram-mb", s.TargetRAMMB, + "Memory limit for apiserver in MB (used to configure sizes of caches, etc.)") + + fs.StringVar(&s.ExternalHost, "external-hostname", s.ExternalHost, + "The hostname to use when generating externalized URLs for this master (e.g. Swagger API Docs).") + + // TODO: remove post-1.6 + fs.String("long-running-request-regexp", "", ""+ + "A regular expression matching long running requests which should "+ + "be excluded from maximum inflight request handling.") + fs.MarkDeprecated("long-running-request-regexp", "regular expression matching of long-running requests is no longer supported") + + deprecatedMasterServiceNamespace := metav1.NamespaceDefault + fs.StringVar(&deprecatedMasterServiceNamespace, "master-service-namespace", deprecatedMasterServiceNamespace, ""+ + "DEPRECATED: the namespace from which the kubernetes master services should be injected into pods.") + + fs.IntVar(&s.MaxRequestsInFlight, "max-requests-inflight", s.MaxRequestsInFlight, ""+ + "The maximum number of non-mutating requests in flight at a given time. When the server exceeds this, "+ + "it rejects requests. Zero for no limit.") + + fs.IntVar(&s.MaxMutatingRequestsInFlight, "max-mutating-requests-inflight", s.MaxMutatingRequestsInFlight, ""+ + "The maximum number of mutating requests in flight at a given time. When the server exceeds this, "+ + "it rejects requests. Zero for no limit.") + + fs.IntVar(&s.MinRequestTimeout, "min-request-timeout", s.MinRequestTimeout, ""+ + "An optional field indicating the minimum number of seconds a handler must keep "+ + "a request open before timing it out. Currently only honored by the watch request "+ + "handler, which picks a randomized value above this number as the connection timeout, "+ + "to spread out load.") + + fs.Var(&s.RuntimeConfig, "runtime-config", ""+ + "A set of key=value pairs that describe runtime configuration that may be passed "+ + "to apiserver. apis/ key can be used to turn on/off specific api versions. "+ + "apis// can be used to turn on/off specific resources. api/all and "+ + "api/legacy are special keys to control all and legacy api versions respectively.") + + fs.StringSliceVar(&s.WatchCacheSizes, "watch-cache-sizes", s.WatchCacheSizes, ""+ + "List of watch cache sizes for every resource (pods, nodes, etc.), comma separated. "+ + "The individual override format: resource#size, where size is a number. It takes effect "+ + "when watch-cache is enabled.") + + utilfeature.DefaultFeatureGate.AddFlag(fs) +} diff --git a/pkg/server/options/serving.go b/pkg/server/options/serving.go new file mode 100644 index 000000000..e9f5722c2 --- /dev/null +++ b/pkg/server/options/serving.go @@ -0,0 +1,220 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package options + +import ( + "fmt" + "net" + "path" + + "github.com/golang/glog" + "github.com/spf13/pflag" + + utilnet "k8s.io/apimachinery/pkg/util/net" + utilflag "k8s.io/apiserver/pkg/util/flag" + certutil "k8s.io/client-go/util/cert" +) + +type ServingOptions struct { + BindAddress net.IP + BindPort int +} + +type SecureServingOptions struct { + ServingOptions ServingOptions + + // ServerCert is the TLS cert info for serving secure traffic + ServerCert GeneratableKeyCert + // SNICertKeys are named CertKeys for serving secure traffic with SNI support. + SNICertKeys []utilflag.NamedCertKey +} + +type CertKey struct { + // CertFile is a file containing a PEM-encoded certificate, and possibly the complete certificate chain + CertFile string + // KeyFile is a file containing a PEM-encoded private key for the certificate specified by CertFile + KeyFile string +} + +type GeneratableKeyCert struct { + CertKey CertKey + + // CACertFile is an optional file containing the certificate chain for CertKey.CertFile + CACertFile string + // CertDirectory is a directory that will contain the certificates. If the cert and key aren't specifically set + // this will be used to derive a match with the "pair-name" + CertDirectory string + // PairName is the name which will be used with CertDirectory to make a cert and key names + // It becomes CertDirector/PairName.crt and CertDirector/PairName.key + PairName string +} + +func NewSecureServingOptions() *SecureServingOptions { + return &SecureServingOptions{ + ServingOptions: ServingOptions{ + BindAddress: net.ParseIP("0.0.0.0"), + BindPort: 6443, + }, + ServerCert: GeneratableKeyCert{ + PairName: "apiserver", + CertDirectory: "/var/run/kubernetes", + }, + } +} + +func (s *SecureServingOptions) Validate() []error { + errors := []error{} + if s == nil { + return errors + } + + errors = append(errors, s.ServingOptions.Validate("secure-port")...) + return errors +} + +func (s *SecureServingOptions) AddFlags(fs *pflag.FlagSet) { + fs.IPVar(&s.ServingOptions.BindAddress, "bind-address", s.ServingOptions.BindAddress, ""+ + "The IP address on which to listen for the --secure-port port. The "+ + "associated interface(s) must be reachable by the rest of the cluster, and by CLI/web "+ + "clients. If blank, all interfaces will be used (0.0.0.0).") + + fs.IntVar(&s.ServingOptions.BindPort, "secure-port", s.ServingOptions.BindPort, ""+ + "The port on which to serve HTTPS with authentication and authorization. If 0, "+ + "don't serve HTTPS at all.") + + fs.StringVar(&s.ServerCert.CertDirectory, "cert-dir", s.ServerCert.CertDirectory, ""+ + "The directory where the TLS certs are located (by default /var/run/kubernetes). "+ + "If --tls-cert-file and --tls-private-key-file are provided, this flag will be ignored.") + + fs.StringVar(&s.ServerCert.CertKey.CertFile, "tls-cert-file", s.ServerCert.CertKey.CertFile, ""+ + "File containing the default x509 Certificate for HTTPS. (CA cert, if any, concatenated "+ + "after server cert). If HTTPS serving is enabled, and --tls-cert-file and "+ + "--tls-private-key-file are not provided, a self-signed certificate and key "+ + "are generated for the public address and saved to /var/run/kubernetes.") + + fs.StringVar(&s.ServerCert.CertKey.KeyFile, "tls-private-key-file", s.ServerCert.CertKey.KeyFile, + "File containing the default x509 private key matching --tls-cert-file.") + + fs.StringVar(&s.ServerCert.CACertFile, "tls-ca-file", s.ServerCert.CACertFile, "If set, this "+ + "certificate authority will used for secure access from Admission "+ + "Controllers. This must be a valid PEM-encoded CA bundle. Altneratively, the certificate authority "+ + "can be appended to the certificate provided by --tls-cert-file.") + + fs.Var(utilflag.NewNamedCertKeyArray(&s.SNICertKeys), "tls-sni-cert-key", ""+ + "A pair of x509 certificate and private key file paths, optionally suffixed with a list of "+ + "domain patterns which are fully qualified domain names, possibly with prefixed wildcard "+ + "segments. If no domain patterns are provided, the names of the certificate are "+ + "extracted. Non-wildcard matches trump over wildcard matches, explicit domain patterns "+ + "trump over extracted names. For multiple key/certificate pairs, use the "+ + "--tls-sni-cert-key multiple times. "+ + "Examples: \"example.key,example.crt\" or \"*.foo.com,foo.com:foo.key,foo.crt\".") +} + +func (s *SecureServingOptions) AddDeprecatedFlags(fs *pflag.FlagSet) { + fs.IPVar(&s.ServingOptions.BindAddress, "public-address-override", s.ServingOptions.BindAddress, + "DEPRECATED: see --bind-address instead.") + fs.MarkDeprecated("public-address-override", "see --bind-address instead.") +} + +func NewInsecureServingOptions() *ServingOptions { + return &ServingOptions{ + BindAddress: net.ParseIP("127.0.0.1"), + BindPort: 8080, + } +} + +func (s ServingOptions) Validate(portArg string) []error { + errors := []error{} + + if s.BindPort < 0 || s.BindPort > 65535 { + errors = append(errors, fmt.Errorf("--%v %v must be between 0 and 65535, inclusive. 0 for turning off secure port.", portArg, s.BindPort)) + } + + return errors +} + +func (s *ServingOptions) DefaultExternalAddress() (net.IP, error) { + return utilnet.ChooseBindAddress(s.BindAddress) +} + +func (s *ServingOptions) AddFlags(fs *pflag.FlagSet) { + fs.IPVar(&s.BindAddress, "insecure-bind-address", s.BindAddress, ""+ + "The IP address on which to serve the --insecure-port (set to 0.0.0.0 for all interfaces). "+ + "Defaults to localhost.") + + fs.IntVar(&s.BindPort, "insecure-port", s.BindPort, ""+ + "The port on which to serve unsecured, unauthenticated access. Default 8080. It is assumed "+ + "that firewall rules are set up such that this port is not reachable from outside of "+ + "the cluster and that port 443 on the cluster's public address is proxied to this "+ + "port. This is performed by nginx in the default setup.") +} + +func (s *ServingOptions) AddDeprecatedFlags(fs *pflag.FlagSet) { + fs.IPVar(&s.BindAddress, "address", s.BindAddress, + "DEPRECATED: see --insecure-bind-address instead.") + fs.MarkDeprecated("address", "see --insecure-bind-address instead.") + + fs.IntVar(&s.BindPort, "port", s.BindPort, "DEPRECATED: see --insecure-port instead.") + fs.MarkDeprecated("port", "see --insecure-port instead.") +} + +func (s *SecureServingOptions) MaybeDefaultWithSelfSignedCerts(publicAddress string, alternateIPs ...net.IP) error { + if s == nil { + return nil + } + keyCert := &s.ServerCert.CertKey + if s.ServingOptions.BindPort == 0 || len(keyCert.CertFile) != 0 || len(keyCert.KeyFile) != 0 { + return nil + } + + keyCert.CertFile = path.Join(s.ServerCert.CertDirectory, s.ServerCert.PairName+".crt") + keyCert.KeyFile = path.Join(s.ServerCert.CertDirectory, s.ServerCert.PairName+".key") + + canReadCertAndKey, err := certutil.CanReadCertAndKey(keyCert.CertFile, keyCert.KeyFile) + if err != nil { + return err + } + if !canReadCertAndKey { + // TODO: It would be nice to set a fqdn subject alt name, but only the kubelets know, the apiserver is clueless + // alternateDNS = append(alternateDNS, "kubernetes.default.svc.CLUSTER.DNS.NAME") + // TODO (cjcullen): Is ClusterIP the right address to sign a cert with? + alternateDNS := []string{"kubernetes.default.svc", "kubernetes.default", "kubernetes"} + + // add either the bind address or localhost to the valid alternates + bindIP := s.ServingOptions.BindAddress.String() + if bindIP == "0.0.0.0" { + alternateDNS = append(alternateDNS, "localhost") + } else { + alternateIPs = append(alternateIPs, s.ServingOptions.BindAddress) + } + + if cert, key, err := certutil.GenerateSelfSignedCertKey(publicAddress, alternateIPs, alternateDNS); err != nil { + return fmt.Errorf("unable to generate self signed cert: %v", err) + } else { + if err := certutil.WriteCert(keyCert.CertFile, cert); err != nil { + return err + } + + if err := certutil.WriteKey(keyCert.KeyFile, key); err != nil { + return err + } + glog.Infof("Generated self-signed cert (%s, %s)", keyCert.CertFile, keyCert.KeyFile) + } + } + + return nil +}