2019-01-12 04:58:27 +00:00
/ *
Copyright 2014 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 app does all of the work necessary to create a Kubernetes
// APIServer by binding together the API, master and APIServer infrastructure.
// It can be configured and called directly or via the hyperkube framework.
package app
import (
2019-02-22 06:55:23 +00:00
"crypto/tls"
2019-01-12 04:58:27 +00:00
"fmt"
"net"
"net/http"
"net/url"
"os"
2019-08-30 18:33:25 +00:00
"strconv"
2019-01-12 04:58:27 +00:00
"strings"
"time"
2019-02-08 04:04:22 +00:00
"k8s.io/kubernetes/pkg/kubelet/types"
2019-01-12 04:58:27 +00:00
"github.com/spf13/cobra"
2019-08-30 18:33:25 +00:00
extensionsapiserver "k8s.io/apiextensions-apiserver/pkg/apiserver"
2019-01-12 04:58:27 +00:00
utilerrors "k8s.io/apimachinery/pkg/util/errors"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/sets"
2019-08-30 18:33:25 +00:00
utilwait "k8s.io/apimachinery/pkg/util/wait"
2019-01-12 04:58:27 +00:00
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authorization/authorizer"
2019-08-30 18:33:25 +00:00
openapinamer "k8s.io/apiserver/pkg/endpoints/openapi"
2020-03-26 21:07:15 +00:00
genericfeatures "k8s.io/apiserver/pkg/features"
2019-01-12 04:58:27 +00:00
genericapiserver "k8s.io/apiserver/pkg/server"
2020-03-26 21:07:15 +00:00
"k8s.io/apiserver/pkg/server/egressselector"
2019-01-12 04:58:27 +00:00
"k8s.io/apiserver/pkg/server/filters"
serveroptions "k8s.io/apiserver/pkg/server/options"
serverstorage "k8s.io/apiserver/pkg/server/storage"
2019-08-30 18:33:25 +00:00
"k8s.io/apiserver/pkg/storage/etcd3/preflight"
2019-12-12 01:27:03 +00:00
"k8s.io/apiserver/pkg/util/feature"
2019-09-27 21:51:53 +00:00
utilfeature "k8s.io/apiserver/pkg/util/feature"
2020-03-26 21:07:15 +00:00
utilflowcontrol "k8s.io/apiserver/pkg/util/flowcontrol"
2019-01-12 04:58:27 +00:00
"k8s.io/apiserver/pkg/util/webhook"
clientgoinformers "k8s.io/client-go/informers"
clientgoclientset "k8s.io/client-go/kubernetes"
2020-08-10 17:43:49 +00:00
"k8s.io/client-go/rest"
2019-04-07 17:07:55 +00:00
"k8s.io/client-go/util/keyutil"
2019-08-30 18:33:25 +00:00
cloudprovider "k8s.io/cloud-provider"
2019-04-07 17:07:55 +00:00
cliflag "k8s.io/component-base/cli/flag"
"k8s.io/component-base/cli/globalflag"
2019-09-27 21:51:53 +00:00
_ "k8s.io/component-base/metrics/prometheus/workqueue" // for workqueue metric registration
2020-08-10 17:43:49 +00:00
"k8s.io/component-base/term"
2019-09-27 21:51:53 +00:00
"k8s.io/component-base/version"
2019-12-12 01:27:03 +00:00
"k8s.io/component-base/version/verflag"
2020-08-10 17:43:49 +00:00
"k8s.io/klog/v2"
2019-01-12 04:58:27 +00:00
aggregatorapiserver "k8s.io/kube-aggregator/pkg/apiserver"
2019-08-30 18:33:25 +00:00
aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme"
2019-01-12 04:58:27 +00:00
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/capabilities"
2019-09-27 21:51:53 +00:00
"k8s.io/kubernetes/pkg/features"
2019-08-30 18:33:25 +00:00
generatedopenapi "k8s.io/kubernetes/pkg/generated/openapi"
2019-01-12 04:58:27 +00:00
"k8s.io/kubernetes/pkg/kubeapiserver"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
kubeauthenticator "k8s.io/kubernetes/pkg/kubeapiserver/authenticator"
"k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
kubeserver "k8s.io/kubernetes/pkg/kubeapiserver/server"
"k8s.io/kubernetes/pkg/master"
"k8s.io/kubernetes/pkg/master/reconcilers"
2019-08-30 18:33:25 +00:00
"k8s.io/kubernetes/pkg/master/tunneler"
2019-01-12 04:58:27 +00:00
rbacrest "k8s.io/kubernetes/pkg/registry/rbac/rest"
"k8s.io/kubernetes/pkg/serviceaccount"
)
2019-08-30 18:33:25 +00:00
const (
etcdRetryLimit = 60
etcdRetryInterval = 1 * time . Second
)
2019-01-12 04:58:27 +00:00
var (
DefaultProxyDialerFn utilnet . DialFunc
)
// NewAPIServerCommand creates a *cobra.Command object with default parameters
func NewAPIServerCommand ( stopCh <- chan struct { } ) * cobra . Command {
s := options . NewServerRunOptions ( )
cmd := & cobra . Command {
Use : "kube-apiserver" ,
Long : ` The Kubernetes API server validates and configures data
for the api objects which include pods , services , replicationcontrollers , and
others . The API Server services REST operations and provides the frontend to the
cluster ' s shared state through which all other components interact . ` ,
2020-08-10 17:43:49 +00:00
// stop printing usage when the command errors
SilenceUsage : true ,
PersistentPreRunE : func ( * cobra . Command , [ ] string ) error {
// silence client-go warnings.
// kube-apiserver loopback clients should not log self-issued warnings.
rest . SetDefaultWarningHandler ( rest . NoWarnings { } )
return nil
} ,
2019-01-12 04:58:27 +00:00
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
verflag . PrintAndExitIfRequested ( )
2020-08-10 17:43:49 +00:00
cliflag . PrintFlags ( cmd . Flags ( ) )
2019-01-12 04:58:27 +00:00
// set default options
completedOptions , err := Complete ( s )
if err != nil {
return err
}
// validate options
if errs := completedOptions . Validate ( ) ; len ( errs ) != 0 {
return utilerrors . NewAggregate ( errs )
}
return Run ( completedOptions , stopCh )
} ,
2020-08-10 17:43:49 +00:00
Args : func ( cmd * cobra . Command , args [ ] string ) error {
for _ , arg := range args {
if len ( arg ) > 0 {
return fmt . Errorf ( "%q does not take any arguments, got %q" , cmd . CommandPath ( ) , args )
}
}
return nil
} ,
2019-01-12 04:58:27 +00:00
}
fs := cmd . Flags ( )
namedFlagSets := s . Flags ( )
verflag . AddFlags ( namedFlagSets . FlagSet ( "global" ) )
globalflag . AddGlobalFlags ( namedFlagSets . FlagSet ( "global" ) , cmd . Name ( ) )
options . AddCustomGlobalFlags ( namedFlagSets . FlagSet ( "generic" ) )
for _ , f := range namedFlagSets . FlagSets {
fs . AddFlagSet ( f )
}
usageFmt := "Usage:\n %s\n"
2019-04-07 17:07:55 +00:00
cols , _ , _ := term . TerminalSize ( cmd . OutOrStdout ( ) )
2019-01-12 04:58:27 +00:00
cmd . SetUsageFunc ( func ( cmd * cobra . Command ) error {
fmt . Fprintf ( cmd . OutOrStderr ( ) , usageFmt , cmd . UseLine ( ) )
2019-04-07 17:07:55 +00:00
cliflag . PrintSections ( cmd . OutOrStderr ( ) , namedFlagSets , cols )
2019-01-12 04:58:27 +00:00
return nil
} )
cmd . SetHelpFunc ( func ( cmd * cobra . Command , args [ ] string ) {
fmt . Fprintf ( cmd . OutOrStdout ( ) , "%s\n\n" + usageFmt , cmd . Long , cmd . UseLine ( ) )
2019-04-07 17:07:55 +00:00
cliflag . PrintSections ( cmd . OutOrStdout ( ) , namedFlagSets , cols )
2019-01-12 04:58:27 +00:00
} )
return cmd
}
type startupConfig struct {
Handler http . Handler
Authenticator authenticator . Request
}
var StartupConfig = make ( chan startupConfig , 1 )
// Run runs the specified APIServer. This should never exit.
func Run ( completeOptions completedServerRunOptions , stopCh <- chan struct { } ) error {
// To help debugging, immediately log version
klog . Infof ( "Version: %+v" , version . Get ( ) )
2019-09-27 21:51:53 +00:00
server , err := CreateServerChain ( completeOptions , stopCh )
2019-01-12 04:58:27 +00:00
if err != nil {
return err
}
2019-09-27 21:51:53 +00:00
prepared , err := server . PrepareRun ( )
if err != nil {
return err
2019-01-12 04:58:27 +00:00
}
2019-09-27 21:51:53 +00:00
return prepared . Run ( stopCh )
2019-01-12 04:58:27 +00:00
}
// CreateServerChain creates the apiservers connected via delegation.
2019-09-27 21:51:53 +00:00
func CreateServerChain ( completedOptions completedServerRunOptions , stopCh <- chan struct { } ) ( * aggregatorapiserver . APIAggregator , error ) {
2019-08-30 18:33:25 +00:00
nodeTunneler , proxyTransport , err := CreateNodeDialer ( completedOptions )
2019-02-22 06:55:23 +00:00
if err != nil {
2019-09-27 21:51:53 +00:00
return nil , err
2019-02-22 06:55:23 +00:00
}
2019-01-12 04:58:27 +00:00
if DefaultProxyDialerFn != nil {
completedOptions . KubeletConfig . Dial = DefaultProxyDialerFn
2019-04-14 14:55:34 +00:00
completedOptions . KubeletConfig . Proxy = http . ProxyURL ( nil )
2019-01-12 04:58:27 +00:00
}
2019-12-12 01:27:03 +00:00
kubeAPIServerConfig , insecureServingInfo , serviceResolver , pluginInitializer , err := CreateKubeAPIServerConfig ( completedOptions , nodeTunneler , proxyTransport )
2019-01-12 04:58:27 +00:00
if err != nil {
2019-09-27 21:51:53 +00:00
return nil , err
2019-01-12 04:58:27 +00:00
}
// If additional API servers are added, they should be gated.
apiExtensionsConfig , err := createAPIExtensionsConfig ( * kubeAPIServerConfig . GenericConfig , kubeAPIServerConfig . ExtraConfig . VersionedInformers , pluginInitializer , completedOptions . ServerRunOptions , completedOptions . MasterCount ,
2020-03-26 21:07:15 +00:00
serviceResolver , webhook . NewDefaultAuthenticationInfoResolverWrapper ( proxyTransport , kubeAPIServerConfig . GenericConfig . EgressSelector , kubeAPIServerConfig . GenericConfig . LoopbackClientConfig ) )
2019-01-12 04:58:27 +00:00
if err != nil {
2019-09-27 21:51:53 +00:00
return nil , err
2019-01-12 04:58:27 +00:00
}
apiExtensionsServer , err := createAPIExtensionsServer ( apiExtensionsConfig , genericapiserver . NewEmptyDelegate ( ) )
if err != nil {
2019-09-27 21:51:53 +00:00
return nil , err
2019-01-12 04:58:27 +00:00
}
2019-12-12 01:27:03 +00:00
kubeAPIServer , err := CreateKubeAPIServer ( kubeAPIServerConfig , apiExtensionsServer . GenericAPIServer )
2019-01-12 04:58:27 +00:00
if err != nil {
2019-09-27 21:51:53 +00:00
return nil , err
2019-01-12 04:58:27 +00:00
}
// aggregator comes last in the chain
2019-08-30 18:33:25 +00:00
aggregatorConfig , err := createAggregatorConfig ( * kubeAPIServerConfig . GenericConfig , completedOptions . ServerRunOptions , kubeAPIServerConfig . ExtraConfig . VersionedInformers , serviceResolver , proxyTransport , pluginInitializer )
2019-01-12 04:58:27 +00:00
if err != nil {
2019-09-27 21:51:53 +00:00
return nil , err
2019-01-12 04:58:27 +00:00
}
aggregatorServer , err := createAggregatorServer ( aggregatorConfig , kubeAPIServer . GenericAPIServer , apiExtensionsServer . Informers )
if err != nil {
// we don't need special handling for innerStopCh because the aggregator server doesn't create any go routines
2019-09-27 21:51:53 +00:00
return nil , err
2019-01-12 04:58:27 +00:00
}
if insecureServingInfo != nil {
insecureHandlerChain := kubeserver . BuildInsecureHandlerChain ( aggregatorServer . GenericAPIServer . UnprotectedHandler ( ) , kubeAPIServerConfig . GenericConfig )
if err := insecureServingInfo . Serve ( insecureHandlerChain , kubeAPIServerConfig . GenericConfig . RequestTimeout , stopCh ) ; err != nil {
2019-09-27 21:51:53 +00:00
return nil , err
2019-01-12 04:58:27 +00:00
}
}
2019-09-27 21:51:53 +00:00
StartupConfig <- startupConfig {
Handler : aggregatorServer . GenericAPIServer . Handler ,
Authenticator : kubeAPIServerConfig . GenericConfig . Authentication . Authenticator ,
}
close ( StartupConfig )
return aggregatorServer , nil
2019-01-12 04:58:27 +00:00
}
// CreateKubeAPIServer creates and wires a workable kube-apiserver
2019-12-12 01:27:03 +00:00
func CreateKubeAPIServer ( kubeAPIServerConfig * master . Config , delegateAPIServer genericapiserver . DelegationTarget ) ( * master . Master , error ) {
2019-01-12 04:58:27 +00:00
kubeAPIServer , err := kubeAPIServerConfig . Complete ( ) . New ( delegateAPIServer )
if err != nil {
return nil , err
}
return kubeAPIServer , nil
}
2019-02-22 06:55:23 +00:00
// CreateNodeDialer creates the dialer infrastructure to connect to the nodes.
2019-08-30 18:33:25 +00:00
func CreateNodeDialer ( s completedServerRunOptions ) ( tunneler . Tunneler , * http . Transport , error ) {
// Setup nodeTunneler if needed
var nodeTunneler tunneler . Tunneler
var proxyDialerFn utilnet . DialFunc
if len ( s . SSHUser ) > 0 {
// Get ssh key distribution func, if supported
var installSSHKey tunneler . InstallSSHKey
2020-08-10 17:43:49 +00:00
cloudprovider . DeprecationWarningForProvider ( s . CloudProvider . CloudProvider )
2019-08-30 18:33:25 +00:00
cloud , err := cloudprovider . InitCloudProvider ( s . CloudProvider . CloudProvider , s . CloudProvider . CloudConfigFile )
if err != nil {
return nil , nil , fmt . Errorf ( "cloud provider could not be initialized: %v" , err )
}
if cloud != nil {
if instances , supported := cloud . Instances ( ) ; supported {
installSSHKey = instances . AddSSHKeyToAllInstances
}
}
if s . KubeletConfig . Port == 0 {
return nil , nil , fmt . Errorf ( "must enable kubelet port if proxy ssh-tunneling is specified" )
}
if s . KubeletConfig . ReadOnlyPort == 0 {
return nil , nil , fmt . Errorf ( "must enable kubelet readonly port if proxy ssh-tunneling is specified" )
}
// Set up the nodeTunneler
// TODO(cjcullen): If we want this to handle per-kubelet ports or other
// kubelet listen-addresses, we need to plumb through options.
healthCheckPath := & url . URL {
Scheme : "http" ,
Host : net . JoinHostPort ( "127.0.0.1" , strconv . FormatUint ( uint64 ( s . KubeletConfig . ReadOnlyPort ) , 10 ) ) ,
Path : "healthz" ,
}
nodeTunneler = tunneler . New ( s . SSHUser , s . SSHKeyfile , healthCheckPath , installSSHKey )
// Use the nodeTunneler's dialer when proxying to pods, services, and nodes
proxyDialerFn = nodeTunneler . Dial
}
// Proxying to pods and services is IP-based... don't expect to be able to verify the hostname
2019-02-22 06:55:23 +00:00
proxyTLSClientConfig := & tls . Config { InsecureSkipVerify : true }
proxyTransport := utilnet . SetTransportDefaults ( & http . Transport {
2019-08-30 18:33:25 +00:00
DialContext : proxyDialerFn ,
2019-02-22 06:55:23 +00:00
TLSClientConfig : proxyTLSClientConfig ,
} )
2019-08-30 18:33:25 +00:00
return nodeTunneler , proxyTransport , nil
2019-02-22 06:55:23 +00:00
}
2019-01-12 04:58:27 +00:00
// CreateKubeAPIServerConfig creates all the resources for running the API server, but runs none of them
func CreateKubeAPIServerConfig (
s completedServerRunOptions ,
2019-08-30 18:33:25 +00:00
nodeTunneler tunneler . Tunneler ,
2019-02-22 06:55:23 +00:00
proxyTransport * http . Transport ,
2019-01-12 04:58:27 +00:00
) (
2019-12-12 01:27:03 +00:00
* master . Config ,
* genericapiserver . DeprecatedInsecureServingInfo ,
aggregatorapiserver . ServiceResolver ,
[ ] admission . PluginInitializer ,
error ,
2019-01-12 04:58:27 +00:00
) {
2019-12-12 01:27:03 +00:00
genericConfig , versionedInformers , insecureServingInfo , serviceResolver , pluginInitializers , admissionPostStartHook , storageFactory , err := buildGenericConfig ( s . ServerRunOptions , proxyTransport )
if err != nil {
return nil , nil , nil , nil , err
2019-01-12 04:58:27 +00:00
}
2019-08-30 18:33:25 +00:00
if _ , port , err := net . SplitHostPort ( s . Etcd . StorageConfig . Transport . ServerList [ 0 ] ) ; err == nil && port != "0" && len ( port ) != 0 {
if err := utilwait . PollImmediate ( etcdRetryInterval , etcdRetryLimit * etcdRetryInterval , preflight . EtcdConnection { ServerList : s . Etcd . StorageConfig . Transport . ServerList } . CheckEtcdServers ) ; err != nil {
2019-12-12 01:27:03 +00:00
return nil , nil , nil , nil , fmt . Errorf ( "error waiting for etcd connection: %v" , err )
2019-08-30 18:33:25 +00:00
}
}
2019-02-08 04:04:22 +00:00
all , _ := types . GetValidatedSources ( [ ] string { types . AllSource } )
2019-08-30 18:33:25 +00:00
2019-01-12 04:58:27 +00:00
capabilities . Initialize ( capabilities . Capabilities {
AllowPrivileged : s . AllowPrivileged ,
// TODO(vmarmol): Implement support for HostNetworkSources.
PrivilegedSources : capabilities . PrivilegedSources {
2019-02-08 04:04:22 +00:00
HostNetworkSources : all ,
HostPIDSources : all ,
HostIPCSources : all ,
2019-01-12 04:58:27 +00:00
} ,
PerConnectionBandwidthLimitBytesPerSec : s . MaxConnectionBytesPerSec ,
} )
2020-08-10 17:43:49 +00:00
s . Metrics . Apply ( )
serviceaccount . RegisterMetrics ( )
s . Logs . Apply ( )
2019-12-12 01:27:03 +00:00
serviceIPRange , apiServerServiceIP , err := master . ServiceIPRange ( s . PrimaryServiceClusterIPRange )
if err != nil {
return nil , nil , nil , nil , err
2019-01-12 04:58:27 +00:00
}
2019-09-27 21:51:53 +00:00
// defaults to empty range and ip
var secondaryServiceIPRange net . IPNet
// process secondary range only if provided by user
if s . SecondaryServiceClusterIPRange . IP != nil {
2019-12-12 01:27:03 +00:00
secondaryServiceIPRange , _ , err = master . ServiceIPRange ( s . SecondaryServiceClusterIPRange )
if err != nil {
return nil , nil , nil , nil , err
2019-09-27 21:51:53 +00:00
}
}
2019-12-12 01:27:03 +00:00
config := & master . Config {
2019-01-12 04:58:27 +00:00
GenericConfig : genericConfig ,
ExtraConfig : master . ExtraConfig {
APIResourceConfigSource : storageFactory . APIResourceConfigSource ,
StorageFactory : storageFactory ,
EventTTL : s . EventTTL ,
KubeletClientConfig : s . KubeletConfig ,
EnableLogsSupport : s . EnableLogsHandler ,
2019-02-22 06:55:23 +00:00
ProxyTransport : proxyTransport ,
2019-01-12 04:58:27 +00:00
2019-08-30 18:33:25 +00:00
Tunneler : nodeTunneler ,
2019-09-27 21:51:53 +00:00
ServiceIPRange : serviceIPRange ,
APIServerServiceIP : apiServerServiceIP ,
SecondaryServiceIPRange : secondaryServiceIPRange ,
2019-01-12 04:58:27 +00:00
APIServerServicePort : 443 ,
ServiceNodePortRange : s . ServiceNodePortRange ,
KubernetesServiceNodePort : s . KubernetesServiceNodePort ,
EndpointReconcilerType : reconcilers . Type ( s . EndpointReconcilerType ) ,
MasterCount : s . MasterCount ,
ServiceAccountIssuer : s . ServiceAccountIssuer ,
ServiceAccountMaxExpiration : s . ServiceAccountTokenMaxExpiration ,
2020-08-10 17:43:49 +00:00
ExtendExpiration : s . Authentication . ServiceAccounts . ExtendExpiration ,
2019-01-12 04:58:27 +00:00
VersionedInformers : versionedInformers ,
} ,
}
2019-12-12 01:27:03 +00:00
clientCAProvider , err := s . Authentication . ClientCert . GetClientCAContentProvider ( )
if err != nil {
return nil , nil , nil , nil , err
}
config . ExtraConfig . ClusterAuthenticationInfo . ClientCA = clientCAProvider
requestHeaderConfig , err := s . Authentication . RequestHeader . ToAuthenticationRequestHeaderConfig ( )
if err != nil {
return nil , nil , nil , nil , err
}
if requestHeaderConfig != nil {
config . ExtraConfig . ClusterAuthenticationInfo . RequestHeaderCA = requestHeaderConfig . CAContentProvider
config . ExtraConfig . ClusterAuthenticationInfo . RequestHeaderAllowedNames = requestHeaderConfig . AllowedClientNames
config . ExtraConfig . ClusterAuthenticationInfo . RequestHeaderExtraHeaderPrefixes = requestHeaderConfig . ExtraHeaderPrefixes
config . ExtraConfig . ClusterAuthenticationInfo . RequestHeaderGroupHeaders = requestHeaderConfig . GroupHeaders
config . ExtraConfig . ClusterAuthenticationInfo . RequestHeaderUsernameHeaders = requestHeaderConfig . UsernameHeaders
}
if err := config . GenericConfig . AddPostStartHook ( "start-kube-apiserver-admission-initializer" , admissionPostStartHook ) ; err != nil {
return nil , nil , nil , nil , err
}
2019-08-30 18:33:25 +00:00
if nodeTunneler != nil {
// Use the nodeTunneler's dialer to connect to the kubelet
config . ExtraConfig . KubeletClientConfig . Dial = nodeTunneler . Dial
}
2019-09-27 21:51:53 +00:00
if config . GenericConfig . EgressSelector != nil {
// Use the config.GenericConfig.EgressSelector lookup to find the dialer to connect to the kubelet
config . ExtraConfig . KubeletClientConfig . Lookup = config . GenericConfig . EgressSelector . Lookup
2020-03-26 21:07:15 +00:00
// Use the config.GenericConfig.EgressSelector lookup as the transport used by the "proxy" subresources.
networkContext := egressselector . Cluster . AsNetworkContext ( )
dialer , err := config . GenericConfig . EgressSelector . Lookup ( networkContext )
if err != nil {
return nil , nil , nil , nil , err
}
c := proxyTransport . Clone ( )
c . DialContext = dialer
config . ExtraConfig . ProxyTransport = c
}
if utilfeature . DefaultFeatureGate . Enabled ( features . ServiceAccountIssuerDiscovery ) {
// Load the public keys.
var pubKeys [ ] interface { }
for _ , f := range s . Authentication . ServiceAccounts . KeyFiles {
keys , err := keyutil . PublicKeysFromFile ( f )
if err != nil {
return nil , nil , nil , nil , fmt . Errorf ( "failed to parse key file %q: %v" , f , err )
}
pubKeys = append ( pubKeys , keys ... )
}
// Plumb the required metadata through ExtraConfig.
config . ExtraConfig . ServiceAccountIssuerURL = s . Authentication . ServiceAccounts . Issuer
config . ExtraConfig . ServiceAccountJWKSURI = s . Authentication . ServiceAccounts . JWKSURI
config . ExtraConfig . ServiceAccountPublicKeys = pubKeys
2019-09-27 21:51:53 +00:00
}
2019-08-30 18:33:25 +00:00
2019-12-12 01:27:03 +00:00
return config , insecureServingInfo , serviceResolver , pluginInitializers , nil
2019-01-12 04:58:27 +00:00
}
// BuildGenericConfig takes the master server options and produces the genericapiserver.Config associated with it
func buildGenericConfig (
s * options . ServerRunOptions ,
2019-02-22 06:55:23 +00:00
proxyTransport * http . Transport ,
2019-01-12 04:58:27 +00:00
) (
genericConfig * genericapiserver . Config ,
versionedInformers clientgoinformers . SharedInformerFactory ,
insecureServingInfo * genericapiserver . DeprecatedInsecureServingInfo ,
serviceResolver aggregatorapiserver . ServiceResolver ,
pluginInitializers [ ] admission . PluginInitializer ,
admissionPostStartHook genericapiserver . PostStartHookFunc ,
storageFactory * serverstorage . DefaultStorageFactory ,
lastErr error ,
) {
genericConfig = genericapiserver . NewConfig ( legacyscheme . Codecs )
genericConfig . MergedResourceConfig = master . DefaultAPIResourceConfigSource ( )
if lastErr = s . GenericServerRunOptions . ApplyTo ( genericConfig ) ; lastErr != nil {
return
}
if lastErr = s . InsecureServing . ApplyTo ( & insecureServingInfo , & genericConfig . LoopbackClientConfig ) ; lastErr != nil {
return
}
if lastErr = s . SecureServing . ApplyTo ( & genericConfig . SecureServing , & genericConfig . LoopbackClientConfig ) ; lastErr != nil {
return
}
if lastErr = s . Features . ApplyTo ( genericConfig ) ; lastErr != nil {
return
}
if lastErr = s . APIEnablement . ApplyTo ( genericConfig , master . DefaultAPIResourceConfigSource ( ) , legacyscheme . Scheme ) ; lastErr != nil {
return
}
2019-09-27 21:51:53 +00:00
if lastErr = s . EgressSelector . ApplyTo ( genericConfig ) ; lastErr != nil {
return
}
2019-01-12 04:58:27 +00:00
2019-08-30 18:33:25 +00:00
genericConfig . OpenAPIConfig = genericapiserver . DefaultOpenAPIConfig ( generatedopenapi . GetOpenAPIDefinitions , openapinamer . NewDefinitionNamer ( legacyscheme . Scheme , extensionsapiserver . Scheme , aggregatorscheme . Scheme ) )
genericConfig . OpenAPIConfig . Info . Title = "Kubernetes"
2019-01-12 04:58:27 +00:00
genericConfig . LongRunningFunc = filters . BasicLongRunningRequestCheck (
sets . NewString ( "watch" , "proxy" ) ,
sets . NewString ( "attach" , "exec" , "proxy" , "log" , "portforward" ) ,
)
kubeVersion := version . Get ( )
genericConfig . Version = & kubeVersion
storageFactoryConfig := kubeapiserver . NewStorageFactoryConfig ( )
2019-12-12 01:27:03 +00:00
storageFactoryConfig . APIResourceConfig = genericConfig . MergedResourceConfig
2019-04-07 17:07:55 +00:00
completedStorageFactoryConfig , err := storageFactoryConfig . Complete ( s . Etcd )
2019-01-12 04:58:27 +00:00
if err != nil {
lastErr = err
return
}
storageFactory , lastErr = completedStorageFactoryConfig . New ( )
if lastErr != nil {
return
}
2019-09-27 21:51:53 +00:00
if genericConfig . EgressSelector != nil {
storageFactory . StorageConfig . Transport . EgressLookup = genericConfig . EgressSelector . Lookup
}
2019-01-12 04:58:27 +00:00
if lastErr = s . Etcd . ApplyWithStorageFactoryTo ( storageFactory , genericConfig ) ; lastErr != nil {
return
}
// Use protobufs for self-communication.
// Since not every generic apiserver has to support protobufs, we
// cannot default to it in generic apiserver and need to explicitly
// set it in kube-apiserver.
genericConfig . LoopbackClientConfig . ContentConfig . ContentType = "application/vnd.kubernetes.protobuf"
2019-09-27 21:51:53 +00:00
// Disable compression for self-communication, since we are going to be
// on a fast local network
genericConfig . LoopbackClientConfig . DisableCompression = true
2019-01-12 04:58:27 +00:00
kubeClientConfig := genericConfig . LoopbackClientConfig
clientgoExternalClient , err := clientgoclientset . NewForConfig ( kubeClientConfig )
if err != nil {
lastErr = fmt . Errorf ( "failed to create real external clientset: %v" , err )
return
}
versionedInformers = clientgoinformers . NewSharedInformerFactory ( clientgoExternalClient , 10 * time . Minute )
2020-06-23 22:12:14 +00:00
// Authentication.ApplyTo requires already applied OpenAPIConfig and EgressSelector if present
if lastErr = s . Authentication . ApplyTo ( & genericConfig . Authentication , genericConfig . SecureServing , genericConfig . EgressSelector , genericConfig . OpenAPIConfig , clientgoExternalClient , versionedInformers ) ; lastErr != nil {
2019-01-12 04:58:27 +00:00
return
}
2020-03-26 21:07:15 +00:00
genericConfig . Authorization . Authorizer , genericConfig . RuleResolver , err = BuildAuthorizer ( s , genericConfig . EgressSelector , versionedInformers )
2019-01-12 04:58:27 +00:00
if err != nil {
lastErr = fmt . Errorf ( "invalid authorization config: %v" , err )
return
}
if ! sets . NewString ( s . Authorization . Modes ... ) . Has ( modes . ModeRBAC ) {
genericConfig . DisabledPostStartHooks . Insert ( rbacrest . PostStartHookName )
}
2020-08-10 17:43:49 +00:00
lastErr = s . Audit . ApplyTo ( genericConfig )
if lastErr != nil {
return
}
2019-01-12 04:58:27 +00:00
admissionConfig := & kubeapiserveradmission . Config {
ExternalInformers : versionedInformers ,
LoopbackClientConfig : genericConfig . LoopbackClientConfig ,
CloudConfigFile : s . CloudProvider . CloudConfigFile ,
}
serviceResolver = buildServiceResolver ( s . EnableAggregatorRouting , genericConfig . LoopbackClientConfig . Host , versionedInformers )
2020-03-26 21:07:15 +00:00
pluginInitializers , admissionPostStartHook , err = admissionConfig . New ( proxyTransport , genericConfig . EgressSelector , serviceResolver )
2019-01-12 04:58:27 +00:00
if err != nil {
lastErr = fmt . Errorf ( "failed to create admission plugin initializer: %v" , err )
return
}
err = s . Admission . ApplyTo (
genericConfig ,
versionedInformers ,
kubeClientConfig ,
2019-12-12 01:27:03 +00:00
feature . DefaultFeatureGate ,
2019-01-12 04:58:27 +00:00
pluginInitializers ... )
if err != nil {
lastErr = fmt . Errorf ( "failed to initialize admission: %v" , err )
}
2020-03-26 21:07:15 +00:00
if utilfeature . DefaultFeatureGate . Enabled ( genericfeatures . APIPriorityAndFairness ) && s . GenericServerRunOptions . EnablePriorityAndFairness {
genericConfig . FlowControl = BuildPriorityAndFairness ( s , clientgoExternalClient , versionedInformers )
}
2019-01-12 04:58:27 +00:00
return
}
// BuildAuthorizer constructs the authorizer
2020-03-26 21:07:15 +00:00
func BuildAuthorizer ( s * options . ServerRunOptions , EgressSelector * egressselector . EgressSelector , versionedInformers clientgoinformers . SharedInformerFactory ) ( authorizer . Authorizer , authorizer . RuleResolver , error ) {
2019-01-12 04:58:27 +00:00
authorizationConfig := s . Authorization . ToAuthorizationConfig ( versionedInformers )
2020-03-26 21:07:15 +00:00
if EgressSelector != nil {
egressDialer , err := EgressSelector . Lookup ( egressselector . Master . AsNetworkContext ( ) )
if err != nil {
return nil , nil , err
}
authorizationConfig . CustomDial = egressDialer
}
2019-01-12 04:58:27 +00:00
return authorizationConfig . New ( )
}
2020-03-26 21:07:15 +00:00
// BuildPriorityAndFairness constructs the guts of the API Priority and Fairness filter
func BuildPriorityAndFairness ( s * options . ServerRunOptions , extclient clientgoclientset . Interface , versionedInformer clientgoinformers . SharedInformerFactory ) utilflowcontrol . Interface {
return utilflowcontrol . New (
versionedInformer ,
extclient . FlowcontrolV1alpha1 ( ) ,
s . GenericServerRunOptions . MaxRequestsInFlight + s . GenericServerRunOptions . MaxMutatingRequestsInFlight ,
s . GenericServerRunOptions . RequestTimeout / 4 ,
)
}
2019-01-12 04:58:27 +00:00
// completedServerRunOptions is a private wrapper that enforces a call of Complete() before Run can be invoked.
type completedServerRunOptions struct {
* options . ServerRunOptions
}
// Complete set default ServerRunOptions.
// Should be called after kube-apiserver flags parsed.
func Complete ( s * options . ServerRunOptions ) ( completedServerRunOptions , error ) {
var options completedServerRunOptions
// set defaults
if err := s . GenericServerRunOptions . DefaultAdvertiseAddress ( s . SecureServing . SecureServingOptions ) ; err != nil {
return options , err
}
if err := kubeoptions . DefaultAdvertiseAddress ( s . GenericServerRunOptions , s . InsecureServing . DeprecatedInsecureServingOptions ) ; err != nil {
return options , err
}
2019-09-27 21:51:53 +00:00
// process s.ServiceClusterIPRange from list to Primary and Secondary
// we process secondary only if provided by user
2019-12-12 01:27:03 +00:00
apiServerServiceIP , primaryServiceIPRange , secondaryServiceIPRange , err := getServiceIPAndRanges ( s . ServiceClusterIPRanges )
if err != nil {
return options , err
2019-09-27 21:51:53 +00:00
}
2019-12-12 01:27:03 +00:00
s . PrimaryServiceClusterIPRange = primaryServiceIPRange
s . SecondaryServiceClusterIPRange = secondaryServiceIPRange
2019-09-27 21:51:53 +00:00
2019-01-12 04:58:27 +00:00
if err := s . SecureServing . MaybeDefaultWithSelfSignedCerts ( s . GenericServerRunOptions . AdvertiseAddress . String ( ) , [ ] string { "kubernetes.default.svc" , "kubernetes.default" , "kubernetes" } , [ ] net . IP { apiServerServiceIP } ) ; err != nil {
return options , fmt . Errorf ( "error creating self-signed certificates: %v" , err )
}
if len ( s . GenericServerRunOptions . ExternalHost ) == 0 {
if len ( s . GenericServerRunOptions . AdvertiseAddress ) > 0 {
s . GenericServerRunOptions . ExternalHost = s . GenericServerRunOptions . AdvertiseAddress . String ( )
} else {
if hostname , err := os . Hostname ( ) ; err == nil {
s . GenericServerRunOptions . ExternalHost = hostname
} else {
return options , fmt . Errorf ( "error finding host name: %v" , err )
}
}
klog . Infof ( "external host was not specified, using %v" , s . GenericServerRunOptions . ExternalHost )
}
s . Authentication . ApplyAuthorization ( s . Authorization )
// Use (ServiceAccountSigningKeyFile != "") as a proxy to the user enabling
// TokenRequest functionality. This defaulting was convenient, but messed up
// a lot of people when they rotated their serving cert with no idea it was
2019-12-12 01:27:03 +00:00
// connected to their service account keys. We are taking this opportunity to
2019-01-12 04:58:27 +00:00
// remove this problematic defaulting.
if s . ServiceAccountSigningKeyFile == "" {
// Default to the private server key for service account token signing
if len ( s . Authentication . ServiceAccounts . KeyFiles ) == 0 && s . SecureServing . ServerCert . CertKey . KeyFile != "" {
if kubeauthenticator . IsValidServiceAccountKeyFile ( s . SecureServing . ServerCert . CertKey . KeyFile ) {
s . Authentication . ServiceAccounts . KeyFiles = [ ] string { s . SecureServing . ServerCert . CertKey . KeyFile }
} else {
klog . Warning ( "No TLS key provided, service account token authentication disabled" )
}
}
}
if s . ServiceAccountSigningKeyFile != "" && s . Authentication . ServiceAccounts . Issuer != "" {
2019-04-07 17:07:55 +00:00
sk , err := keyutil . PrivateKeyFromFile ( s . ServiceAccountSigningKeyFile )
2019-01-12 04:58:27 +00:00
if err != nil {
return options , fmt . Errorf ( "failed to parse service-account-issuer-key-file: %v" , err )
}
if s . Authentication . ServiceAccounts . MaxExpiration != 0 {
lowBound := time . Hour
upBound := time . Duration ( 1 << 32 ) * time . Second
if s . Authentication . ServiceAccounts . MaxExpiration < lowBound ||
s . Authentication . ServiceAccounts . MaxExpiration > upBound {
return options , fmt . Errorf ( "the serviceaccount max expiration must be between 1 hour to 2^32 seconds" )
}
2020-08-10 17:43:49 +00:00
if s . Authentication . ServiceAccounts . ExtendExpiration {
if s . Authentication . ServiceAccounts . MaxExpiration < serviceaccount . WarnOnlyBoundTokenExpirationSeconds * time . Second {
klog . Warningf ( "service-account-extend-token-expiration is true, in order to correctly trigger safe transition logic, service-account-max-token-expiration must be set longer than 3607 seconds (currently %s)" , s . Authentication . ServiceAccounts . MaxExpiration )
}
if s . Authentication . ServiceAccounts . MaxExpiration < serviceaccount . ExpirationExtensionSeconds * time . Second {
klog . Warningf ( "service-account-extend-token-expiration is true, enabling tokens valid up to 1 year, which is longer than service-account-max-token-expiration set to %s" , s . Authentication . ServiceAccounts . MaxExpiration )
}
}
2019-01-12 04:58:27 +00:00
}
s . ServiceAccountIssuer , err = serviceaccount . JWTTokenGenerator ( s . Authentication . ServiceAccounts . Issuer , sk )
if err != nil {
return options , fmt . Errorf ( "failed to build token generator: %v" , err )
}
s . ServiceAccountTokenMaxExpiration = s . Authentication . ServiceAccounts . MaxExpiration
}
if s . Etcd . EnableWatchCache {
2020-08-10 17:43:49 +00:00
// Ensure that overrides parse correctly.
if _ , err := serveroptions . ParseWatchCacheSizes ( s . Etcd . WatchCacheSizes ) ; err != nil {
2019-01-12 04:58:27 +00:00
return options , err
}
}
if s . APIEnablement . RuntimeConfig != nil {
for key , value := range s . APIEnablement . RuntimeConfig {
if key == "v1" || strings . HasPrefix ( key , "v1/" ) ||
key == "api/v1" || strings . HasPrefix ( key , "api/v1/" ) {
delete ( s . APIEnablement . RuntimeConfig , key )
s . APIEnablement . RuntimeConfig [ "/v1" ] = value
}
if key == "api/legacy" {
delete ( s . APIEnablement . RuntimeConfig , key )
}
}
}
options . ServerRunOptions = s
return options , nil
}
func buildServiceResolver ( enabledAggregatorRouting bool , hostname string , informer clientgoinformers . SharedInformerFactory ) webhook . ServiceResolver {
var serviceResolver webhook . ServiceResolver
if enabledAggregatorRouting {
serviceResolver = aggregatorapiserver . NewEndpointServiceResolver (
informer . Core ( ) . V1 ( ) . Services ( ) . Lister ( ) ,
informer . Core ( ) . V1 ( ) . Endpoints ( ) . Lister ( ) ,
)
} else {
serviceResolver = aggregatorapiserver . NewClusterIPServiceResolver (
informer . Core ( ) . V1 ( ) . Services ( ) . Lister ( ) ,
)
}
// resolve kubernetes.default.svc locally
if localHost , err := url . Parse ( hostname ) ; err == nil {
serviceResolver = aggregatorapiserver . NewLoopbackServiceResolver ( serviceResolver , localHost )
}
return serviceResolver
}
2019-12-12 01:27:03 +00:00
func getServiceIPAndRanges ( serviceClusterIPRanges string ) ( net . IP , net . IPNet , net . IPNet , error ) {
serviceClusterIPRangeList := [ ] string { }
if serviceClusterIPRanges != "" {
serviceClusterIPRangeList = strings . Split ( serviceClusterIPRanges , "," )
}
var apiServerServiceIP net . IP
var primaryServiceIPRange net . IPNet
var secondaryServiceIPRange net . IPNet
var err error
// nothing provided by user, use default range (only applies to the Primary)
if len ( serviceClusterIPRangeList ) == 0 {
var primaryServiceClusterCIDR net . IPNet
primaryServiceIPRange , apiServerServiceIP , err = master . ServiceIPRange ( primaryServiceClusterCIDR )
if err != nil {
return net . IP { } , net . IPNet { } , net . IPNet { } , fmt . Errorf ( "error determining service IP ranges: %v" , err )
}
return apiServerServiceIP , primaryServiceIPRange , net . IPNet { } , nil
}
if len ( serviceClusterIPRangeList ) > 0 {
_ , primaryServiceClusterCIDR , err := net . ParseCIDR ( serviceClusterIPRangeList [ 0 ] )
if err != nil {
return net . IP { } , net . IPNet { } , net . IPNet { } , fmt . Errorf ( "service-cluster-ip-range[0] is not a valid cidr" )
}
primaryServiceIPRange , apiServerServiceIP , err = master . ServiceIPRange ( * ( primaryServiceClusterCIDR ) )
if err != nil {
return net . IP { } , net . IPNet { } , net . IPNet { } , fmt . Errorf ( "error determining service IP ranges for primary service cidr: %v" , err )
}
}
// user provided at least two entries
// note: validation asserts that the list is max of two dual stack entries
if len ( serviceClusterIPRangeList ) > 1 {
_ , secondaryServiceClusterCIDR , err := net . ParseCIDR ( serviceClusterIPRangeList [ 1 ] )
if err != nil {
return net . IP { } , net . IPNet { } , net . IPNet { } , fmt . Errorf ( "service-cluster-ip-range[1] is not an ip net" )
}
secondaryServiceIPRange = * secondaryServiceClusterCIDR
2019-01-12 04:58:27 +00:00
}
2019-12-12 01:27:03 +00:00
return apiServerServiceIP , primaryServiceIPRange , secondaryServiceIPRange , nil
2019-01-12 04:58:27 +00:00
}