2014-06-06 23:40:48 +00:00
/ *
Copyright 2014 Google Inc . All rights reserved .
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 .
* /
2014-06-11 23:02:08 +00:00
2014-06-06 23:40:48 +00:00
// apiserver is the main api server and master for the cluster.
// it is responsible for serving the cluster management API.
package main
import (
2014-12-01 22:11:59 +00:00
"crypto/tls"
2014-06-06 23:40:48 +00:00
"flag"
2014-06-16 05:30:02 +00:00
"net"
2014-07-02 00:08:32 +00:00
"net/http"
2014-06-16 05:30:02 +00:00
"strconv"
2014-10-16 21:18:16 +00:00
"strings"
2014-07-18 03:54:54 +00:00
"time"
2014-06-06 23:40:48 +00:00
2014-08-09 21:12:55 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
2014-09-16 14:04:12 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/capabilities"
2014-07-02 00:08:32 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
2014-06-17 17:50:42 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider"
2014-06-16 06:29:07 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/master"
2014-09-26 01:11:01 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
2014-06-06 23:40:48 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
2014-08-30 06:19:32 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/version/verflag"
2014-09-26 01:11:01 +00:00
"github.com/coreos/go-etcd/etcd"
2014-06-25 03:51:57 +00:00
"github.com/golang/glog"
2014-06-06 23:40:48 +00:00
)
var (
2014-10-28 00:56:33 +00:00
// Note: the weird ""+ in below lines seems to be the only way to get gofmt to
// arrange these text blocks sensibly. Grrr.
2014-10-28 23:49:52 +00:00
port = flag . Int ( "port" , 8080 , "" +
2014-10-28 00:56:33 +00:00
"The port to listen on. Default 8080. It is assumed that firewall rules are " +
"set up such that this port is not reachable from outside of the cluster. It is " +
"further assumed that port 443 on the cluster's public address is proxied to this " +
"port. This is performed by nginx in the default setup." )
2014-10-04 04:34:30 +00:00
address = util . IP ( net . ParseIP ( "127.0.0.1" ) )
2014-10-28 00:56:33 +00:00
publicAddressOverride = flag . String ( "public_address_override" , "" , "" +
"Public serving address. Read only port will be opened on this address, " +
"and it is assumed that port 443 at this address will be proxied/redirected " +
"to '-address':'-port'. If blank, the address in the first listed interface " +
"will be used." )
2014-10-28 23:49:52 +00:00
readOnlyPort = flag . Int ( "read_only_port" , 7080 , "" +
2014-10-28 00:56:33 +00:00
"The port from which to serve read-only resources. If 0, don't serve on a " +
"read-only address. It is assumed that firewall rules are set up such that " +
"this port is not reachable from outside of the cluster." )
2014-11-06 00:05:06 +00:00
securePort = flag . Int ( "secure_port" , 0 , "The port from which to serve HTTPS with authentication and authorization. If 0, don't serve HTTPS " )
tlsCertFile = flag . String ( "tls_cert_file" , "" , "File containing x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert)." )
tlsPrivateKeyFile = flag . String ( "tls_private_key_file" , "" , "File containing x509 private key matching --tls_cert_file." )
2014-10-06 23:11:04 +00:00
apiPrefix = flag . String ( "api_prefix" , "/api" , "The prefix for API requests on the server. Default '/api'." )
storageVersion = flag . String ( "storage_version" , "" , "The version to store resources with. Defaults to server preferred" )
cloudProvider = flag . String ( "cloud_provider" , "" , "The provider for cloud services. Empty string for no provider." )
cloudConfigFile = flag . String ( "cloud_config" , "" , "The path to the cloud provider configuration file. Empty string for no configuration file." )
healthCheckMinions = flag . Bool ( "health_check_minions" , true , "If true, health check minions and filter unhealthy ones. Default true." )
eventTTL = flag . Duration ( "event_ttl" , 48 * time . Hour , "Amount of time to retain events. Default 2 days." )
2014-11-06 00:05:06 +00:00
tokenAuthFile = flag . String ( "token_auth_file" , "" , "If set, the file that will be used to secure the secure port of the API server via token authentication." )
authorizationMode = flag . String ( "authorization_mode" , "AlwaysAllow" , "Selects how to do authorization on the secure port. One of: " + strings . Join ( apiserver . AuthorizationModeChoices , "," ) )
authorizationPolicyFile = flag . String ( "authorization_policy_file" , "" , "File with authorization policy in csv format, used with --authorization_mode=ABAC, on the secure port." )
2014-10-06 23:11:04 +00:00
etcdServerList util . StringList
etcdConfigFile = flag . String ( "etcd_config" , "" , "The config file for the etcd client. Mutually exclusive with -etcd_servers." )
corsAllowedOriginList util . StringList
allowPrivileged = flag . Bool ( "allow_privileged" , false , "If true, allow privileged containers." )
portalNet util . IPNet // TODO: make this a list
enableLogsSupport = flag . Bool ( "enable_logs_support" , true , "Enables server endpoint for log collection" )
kubeletConfig = client . KubeletConfig {
2014-10-10 22:19:23 +00:00
Port : 10250 ,
EnableHttps : false ,
}
2014-06-06 23:40:48 +00:00
)
func init ( ) {
2014-10-04 04:34:30 +00:00
flag . Var ( & address , "address" , "The IP address on to serve on (set to 0.0.0.0 for all interfaces)" )
2014-09-26 01:11:01 +00:00
flag . Var ( & etcdServerList , "etcd_servers" , "List of etcd servers to watch (http://ip:port), comma separated. Mutually exclusive with -etcd_config" )
2014-09-03 18:33:52 +00:00
flag . Var ( & corsAllowedOriginList , "cors_allowed_origins" , "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." )
2014-09-18 23:03:34 +00:00
flag . Var ( & portalNet , "portal_net" , "A CIDR notation IP range from which to assign portal IPs. This must not overlap with any IP ranges assigned to nodes for pods." )
2014-10-10 22:19:23 +00:00
client . BindKubeletClientConfigFlags ( flag . CommandLine , & kubeletConfig )
2014-06-06 23:40:48 +00:00
}
2014-09-18 23:03:34 +00:00
// TODO: Longer term we should read this from some config store, rather than a flag.
func verifyPortalFlags ( ) {
if portalNet . IP == nil {
glog . Fatal ( "No -portal_net specified" )
}
}
2014-09-26 01:11:01 +00:00
func newEtcd ( etcdConfigFile string , etcdServerList util . StringList ) ( helper tools . EtcdHelper , err error ) {
var client tools . EtcdGetSet
if etcdConfigFile != "" {
client , err = etcd . NewClientFromFile ( etcdConfigFile )
if err != nil {
return helper , err
}
} else {
client = etcd . NewClient ( etcdServerList )
}
return master . NewEtcdHelper ( client , * storageVersion )
}
2014-06-06 23:40:48 +00:00
func main ( ) {
flag . Parse ( )
2014-06-25 03:51:57 +00:00
util . InitLogs ( )
defer util . FlushLogs ( )
2014-06-06 23:40:48 +00:00
2014-08-01 05:55:44 +00:00
verflag . PrintAndExitIfRequested ( )
2014-09-18 23:03:34 +00:00
verifyPortalFlags ( )
2014-06-06 23:40:48 +00:00
2014-09-26 01:11:01 +00:00
if ( * etcdConfigFile != "" && len ( etcdServerList ) != 0 ) || ( * etcdConfigFile == "" && len ( etcdServerList ) == 0 ) {
glog . Fatalf ( "specify either -etcd_servers or -etcd_config" )
2014-08-15 23:42:37 +00:00
}
2014-09-16 22:18:33 +00:00
capabilities . Initialize ( capabilities . Capabilities {
2014-09-16 14:04:12 +00:00
AllowPrivileged : * allowPrivileged ,
} )
2014-10-22 01:21:44 +00:00
cloud := cloudprovider . InitCloudProvider ( * cloudProvider , * cloudConfigFile )
2014-06-06 23:40:48 +00:00
2014-10-10 22:19:23 +00:00
kubeletClient , err := client . NewKubeletClient ( & kubeletConfig )
if err != nil {
glog . Fatalf ( "Failure to start kubelet client: %v" , err )
2014-07-02 00:08:32 +00:00
}
2014-09-30 00:15:00 +00:00
// TODO: expose same flags as client.BindClientConfigFlags but for a server
clientConfig := & client . Config {
2014-10-04 04:34:30 +00:00
Host : net . JoinHostPort ( address . String ( ) , strconv . Itoa ( int ( * port ) ) ) ,
2014-09-30 00:15:00 +00:00
Version : * storageVersion ,
}
client , err := client . New ( clientConfig )
2014-08-28 13:56:38 +00:00
if err != nil {
glog . Fatalf ( "Invalid server address: %v" , err )
}
2014-07-18 20:22:26 +00:00
2014-09-26 01:11:01 +00:00
helper , err := newEtcd ( * etcdConfigFile , etcdServerList )
2014-09-11 23:01:29 +00:00
if err != nil {
2014-09-26 01:11:01 +00:00
glog . Fatalf ( "Invalid storage version or misconfigured etcd: %v" , err )
2014-09-11 23:01:29 +00:00
}
2014-09-18 23:03:34 +00:00
n := net . IPNet ( portalNet )
2014-11-02 06:50:00 +00:00
2014-11-19 15:31:43 +00:00
authenticator , err := apiserver . NewAuthenticatorFromTokenFile ( * tokenAuthFile )
if err != nil {
glog . Fatalf ( "Invalid Authentication Config: %v" , err )
}
2014-10-06 23:11:04 +00:00
authorizer , err := apiserver . NewAuthorizerFromAuthorizationConfig ( * authorizationMode , * authorizationPolicyFile )
2014-11-02 06:50:00 +00:00
if err != nil {
glog . Fatalf ( "Invalid Authorization Config: %v" , err )
}
2014-10-28 00:56:33 +00:00
config := & master . Config {
2014-10-22 01:21:44 +00:00
Client : client ,
Cloud : cloud ,
EtcdHelper : helper ,
HealthCheckMinions : * healthCheckMinions ,
EventTTL : * eventTTL ,
KubeletClient : kubeletClient ,
2014-10-23 23:55:14 +00:00
PortalNet : & n ,
EnableLogsSupport : * enableLogsSupport ,
EnableUISupport : true ,
APIPrefix : * apiPrefix ,
CorsAllowedOriginList : corsAllowedOriginList ,
2014-10-22 01:21:44 +00:00
ReadOnlyPort : * readOnlyPort ,
ReadWritePort : * port ,
PublicAddress : * publicAddressOverride ,
2014-11-19 15:31:43 +00:00
Authenticator : authenticator ,
2014-11-02 06:50:00 +00:00
Authorizer : authorizer ,
2014-10-28 00:56:33 +00:00
}
m := master . New ( config )
2014-11-06 00:05:06 +00:00
// We serve on 3 ports. See docs/reaching_the_api.md
2014-10-28 00:56:33 +00:00
roLocation := ""
2014-10-20 22:23:28 +00:00
if * readOnlyPort != 0 {
2014-10-28 00:56:33 +00:00
roLocation = net . JoinHostPort ( config . PublicAddress , strconv . Itoa ( config . ReadOnlyPort ) )
}
2014-11-06 00:05:06 +00:00
secureLocation := ""
if * securePort != 0 {
secureLocation = net . JoinHostPort ( config . PublicAddress , strconv . Itoa ( * securePort ) )
}
2014-10-28 00:56:33 +00:00
rwLocation := net . JoinHostPort ( address . String ( ) , strconv . Itoa ( int ( * port ) ) )
// See the flag commentary to understand our assumptions when opening the read-only and read-write ports.
if roLocation != "" {
2014-10-20 22:23:28 +00:00
// Allow 1 read-only request per second, allow up to 20 in a burst before enforcing.
rl := util . NewTokenBucketRateLimiter ( 1.0 , 20 )
readOnlyServer := & http . Server {
2014-10-28 00:56:33 +00:00
Addr : roLocation ,
2014-11-06 17:11:31 +00:00
Handler : apiserver . RecoverPanics ( apiserver . ReadOnly ( apiserver . RateLimit ( rl , m . InsecureHandler ) ) ) ,
2014-10-20 22:23:28 +00:00
ReadTimeout : 5 * time . Minute ,
WriteTimeout : 5 * time . Minute ,
MaxHeaderBytes : 1 << 20 ,
}
2014-11-06 00:05:06 +00:00
glog . Infof ( "Serving read-only insecurely on %s" , roLocation )
2014-10-20 22:23:28 +00:00
go func ( ) {
defer util . HandleCrash ( )
2014-11-05 20:07:33 +00:00
for {
if err := readOnlyServer . ListenAndServe ( ) ; err != nil {
glog . Errorf ( "Unable to listen for read only traffic (%v); will try again." , err )
}
time . Sleep ( 15 * time . Second )
}
2014-10-20 22:23:28 +00:00
} ( )
}
2014-09-03 18:33:52 +00:00
2014-11-06 00:05:06 +00:00
if secureLocation != "" {
secureServer := & http . Server {
Addr : secureLocation ,
Handler : apiserver . RecoverPanics ( m . Handler ) ,
ReadTimeout : 5 * time . Minute ,
WriteTimeout : 5 * time . Minute ,
MaxHeaderBytes : 1 << 20 ,
2014-12-01 22:11:59 +00:00
TLSConfig : & tls . Config {
// Populate PeerCertificates in requests, but don't reject connections without certificates
// This allows certificates to be validated by authenticators, while still allowing other auth types
ClientAuth : tls . RequestClientCert ,
} ,
2014-11-06 00:05:06 +00:00
}
glog . Infof ( "Serving securely on %s" , secureLocation )
go func ( ) {
defer util . HandleCrash ( )
for {
if err := secureServer . ListenAndServeTLS ( * tlsCertFile , * tlsPrivateKeyFile ) ; err != nil {
glog . Errorf ( "Unable to listen for secure (%v); will try again." , err )
}
time . Sleep ( 15 * time . Second )
}
} ( )
}
2014-08-09 21:12:55 +00:00
s := & http . Server {
2014-10-28 00:56:33 +00:00
Addr : rwLocation ,
2014-11-06 17:11:31 +00:00
Handler : apiserver . RecoverPanics ( m . InsecureHandler ) ,
2014-08-18 17:32:29 +00:00
ReadTimeout : 5 * time . Minute ,
WriteTimeout : 5 * time . Minute ,
2014-08-09 21:12:55 +00:00
MaxHeaderBytes : 1 << 20 ,
}
2014-11-06 00:05:06 +00:00
glog . Infof ( "Serving insecurely on %s" , rwLocation )
2014-08-09 21:12:55 +00:00
glog . Fatal ( s . ListenAndServe ( ) )
2014-06-06 23:40:48 +00:00
}