Merge pull request #37830 from sttts/sttts-stratify-cert-generation

Automatic merge from submit-queue

Stratify apiserver cert generation

- move self-signed cert generation to `SecureServingOptions.MaybeDefaultWithSelfSignedCerts`
- make cert generation only depend on `ServerRunOptions`, not on an unfinished `Config` (this breaks the chicken-egg problem of a finished config in https://github.com/kubernetes/kubernetes/pull/35387#pullrequestreview-5368176)
- move loopback client config code into `config_selfclient.go`

Replaces https://github.com/kubernetes/kubernetes/pull/35387#event-833649341 by getting rid of duplicated `Complete`.
This commit is contained in:
Kubernetes Submit Queue 2016-12-05 10:15:47 -08:00 committed by GitHub
commit 5e41d0904f
14 changed files with 472 additions and 333 deletions

View File

@ -35,7 +35,6 @@ go_library(
"//pkg/generated/openapi:go_default_library",
"//pkg/genericapiserver:go_default_library",
"//pkg/genericapiserver/authorizer:go_default_library",
"//pkg/genericapiserver/options:go_default_library",
"//pkg/master:go_default_library",
"//pkg/registry/cachesize:go_default_library",
"//pkg/runtime/schema:go_default_library",

View File

@ -21,6 +21,7 @@ package app
import (
"crypto/tls"
"fmt"
"net"
"net/http"
"net/url"
@ -49,7 +50,6 @@ import (
generatedopenapi "k8s.io/kubernetes/pkg/generated/openapi"
"k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/genericapiserver/authorizer"
genericoptions "k8s.io/kubernetes/pkg/genericapiserver/options"
"k8s.io/kubernetes/pkg/master"
"k8s.io/kubernetes/pkg/registry/cachesize"
"k8s.io/kubernetes/pkg/runtime/schema"
@ -85,20 +85,25 @@ func Run(s *options.ServerRunOptions) error {
return err
}
genericapiserver.DefaultAndValidateRunOptions(s.GenericServerRunOptions)
genericConfig := genericapiserver.NewConfig(). // create the new config
ApplyOptions(s.GenericServerRunOptions). // apply the options selected
ApplySecureServingOptions(s.SecureServing).
ApplyInsecureServingOptions(s.InsecureServing).
ApplyAuthenticationOptions(s.Authentication).
ApplyRBACSuperUser(s.Authorization.RBACSuperUser)
serviceIPRange, apiServerServiceIP, err := master.DefaultServiceIPRange(s.GenericServerRunOptions.ServiceClusterIPRange)
if err != nil {
glog.Fatalf("Error determining service IP ranges: %v", err)
return fmt.Errorf("error determining service IP ranges: %v", err)
}
if err := genericConfig.MaybeGenerateServingCerts(apiServerServiceIP); err != nil {
glog.Fatalf("Failed to generate service certificate: %v", err)
if err := s.SecureServing.MaybeDefaultWithSelfSignedCerts(s.GenericServerRunOptions.AdvertiseAddress.String(), apiServerServiceIP); err != nil {
return fmt.Errorf("error creating self-signed certificates: %v", err)
}
genericapiserver.DefaultAndValidateRunOptions(s.GenericServerRunOptions)
genericConfig, err := genericapiserver.NewConfig(). // create the new config
ApplyOptions(s.GenericServerRunOptions). // apply the options selected
ApplyInsecureServingOptions(s.InsecureServing).
ApplyAuthenticationOptions(s.Authentication).
ApplyRBACSuperUser(s.Authorization.RBACSuperUser).
ApplySecureServingOptions(s.SecureServing)
if err != nil {
return fmt.Errorf("failed to configure https: %s", err)
}
capabilities.Initialize(capabilities.Capabilities{
@ -120,7 +125,7 @@ func Run(s *options.ServerRunOptions) error {
var installSSH genericapiserver.InstallSSHKey
cloud, err := cloudprovider.InitCloudProvider(s.GenericServerRunOptions.CloudProvider, s.GenericServerRunOptions.CloudConfigFile)
if err != nil {
glog.Fatalf("Cloud provider could not be initialized: %v", err)
return fmt.Errorf("cloud provider could not be initialized: %v", err)
}
if cloud != nil {
if instances, supported := cloud.Instances(); supported {
@ -128,7 +133,7 @@ func Run(s *options.ServerRunOptions) error {
}
}
if s.KubeletConfig.Port == 0 {
glog.Fatalf("Must enable kubelet port if proxy ssh-tunneling is specified.")
return fmt.Errorf("must enable kubelet port if proxy ssh-tunneling is specified")
}
// Set up the tunneler
// TODO(cjcullen): If we want this to handle per-kubelet ports or other
@ -173,7 +178,7 @@ func Run(s *options.ServerRunOptions) error {
storageGroupsToEncodingVersion, err := s.GenericServerRunOptions.StorageGroupsToEncodingVersion()
if err != nil {
glog.Fatalf("error generating storage version map: %s", err)
return fmt.Errorf("error generating storage version map: %s", err)
}
storageFactory, err := genericapiserver.BuildDefaultStorageFactory(
s.Etcd.StorageConfig, s.GenericServerRunOptions.DefaultStorageMediaType, api.Codecs,
@ -182,7 +187,7 @@ func Run(s *options.ServerRunOptions) error {
[]schema.GroupVersionResource{batch.Resource("cronjobs").WithVersion("v2alpha1")},
master.DefaultAPIResourceConfigSource(), s.GenericServerRunOptions.RuntimeConfig)
if err != nil {
glog.Fatalf("error in initializing storage factory: %s", err)
return fmt.Errorf("error in initializing storage factory: %s", err)
}
storageFactory.AddCohabitatingResources(batch.Resource("jobs"), extensions.Resource("jobs"))
storageFactory.AddCohabitatingResources(autoscaling.Resource("horizontalpodautoscalers"), extensions.Resource("horizontalpodautoscalers"))
@ -221,20 +226,20 @@ func Run(s *options.ServerRunOptions) error {
// go directly to etcd to avoid recursive auth insanity
storageConfig, err := storageFactory.NewConfig(api.Resource("serviceaccounts"))
if err != nil {
glog.Fatalf("Unable to get serviceaccounts storage: %v", err)
return fmt.Errorf("unable to get serviceaccounts storage: %v", err)
}
authenticatorConfig.ServiceAccountTokenGetter = serviceaccountcontroller.NewGetterFromStorageInterface(storageConfig, storageFactory.ResourcePrefix(api.Resource("serviceaccounts")), storageFactory.ResourcePrefix(api.Resource("secrets")))
}
apiAuthenticator, securityDefinitions, err := authenticator.New(authenticatorConfig)
if err != nil {
glog.Fatalf("Invalid Authentication Config: %v", err)
return fmt.Errorf("invalid Authentication Config: %v", err)
}
privilegedLoopbackToken := uuid.NewRandom().String()
selfClientConfig, err := genericoptions.NewSelfClientConfig(s.SecureServing, s.InsecureServing, privilegedLoopbackToken)
selfClientConfig, err := genericapiserver.NewSelfClientConfig(genericConfig.SecureServingInfo, genericConfig.InsecureServingInfo, privilegedLoopbackToken)
if err != nil {
glog.Fatalf("Failed to create clientset: %v", err)
return fmt.Errorf("failed to create clientset: %v", err)
}
client, err := internalclientset.NewForConfig(selfClientConfig)
if err != nil {
@ -245,14 +250,14 @@ func Run(s *options.ServerRunOptions) error {
authorizationConfig := s.Authorization.ToAuthorizationConfig(sharedInformers)
apiAuthorizer, err := authorizer.NewAuthorizerFromAuthorizationConfig(authorizationConfig)
if err != nil {
glog.Fatalf("Invalid Authorization Config: %v", err)
return fmt.Errorf("invalid Authorization Config: %v", err)
}
admissionControlPluginNames := strings.Split(s.GenericServerRunOptions.AdmissionControl, ",")
pluginInitializer := admission.NewPluginInitializer(sharedInformers, apiAuthorizer)
admissionController, err := admission.NewFromPlugins(client, admissionControlPluginNames, s.GenericServerRunOptions.AdmissionControlConfigFile, pluginInitializer)
if err != nil {
glog.Fatalf("Failed to initialize plugins: %v", err)
return fmt.Errorf("failed to initialize plugins: %v", err)
}
proxyTransport := utilnet.SetTransportDefaults(&http.Transport{

View File

@ -36,6 +36,8 @@ import (
// Install the testgroup API
_ "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/test_apis/testgroup/install"
"github.com/golang/glog"
)
const (
@ -93,20 +95,21 @@ func (serverOptions *ServerRunOptions) Run(stopCh <-chan struct{}) error {
if errs := serverOptions.InsecureServing.Validate("insecure-port"); len(errs) > 0 {
return utilerrors.NewAggregate(errs)
}
if err := serverOptions.SecureServing.MaybeDefaultWithSelfSignedCerts(serverOptions.GenericServerRunOptions.AdvertiseAddress.String()); err != nil {
glog.Fatalf("Error creating self-signed certificates: %v", err)
}
config := genericapiserver.NewConfig().
config, err := genericapiserver.NewConfig().
ApplyOptions(serverOptions.GenericServerRunOptions).
ApplySecureServingOptions(serverOptions.SecureServing).
ApplyInsecureServingOptions(serverOptions.InsecureServing).
ApplyAuthenticationOptions(serverOptions.Authentication).
Complete()
if err := config.MaybeGenerateServingCerts(); err != nil {
// this wasn't treated as fatal for this process before
fmt.Printf("Error creating cert: %v", err)
ApplySecureServingOptions(serverOptions.SecureServing)
if err != nil {
return fmt.Errorf("failed to configure https: %s", err)
}
config.Authorizer = authorizer.NewAlwaysAllowAuthorizer()
s, err := config.New()
s, err := config.Complete().New()
if err != nil {
return fmt.Errorf("Error in bringing up the server: %v", err)
}

View File

@ -42,7 +42,6 @@ go_library(
"//pkg/generated/openapi:go_default_library",
"//pkg/genericapiserver:go_default_library",
"//pkg/genericapiserver/authorizer:go_default_library",
"//pkg/genericapiserver/options:go_default_library",
"//pkg/registry/cachesize:go_default_library",
"//pkg/registry/core/configmap/etcd:go_default_library",
"//pkg/registry/core/event/etcd:go_default_library",

View File

@ -20,6 +20,7 @@ limitations under the License.
package app
import (
"fmt"
"strings"
"time"
@ -37,7 +38,6 @@ import (
"k8s.io/kubernetes/pkg/generated/openapi"
"k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/genericapiserver/authorizer"
genericoptions "k8s.io/kubernetes/pkg/genericapiserver/options"
"k8s.io/kubernetes/pkg/registry/cachesize"
"k8s.io/kubernetes/pkg/registry/generic"
genericregistry "k8s.io/kubernetes/pkg/registry/generic/registry"
@ -73,16 +73,19 @@ func Run(s *options.ServerRunOptions) error {
return err
}
genericapiserver.DefaultAndValidateRunOptions(s.GenericServerRunOptions)
genericConfig := genericapiserver.NewConfig(). // create the new config
ApplyOptions(s.GenericServerRunOptions). // apply the options selected
ApplySecureServingOptions(s.SecureServing).
ApplyInsecureServingOptions(s.InsecureServing).
ApplyAuthenticationOptions(s.Authentication).
ApplyRBACSuperUser(s.Authorization.RBACSuperUser)
if err := s.SecureServing.MaybeDefaultWithSelfSignedCerts(s.GenericServerRunOptions.AdvertiseAddress.String()); err != nil {
return fmt.Errorf("error creating self-signed certificates: %v", err)
}
if err := genericConfig.MaybeGenerateServingCerts(); err != nil {
glog.Fatalf("Failed to generate service certificate: %v", err)
genericapiserver.DefaultAndValidateRunOptions(s.GenericServerRunOptions)
genericConfig, err := genericapiserver.NewConfig(). // create the new config
ApplyOptions(s.GenericServerRunOptions). // apply the options selected
ApplyInsecureServingOptions(s.InsecureServing).
ApplyAuthenticationOptions(s.Authentication).
ApplyRBACSuperUser(s.Authorization.RBACSuperUser).
ApplySecureServingOptions(s.SecureServing)
if err != nil {
return fmt.Errorf("failed to configure https: %s", err)
}
// TODO: register cluster federation resources here.
@ -130,7 +133,7 @@ func Run(s *options.ServerRunOptions) error {
}
privilegedLoopbackToken := uuid.NewRandom().String()
selfClientConfig, err := genericoptions.NewSelfClientConfig(s.SecureServing, s.InsecureServing, privilegedLoopbackToken)
selfClientConfig, err := genericapiserver.NewSelfClientConfig(genericConfig.SecureServingInfo, genericConfig.InsecureServingInfo, privilegedLoopbackToken)
if err != nil {
glog.Fatalf("Failed to create clientset: %v", err)
}

View File

@ -14,6 +14,7 @@ go_library(
name = "go_default_library",
srcs = [
"config.go",
"config_selfclient.go",
"default_storage_factory_builder.go",
"discovery.go",
"doc.go",
@ -117,6 +118,7 @@ go_test(
"//pkg/storage/storagebackend:go_default_library",
"//pkg/util/cert:go_default_library",
"//pkg/util/clock:go_default_library",
"//pkg/util/config:go_default_library",
"//pkg/util/net:go_default_library",
"//pkg/util/sets:go_default_library",
"//pkg/version:go_default_library",

View File

@ -17,12 +17,14 @@ limitations under the License.
package genericapiserver
import (
"crypto/tls"
"encoding/pem"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"os"
"path"
"regexp"
goruntime "runtime"
"sort"
@ -58,7 +60,6 @@ import (
"k8s.io/kubernetes/pkg/genericapiserver/routes"
genericvalidation "k8s.io/kubernetes/pkg/genericapiserver/validation"
"k8s.io/kubernetes/pkg/runtime"
certutil "k8s.io/kubernetes/pkg/util/cert"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/version"
authenticatorunion "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/union"
@ -169,35 +170,21 @@ type ServingInfo struct {
type SecureServingInfo struct {
ServingInfo
// ServerCert is the TLS cert info for serving secure traffic
ServerCert GeneratableKeyCert
// SNICerts are named CertKeys for serving secure traffic with SNI support.
SNICerts []NamedCertKey
// Cert is the main server cert which is used if SNI does not match. Cert must be non-nil and is
// allowed to be in SNICerts.
Cert *tls.Certificate
// CACert is an optional certificate authority used for the loopback connection of the Admission controllers.
// If this is nil, the certificate authority is extracted from Cert or a matching SNI certificate.
CACert *tls.Certificate
// SNICerts are the TLS certificates by name used for SNI.
SNICerts map[string]*tls.Certificate
// ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates
ClientCA string
}
type CertKey struct {
// CertFile is a file containing a PEM-encoded certificate
CertFile string
// KeyFile is a file containing a PEM-encoded private key for the certificate specified by CertFile
KeyFile string
}
type NamedCertKey struct {
CertKey
// Names is a list of domain patterns: fully qualified domain names, possibly prefixed with
// wildcard segments.
Names []string
}
type GeneratableKeyCert struct {
CertKey
// Generate indicates that the cert/key pair should be generated if its not present.
Generate bool
}
// NewConfig returns a Config struct with the default values
func NewConfig() *Config {
longRunningRE := regexp.MustCompile(options.DefaultLongRunningRequestRE)
@ -238,45 +225,69 @@ func NewConfig() *Config {
return config.ApplyOptions(defaultOptions)
}
func (c *Config) ApplySecureServingOptions(secureServing *options.SecureServingOptions) *Config {
func (c *Config) ApplySecureServingOptions(secureServing *options.SecureServingOptions) (*Config, error) {
if secureServing == nil || secureServing.ServingOptions.BindPort <= 0 {
return c
return c, nil
}
secureServingInfo := &SecureServingInfo{
ServingInfo: ServingInfo{
BindAddress: net.JoinHostPort(secureServing.ServingOptions.BindAddress.String(), strconv.Itoa(secureServing.ServingOptions.BindPort)),
},
ServerCert: GeneratableKeyCert{
CertKey: CertKey{
CertFile: secureServing.ServerCert.CertKey.CertFile,
KeyFile: secureServing.ServerCert.CertKey.KeyFile,
},
},
SNICerts: []NamedCertKey{},
ClientCA: secureServing.ClientCA,
}
if secureServing.ServerCert.CertKey.CertFile == "" && secureServing.ServerCert.CertKey.KeyFile == "" {
secureServingInfo.ServerCert.Generate = true
secureServingInfo.ServerCert.CertFile = path.Join(secureServing.ServerCert.CertDirectory, secureServing.ServerCert.PairName+".crt")
secureServingInfo.ServerCert.KeyFile = path.Join(secureServing.ServerCert.CertDirectory, secureServing.ServerCert.PairName+".key")
serverCertFile, serverKeyFile := secureServing.ServerCert.CertKey.CertFile, secureServing.ServerCert.CertKey.KeyFile
// load main cert
if len(serverCertFile) != 0 || len(serverKeyFile) != 0 {
tlsCert, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile)
if err != nil {
return nil, fmt.Errorf("unable to load server certificate: %v", err)
}
secureServingInfo.Cert = &tlsCert
}
secureServingInfo.SNICerts = nil
for _, nkc := range secureServing.SNICertKeys {
secureServingInfo.SNICerts = append(secureServingInfo.SNICerts, NamedCertKey{
CertKey: CertKey{
KeyFile: nkc.KeyFile,
CertFile: nkc.CertFile,
},
Names: nkc.Names,
// optionally load CA cert
if len(secureServing.ServerCert.CACertFile) != 0 {
pemData, err := ioutil.ReadFile(secureServing.ServerCert.CACertFile)
if err != nil {
return nil, fmt.Errorf("failed to read certificate authority from %q: %v", secureServing.ServerCert.CACertFile, err)
}
block, pemData := pem.Decode(pemData)
if block == nil {
return nil, fmt.Errorf("no certificate found in certificate authority file %q", secureServing.ServerCert.CACertFile)
}
if block.Type != "CERTIFICATE" {
return nil, fmt.Errorf("expected CERTIFICATE block in certiticate authority file %q, found: %s", secureServing.ServerCert.CACertFile, block.Type)
}
secureServingInfo.CACert = &tls.Certificate{
Certificate: [][]byte{block.Bytes},
}
}
// load SNI certs
namedTlsCerts := make([]namedTlsCert, 0, len(secureServing.SNICertKeys))
for _, nck := range secureServing.SNICertKeys {
tlsCert, err := tls.LoadX509KeyPair(nck.CertFile, nck.KeyFile)
namedTlsCerts = append(namedTlsCerts, namedTlsCert{
tlsCert: tlsCert,
names: nck.Names,
})
if err != nil {
return nil, fmt.Errorf("failed to load SNI cert and key: %v", err)
}
}
var err error
secureServingInfo.SNICerts, err = getNamedCertificateMap(namedTlsCerts)
if err != nil {
return nil, err
}
c.SecureServingInfo = secureServingInfo
c.ReadWritePort = secureServing.ServingOptions.BindPort
return c
return c, nil
}
func (c *Config) ApplyInsecureServingOptions(insecureServing *options.ServingOptions) *Config {
@ -456,38 +467,6 @@ func (c completedConfig) New() (*GenericAPIServer, error) {
return s, nil
}
// MaybeGenerateServingCerts generates serving certificates if requested and needed.
func (c *Config) MaybeGenerateServingCerts(alternateIPs ...net.IP) error {
// 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")
if c.SecureServingInfo != nil && c.SecureServingInfo.ServerCert.Generate {
canReadCertAndKey, err := certutil.CanReadCertAndKey(c.SecureServingInfo.ServerCert.CertFile, c.SecureServingInfo.ServerCert.KeyFile)
if err != nil {
return err
}
if canReadCertAndKey {
return nil
}
// TODO (cjcullen): Is ClusterIP the right address to sign a cert with?
alternateDNS := []string{"kubernetes.default.svc", "kubernetes.default", "kubernetes", "localhost"}
if cert, key, err := certutil.GenerateSelfSignedCertKey(c.PublicAddress.String(), alternateIPs, alternateDNS); err != nil {
return fmt.Errorf("unable to generate self signed cert: %v", err)
} else {
if err := certutil.WriteCert(c.SecureServingInfo.ServerCert.CertFile, cert); err != nil {
return err
}
if err := certutil.WriteKey(c.SecureServingInfo.ServerCert.KeyFile, key); err != nil {
return err
}
glog.Infof("Generated self-signed cert (%s, %s)", c.SecureServingInfo.ServerCert.CertFile, c.SecureServingInfo.ServerCert.KeyFile)
}
}
return nil
}
func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) (secure, insecure http.Handler) {
generic := func(handler http.Handler) http.Handler {
handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")

View File

@ -0,0 +1,138 @@
/*
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 genericapiserver
import (
"bytes"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"net"
"k8s.io/kubernetes/pkg/client/restclient"
"github.com/golang/glog"
)
// NewSelfClientConfig returns a clientconfig which can be used to talk to this apiserver.
func NewSelfClientConfig(secureServingInfo *SecureServingInfo, insecureServingInfo *ServingInfo, token string) (*restclient.Config, error) {
if cfg, err := secureServingInfo.NewSelfClientConfig(token); err != nil || cfg != nil {
if insecureServingInfo == nil {
// be fatal if insecure port is not available
return cfg, err
} else {
glog.Warningf("Failed to create secure local client, falling back to insecure local connection: %v", err)
}
}
if cfg, err := insecureServingInfo.NewSelfClientConfig(token); err != nil || cfg != nil {
return cfg, err
}
return nil, errors.New("Unable to set url for apiserver local client")
}
func (s *SecureServingInfo) NewSelfClientConfig(token string) (*restclient.Config, error) {
if s == nil || (s.Cert == nil && len(s.SNICerts) == 0) {
return nil, nil
}
host, port, err := net.SplitHostPort(s.ServingInfo.BindAddress)
if err != nil {
// should never happen
return nil, fmt.Errorf("invalid secure bind address: %q", s.ServingInfo.BindAddress)
}
if host == "0.0.0.0" {
// compare MaybeDefaultWithSelfSignedCerts which adds "localhost" to the cert as alternateDNS
host = "localhost"
}
clientConfig := &restclient.Config{
// Increase QPS limits. The client is currently passed to all admission plugins,
// and those can be throttled in case of higher load on apiserver - see #22340 and #22422
// for more details. Once #22422 is fixed, we may want to remove it.
QPS: 50,
Burst: 100,
Host: "https://" + net.JoinHostPort(host, port),
BearerToken: token,
}
// find certificate for host: either explicitly given, from the server cert bundle or one of the SNI certs
var derCA []byte
if s.CACert != nil {
derCA = s.CACert.Certificate[0]
}
if derCA == nil && s.Cert != nil {
x509Cert, err := x509.ParseCertificate(s.Cert.Certificate[0])
if err != nil {
return nil, fmt.Errorf("failed to parse server certificate: %v", err)
}
if (net.ParseIP(host) != nil && certMatchesIP(x509Cert, host)) || certMatchesName(x509Cert, host) {
derCA = s.Cert.Certificate[0]
}
}
if derCA == nil && net.ParseIP(host) == nil {
if cert, found := s.SNICerts[host]; found {
derCA = cert.Certificate[0]
}
}
if derCA == nil {
return nil, fmt.Errorf("failed to find certificate which matches %q", host)
}
pemCA := bytes.Buffer{}
if err := pem.Encode(&pemCA, &pem.Block{Type: "CERTIFICATE", Bytes: derCA}); err != nil {
return nil, err
}
clientConfig.CAData = pemCA.Bytes()
return clientConfig, nil
}
func (s *ServingInfo) NewSelfClientConfig(token string) (*restclient.Config, error) {
if s == nil {
return nil, nil
}
return &restclient.Config{
Host: s.BindAddress,
// Increase QPS limits. The client is currently passed to all admission plugins,
// and those can be throttled in case of higher load on apiserver - see #22340 and #22422
// for more details. Once #22422 is fixed, we may want to remove it.
QPS: 50,
Burst: 100,
}, nil
}
func certMatchesName(cert *x509.Certificate, name string) bool {
for _, certName := range cert.DNSNames {
if certName == name {
return true
}
}
return false
}
func certMatchesIP(cert *x509.Certificate, ip string) bool {
for _, certIP := range cert.IPAddresses {
if certIP.String() == ip {
return true
}
}
return false
}

View File

@ -28,14 +28,15 @@ go_library(
"//pkg/apiserver/authenticator:go_default_library",
"//pkg/client/clientset_generated/release_1_5/typed/authentication/v1beta1:go_default_library",
"//pkg/client/clientset_generated/release_1_5/typed/authorization/v1beta1:go_default_library",
"//pkg/client/restclient:go_default_library",
"//pkg/client/unversioned/clientcmd:go_default_library",
"//pkg/controller/informers:go_default_library",
"//pkg/genericapiserver/authorizer:go_default_library",
"//pkg/runtime/schema:go_default_library",
"//pkg/storage/storagebackend:go_default_library",
"//pkg/util/cert:go_default_library",
"//pkg/util/config:go_default_library",
"//pkg/util/net:go_default_library",
"//vendor:github.com/golang/glog",
"//vendor:github.com/spf13/pflag",
],
)

View File

@ -75,7 +75,6 @@ type ServerRunOptions struct {
// for testing). This is not actually exposed as a flag.
DefaultStorageVersions string
TargetRAMMB int
TLSCAFile string
WatchCacheSizes []string
}

View File

@ -17,14 +17,14 @@ limitations under the License.
package options
import (
"errors"
"fmt"
"net"
"strconv"
"path"
"github.com/golang/glog"
"github.com/spf13/pflag"
"k8s.io/kubernetes/pkg/client/restclient"
certutil "k8s.io/kubernetes/pkg/util/cert"
"k8s.io/kubernetes/pkg/util/config"
utilnet "k8s.io/kubernetes/pkg/util/net"
)
@ -43,14 +43,10 @@ type SecureServingOptions struct {
SNICertKeys []config.NamedCertKey
// ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates
ClientCA string
// ServerCA is the certificate bundle for the signer of your serving certificate. Used for building a loopback
// connection to the API server for admission.
ServerCA string
}
type CertKey struct {
// CertFile is a file containing a PEM-encoded certificate
// 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
@ -59,6 +55,8 @@ type CertKey struct {
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
@ -80,31 +78,6 @@ func NewSecureServingOptions() *SecureServingOptions {
}
}
func (s *SecureServingOptions) NewSelfClientConfig(token string) *restclient.Config {
if s == nil || s.ServingOptions.BindPort <= 0 || len(s.ServerCA) == 0 {
return nil
}
clientConfig := &restclient.Config{
// Increase QPS limits. The client is currently passed to all admission plugins,
// and those can be throttled in case of higher load on apiserver - see #22340 and #22422
// for more details. Once #22422 is fixed, we may want to remove it.
QPS: 50,
Burst: 100,
}
// Use secure port if the ServerCA is specified
host := s.ServingOptions.BindAddress.String()
if host == "0.0.0.0" {
host = "localhost"
}
clientConfig.Host = "https://" + net.JoinHostPort(host, strconv.Itoa(s.ServingOptions.BindPort))
clientConfig.CAFile = s.ServerCA
clientConfig.BearerToken = token
return clientConfig
}
func (s *SecureServingOptions) Validate() []error {
errors := []error{}
if s == nil {
@ -138,6 +111,11 @@ func (s *SecureServingOptions) AddFlags(fs *pflag.FlagSet) {
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(config.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 "+
@ -151,18 +129,12 @@ func (s *SecureServingOptions) AddFlags(fs *pflag.FlagSet) {
"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.")
fs.StringVar(&s.ServerCA, "tls-ca-file", s.ServerCA, "If set, this "+
"certificate authority will used for secure access from Admission "+
"Controllers. This must be a valid PEM-encoded CA bundle.")
}
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 {
@ -182,23 +154,6 @@ func (s ServingOptions) Validate(portArg string) []error {
return errors
}
func (s *ServingOptions) NewSelfClientConfig(token string) *restclient.Config {
if s == nil || s.BindPort <= 0 {
return nil
}
clientConfig := &restclient.Config{
// Increase QPS limits. The client is currently passed to all admission plugins,
// and those can be throttled in case of higher load on apiserver - see #22340 and #22422
// for more details. Once #22422 is fixed, we may want to remove it.
QPS: 50,
Burst: 100,
}
clientConfig.Host = net.JoinHostPort(s.BindAddress.String(), strconv.Itoa(s.BindPort))
return clientConfig
}
func (s *ServingOptions) DefaultExternalAddress() (net.IP, error) {
return utilnet.ChooseBindAddress(s.BindAddress)
}
@ -224,14 +179,47 @@ func (s *ServingOptions) AddDeprecatedFlags(fs *pflag.FlagSet) {
fs.MarkDeprecated("port", "see --insecure-port instead.")
}
// Returns a clientconfig which can be used to talk to this apiserver.
func NewSelfClientConfig(secureServingOptions *SecureServingOptions, insecureServingOptions *ServingOptions, token string) (*restclient.Config, error) {
if cfg := secureServingOptions.NewSelfClientConfig(token); cfg != nil {
return cfg, nil
}
if cfg := insecureServingOptions.NewSelfClientConfig(token); cfg != nil {
return cfg, nil
func (s *SecureServingOptions) MaybeDefaultWithSelfSignedCerts(publicAddress string, alternateIPs ...net.IP) error {
keyCert := &s.ServerCert.CertKey
if s == nil || len(keyCert.CertFile) != 0 || len(keyCert.KeyFile) != 0 {
return nil
}
return nil, errors.New("Unable to set url for apiserver local client")
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
}

View File

@ -42,17 +42,12 @@ const (
// be loaded or the initial listen call fails. The actual server loop (stoppable by closing
// stopCh) runs in a go routine, i.e. serveSecurely does not block.
func (s *GenericAPIServer) serveSecurely(stopCh <-chan struct{}) error {
namedCerts, err := getNamedCertificateMap(s.SecureServingInfo.SNICerts)
if err != nil {
return fmt.Errorf("unable to load SNI certificates: %v", err)
}
secureServer := &http.Server{
Addr: s.SecureServingInfo.BindAddress,
Handler: s.Handler,
MaxHeaderBytes: 1 << 20,
TLSConfig: &tls.Config{
NameToCertificate: namedCerts,
NameToCertificate: s.SecureServingInfo.SNICerts,
// Can't use SSLv3 because of POODLE and BEAST
// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
// Can't use TLSv1.1 because of RC4 cipher usage
@ -62,19 +57,15 @@ func (s *GenericAPIServer) serveSecurely(stopCh <-chan struct{}) error {
},
}
if len(s.SecureServingInfo.ServerCert.CertFile) != 0 || len(s.SecureServingInfo.ServerCert.KeyFile) != 0 {
secureServer.TLSConfig.Certificates = make([]tls.Certificate, 1)
secureServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(s.SecureServingInfo.ServerCert.CertFile, s.SecureServingInfo.ServerCert.KeyFile)
if err != nil {
return fmt.Errorf("unable to load server certificate: %v", err)
}
if s.SecureServingInfo.Cert != nil {
secureServer.TLSConfig.Certificates = []tls.Certificate{*s.SecureServingInfo.Cert}
}
// append all named certs. Otherwise, the go tls stack will think no SNI processing
// is necessary because there is only one cert anyway.
// Moreover, if ServerCert.CertFile/ServerCert.KeyFile are not set, the first SNI
// cert will become the default cert. That's what we expect anyway.
for _, c := range namedCerts {
for _, c := range s.SecureServingInfo.SNICerts {
secureServer.TLSConfig.Certificates = append(secureServer.TLSConfig.Certificates, *c)
}
@ -91,6 +82,7 @@ func (s *GenericAPIServer) serveSecurely(stopCh <-chan struct{}) error {
}
glog.Infof("Serving securely on %s", s.SecureServingInfo.BindAddress)
var err error
s.effectiveSecurePort, err = runServer(secureServer, s.SecureServingInfo.BindNetwork, stopCh)
return err
}
@ -186,48 +178,40 @@ func runServer(server *http.Server, network string, stopCh <-chan struct{}) (int
return tcpAddr.Port, nil
}
// getNamedCertificateMap returns a map of strings to *tls.Certificate, suitable for use in
// tls.Config#NamedCertificates. Returns an error if any of the certs cannot be loaded.
// Returns nil if len(namedCertKeys) == 0
func getNamedCertificateMap(namedCertKeys []NamedCertKey) (map[string]*tls.Certificate, error) {
if len(namedCertKeys) == 0 {
return nil, nil
}
type namedTlsCert struct {
tlsCert tls.Certificate
// load keys
tlsCerts := make([]tls.Certificate, len(namedCertKeys))
for i := range namedCertKeys {
var err error
nkc := &namedCertKeys[i]
tlsCerts[i], err = tls.LoadX509KeyPair(nkc.CertFile, nkc.KeyFile)
if err != nil {
return nil, err
}
}
// names is a list of domain patterns: fully qualified domain names, possibly prefixed with
// wildcard segments.
names []string
}
// getNamedCertificateMap returns a map of *tls.Certificate by name. It's is
// suitable for use in tls.Config#NamedCertificates. Returns an error if any of the certs
// cannot be loaded. Returns nil if len(certs) == 0
func getNamedCertificateMap(certs []namedTlsCert) (map[string]*tls.Certificate, error) {
// register certs with implicit names first, reverse order such that earlier trump over the later
tlsCertsByName := map[string]*tls.Certificate{}
for i := len(namedCertKeys) - 1; i >= 0; i-- {
nkc := &namedCertKeys[i]
if len(nkc.Names) > 0 {
byName := map[string]*tls.Certificate{}
for i := len(certs) - 1; i >= 0; i-- {
if len(certs[i].names) > 0 {
continue
}
cert := &tlsCerts[i]
cert := &certs[i].tlsCert
// read names from certificate common names and DNS names
if len(cert.Certificate) == 0 {
return nil, fmt.Errorf("no certificate found in %q", nkc.CertFile)
return nil, fmt.Errorf("empty SNI certificate, skipping")
}
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
if err != nil {
return nil, fmt.Errorf("parse error for certificate in %q: %v", nkc.CertFile, err)
return nil, fmt.Errorf("parse error for SNI certificate: %v", err)
}
cn := x509Cert.Subject.CommonName
if cn == "*" || len(validation.IsDNS1123Subdomain(strings.TrimPrefix(cn, "*."))) == 0 {
tlsCertsByName[cn] = cert
byName[cn] = cert
}
for _, san := range x509Cert.DNSNames {
tlsCertsByName[san] = cert
byName[san] = cert
}
// intentionally all IPs in the cert are ignored as SNI forbids passing IPs
// to select a cert. Before go 1.6 the tls happily passed IPs as SNI values.
@ -235,17 +219,14 @@ func getNamedCertificateMap(namedCertKeys []NamedCertKey) (map[string]*tls.Certi
// register certs with explicit names last, overwriting every of the implicit ones,
// again in reverse order.
for i := len(namedCertKeys) - 1; i >= 0; i-- {
nkc := &namedCertKeys[i]
if len(nkc.Names) == 0 {
continue
}
for _, name := range nkc.Names {
tlsCertsByName[name] = &tlsCerts[i]
for i := len(certs) - 1; i >= 0; i-- {
namedCert := &certs[i]
for _, name := range namedCert.names {
byName[name] = &certs[i].tlsCert
}
}
return tlsCertsByName, nil
return byName, nil
}
// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted

View File

@ -20,15 +20,18 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"io/ioutil"
"net"
"os"
"testing"
utilcert "k8s.io/kubernetes/pkg/util/cert"
"github.com/stretchr/testify/assert"
"k8s.io/kubernetes/pkg/genericapiserver/options"
utilcert "k8s.io/kubernetes/pkg/util/cert"
"k8s.io/kubernetes/pkg/util/config"
)
type TestCertSpec struct {
@ -41,47 +44,6 @@ type NamedTestCertSpec struct {
explicitNames []string // as --tls-sni-cert-key explicit names
}
func createTestCerts(spec TestCertSpec) (certFilePath, keyFilePath string, err error) {
var ips []net.IP
for _, ip := range spec.ips {
ips = append(ips, net.ParseIP(ip))
}
certPem, keyPem, err := utilcert.GenerateSelfSignedCertKey(spec.host, ips, spec.names)
if err != nil {
return "", "", err
}
certFile, err := ioutil.TempFile(os.TempDir(), "cert")
if err != nil {
return "", "", err
}
keyFile, err := ioutil.TempFile(os.TempDir(), "key")
if err != nil {
os.Remove(certFile.Name())
return "", "", err
}
_, err = certFile.Write(certPem)
if err != nil {
os.Remove(certFile.Name())
os.Remove(keyFile.Name())
return "", "", err
}
certFile.Close()
_, err = keyFile.Write(keyPem)
if err != nil {
os.Remove(certFile.Name())
os.Remove(keyFile.Name())
return "", "", err
}
keyFile.Close()
return certFile.Name(), keyFile.Name(), nil
}
func TestGetNamedCertificateMap(t *testing.T) {
tests := []struct {
certs []NamedTestCertSpec
@ -225,26 +187,21 @@ func TestGetNamedCertificateMap(t *testing.T) {
NextTest:
for i, test := range tests {
var namedCertKeys []NamedCertKey
var namedTLSCerts []namedTlsCert
bySignature := map[string]int{} // index in test.certs by cert signature
for j, c := range test.certs {
certFile, keyFile, err := createTestCerts(c.TestCertSpec)
cert, err := createTestTLSCerts(c.TestCertSpec)
if err != nil {
t.Errorf("%d - failed to create cert %d: %v", i, j, err)
continue NextTest
}
defer os.Remove(certFile)
defer os.Remove(keyFile)
namedCertKeys = append(namedCertKeys, NamedCertKey{
CertKey: CertKey{
KeyFile: keyFile,
CertFile: certFile,
},
Names: c.explicitNames,
namedTLSCerts = append(namedTLSCerts, namedTlsCert{
tlsCert: cert,
names: c.explicitNames,
})
sig, err := certFileSignature(certFile, keyFile)
sig, err := certSignature(cert)
if err != nil {
t.Errorf("%d - failed to get signature for %d: %v", i, j, err)
continue NextTest
@ -252,7 +209,7 @@ NextTest:
bySignature[sig] = j
}
certMap, err := getNamedCertificateMap(namedCertKeys)
certMap, err := getNamedCertificateMap(namedTLSCerts)
if err == nil && len(test.errorString) != 0 {
t.Errorf("%d - expected no error, got: %v", i, err)
} else if err != nil && err.Error() != test.errorString {
@ -363,19 +320,30 @@ func TestServerRunWithSNI(t *testing.T) {
},
}
tempDir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tempDir)
NextTest:
for i, test := range tests {
// create server cert
serverCertFile, serverKeyFile, err := createTestCerts(test.Cert)
serverCertBundleFile, serverKeyFile, err := createTestCertFiles(tempDir, test.Cert)
if err != nil {
t.Errorf("%d - failed to create server cert: %v", i, err)
continue NextTest
}
defer os.Remove(serverCertFile)
defer os.Remove(serverKeyFile)
ca, err := caCertFromBundle(serverCertBundleFile)
if err != nil {
t.Errorf("%d - failed to extract ca cert from server cert bundle: %v", i, err)
continue NextTest
}
caCerts := []*x509.Certificate{ca}
// create SNI certs
var namedCertKeys []NamedCertKey
serverSig, err := certFileSignature(serverCertFile, serverKeyFile)
var namedCertKeys []config.NamedCertKey
serverSig, err := certFileSignature(serverCertBundleFile, serverKeyFile)
if err != nil {
t.Errorf("%d - failed to get server cert signature: %v", i, err)
continue NextTest
@ -384,24 +352,27 @@ NextTest:
serverSig: -1,
}
for j, c := range test.SNICerts {
certFile, keyFile, err := createTestCerts(c.TestCertSpec)
certBundleFile, keyFile, err := createTestCertFiles(tempDir, c.TestCertSpec)
if err != nil {
t.Errorf("%d - failed to create SNI cert %d: %v", i, j, err)
continue NextTest
}
defer os.Remove(certFile)
defer os.Remove(keyFile)
namedCertKeys = append(namedCertKeys, NamedCertKey{
CertKey: CertKey{
KeyFile: keyFile,
CertFile: certFile,
},
Names: c.explicitNames,
namedCertKeys = append(namedCertKeys, config.NamedCertKey{
KeyFile: keyFile,
CertFile: certBundleFile,
Names: c.explicitNames,
})
ca, err := caCertFromBundle(certBundleFile)
if err != nil {
t.Errorf("%d - failed to extract ca cert from SNI cert %d: %v", i, j, err)
continue NextTest
}
caCerts = append(caCerts, ca)
// store index in namedCertKeys with the signature as the key
sig, err := certFileSignature(certFile, keyFile)
sig, err := certFileSignature(certBundleFile, keyFile)
if err != nil {
t.Errorf("%d - failed get SNI cert %d signature: %v", i, j, err)
continue NextTest
@ -416,17 +387,22 @@ NextTest:
defer etcdserver.Terminate(t)
config.EnableIndex = true
config.SecureServingInfo = &SecureServingInfo{
ServingInfo: ServingInfo{
BindAddress: "localhost:0",
_, err = config.ApplySecureServingOptions(&options.SecureServingOptions{
ServingOptions: options.ServingOptions{
BindAddress: net.ParseIP("127.0.0.1"),
BindPort: 6443,
},
ServerCert: GeneratableKeyCert{
CertKey: CertKey{
CertFile: serverCertFile,
ServerCert: options.GeneratableKeyCert{
CertKey: options.CertKey{
CertFile: serverCertBundleFile,
KeyFile: serverKeyFile,
},
},
SNICerts: namedCertKeys,
SNICertKeys: namedCertKeys,
})
if err != nil {
t.Errorf("%d - failed applying the SecureServingOptions: %v", i, err)
continue NextTest
}
config.InsecureServingInfo = nil
@ -436,27 +412,18 @@ NextTest:
continue NextTest
}
// patch in a 0-port to enable auto port allocation
s.SecureServingInfo.BindAddress = "127.0.0.1:0"
if err := s.serveSecurely(stopCh); err != nil {
t.Errorf("%d - failed running the server: %v", i, err)
continue NextTest
}
// load certificates into a pool
// load ca certificates into a pool
roots := x509.NewCertPool()
certFiles := []string{serverCertFile}
for _, c := range namedCertKeys {
certFiles = append(certFiles, c.CertFile)
}
for _, certFile := range certFiles {
bs, err := ioutil.ReadFile(certFile)
if err != nil {
t.Errorf("%d - error reading %q: %v", i, certFile, err)
continue NextTest
}
if ok := roots.AppendCertsFromPEM(bs); !ok {
t.Errorf("%d - error adding cert %q to the pool", i, certFile)
continue NextTest
}
for _, caCert := range caCerts {
roots.AddCert(caCert)
}
// try to dial
@ -485,6 +452,77 @@ NextTest:
}
}
func parseIPList(ips []string) []net.IP {
var netIPs []net.IP
for _, ip := range ips {
netIPs = append(netIPs, net.ParseIP(ip))
}
return netIPs
}
func createTestTLSCerts(spec TestCertSpec) (tlsCert tls.Certificate, err error) {
certPem, keyPem, err := utilcert.GenerateSelfSignedCertKey(spec.host, parseIPList(spec.ips), spec.names)
if err != nil {
return tlsCert, err
}
tlsCert, err = tls.X509KeyPair(certPem, keyPem)
return tlsCert, err
}
func createTestCertFiles(dir string, spec TestCertSpec) (certFilePath, keyFilePath string, err error) {
certPem, keyPem, err := utilcert.GenerateSelfSignedCertKey(spec.host, parseIPList(spec.ips), spec.names)
if err != nil {
return "", "", err
}
certFile, err := ioutil.TempFile(dir, "cert")
if err != nil {
return "", "", err
}
keyFile, err := ioutil.TempFile(dir, "key")
if err != nil {
return "", "", err
}
_, err = certFile.Write(certPem)
if err != nil {
return "", "", err
}
certFile.Close()
_, err = keyFile.Write(keyPem)
if err != nil {
return "", "", err
}
keyFile.Close()
return certFile.Name(), keyFile.Name(), nil
}
func caCertFromBundle(bundlePath string) (*x509.Certificate, error) {
pemData, err := ioutil.ReadFile(bundlePath)
if err != nil {
return nil, err
}
// fetch last block
var block *pem.Block
for {
var nextBlock *pem.Block
nextBlock, pemData = pem.Decode(pemData)
if nextBlock == nil {
if block == nil {
return nil, fmt.Errorf("no certificate found in %q", bundlePath)
}
return x509.ParseCertificate(block.Bytes)
}
block = nextBlock
}
}
func x509CertSignature(cert *x509.Certificate) string {
return base64.StdEncoding.EncodeToString(cert.Signature)
}
@ -494,13 +532,13 @@ func certFileSignature(certFile, keyFile string) (string, error) {
if err != nil {
return "", err
}
return certSignature(cert)
}
func certSignature(cert tls.Certificate) (string, error) {
x509Certs, err := x509.ParseCertificates(cert.Certificate[0])
if err != nil {
return "", err
}
if len(x509Certs) == 0 {
return "", fmt.Errorf("expected at least one cert after reparsing cert %q", certFile)
}
return x509CertSignature(x509Certs[0]), nil
}

View File

@ -140,6 +140,7 @@ Federated Services DNS non-local federated service missing local service should
Federated Services DNS non-local federated service should be able to discover a non-local federated service,jlowdermilk,1
Federated Services DNS should be able to discover a federated service,derekwaynecarr,1
Federated Services Service creation should create matching services in underlying clusters,jbeda,1
Federated Services Service creation should not be deleted from underlying clusters when it is deleted,sttts,0
Federated Services Service creation should succeed,rmmh,1
Federated ingresses Federated Ingresses Ingress connectivity and DNS should be able to connect to a federated ingress via its load balancer,rmmh,1
Federated ingresses Federated Ingresses should be created and deleted successfully,dchen1107,1
@ -444,7 +445,7 @@ Services should be able to create a functioning NodePort service,bprashanth,0
Services should be able to up and down services,bprashanth,0
Services should check NodePort out-of-range,bprashanth,0
Services should create endpoints for unready pods,maisem,0
Serivces should only allow access from service loadbalancer source ranges,freehan,0
Services should only allow access from service loadbalancer source ranges,sttts,0
Services should preserve source pod IP for traffic thru service cluster IP,Random-Liu,1
Services should prevent NodePort collisions,bprashanth,0
Services should provide secure master service,bprashanth,0
@ -546,7 +547,6 @@ k8s.io/kubernetes/pkg/api/errors,yifan-gu,1
k8s.io/kubernetes/pkg/api/events,jlowdermilk,1
k8s.io/kubernetes/pkg/api/install,timothysc,1
k8s.io/kubernetes/pkg/api/meta,fabioy,1
k8s.io/kubernetes/pkg/api/pod,piosz,1
k8s.io/kubernetes/pkg/api/resource,smarterclayton,1
k8s.io/kubernetes/pkg/api/service,spxtr,1
k8s.io/kubernetes/pkg/api/testapi,caesarxuchao,1
@ -554,6 +554,9 @@ k8s.io/kubernetes/pkg/api/unversioned,kevin-wangzefeng,1
k8s.io/kubernetes/pkg/api/unversioned/validation,brendandburns,1
k8s.io/kubernetes/pkg/api/util,ghodss,1
k8s.io/kubernetes/pkg/api/v1,vulpecula,1
k8s.io/kubernetes/pkg/api/v1/endpoints,sttts,0
k8s.io/kubernetes/pkg/api/v1/pod,sttts,0
k8s.io/kubernetes/pkg/api/v1/service,sttts,0
k8s.io/kubernetes/pkg/api/validation,smarterclayton,1
k8s.io/kubernetes/pkg/api/validation/path,luxas,1
k8s.io/kubernetes/pkg/apimachinery,gmarek,1
@ -787,6 +790,7 @@ k8s.io/kubernetes/pkg/registry/policy/poddisruptionbudget/etcd,xiang90,1
k8s.io/kubernetes/pkg/registry/storage/storageclass,brendandburns,1
k8s.io/kubernetes/pkg/registry/storage/storageclass/etcd,eparis,1
k8s.io/kubernetes/pkg/runtime,wojtek-t,0
k8s.io/kubernetes/pkg/runtime/schema,sttts,0
k8s.io/kubernetes/pkg/runtime/serializer,wojtek-t,0
k8s.io/kubernetes/pkg/runtime/serializer/json,wojtek-t,0
k8s.io/kubernetes/pkg/runtime/serializer/protobuf,wojtek-t,0

1 name owner auto-assigned
140 Federated Services DNS non-local federated service should be able to discover a non-local federated service jlowdermilk 1
141 Federated Services DNS should be able to discover a federated service derekwaynecarr 1
142 Federated Services Service creation should create matching services in underlying clusters jbeda 1
143 Federated Services Service creation should not be deleted from underlying clusters when it is deleted sttts 0
144 Federated Services Service creation should succeed rmmh 1
145 Federated ingresses Federated Ingresses Ingress connectivity and DNS should be able to connect to a federated ingress via its load balancer rmmh 1
146 Federated ingresses Federated Ingresses should be created and deleted successfully dchen1107 1
445 Services should be able to up and down services bprashanth 0
446 Services should check NodePort out-of-range bprashanth 0
447 Services should create endpoints for unready pods maisem 0
448 Serivces should only allow access from service loadbalancer source ranges Services should only allow access from service loadbalancer source ranges freehan sttts 0
449 Services should preserve source pod IP for traffic thru service cluster IP Random-Liu 1
450 Services should prevent NodePort collisions bprashanth 0
451 Services should provide secure master service bprashanth 0
547 k8s.io/kubernetes/pkg/api/events jlowdermilk 1
548 k8s.io/kubernetes/pkg/api/install timothysc 1
549 k8s.io/kubernetes/pkg/api/meta fabioy 1
k8s.io/kubernetes/pkg/api/pod piosz 1
550 k8s.io/kubernetes/pkg/api/resource smarterclayton 1
551 k8s.io/kubernetes/pkg/api/service spxtr 1
552 k8s.io/kubernetes/pkg/api/testapi caesarxuchao 1
554 k8s.io/kubernetes/pkg/api/unversioned/validation brendandburns 1
555 k8s.io/kubernetes/pkg/api/util ghodss 1
556 k8s.io/kubernetes/pkg/api/v1 vulpecula 1
557 k8s.io/kubernetes/pkg/api/v1/endpoints sttts 0
558 k8s.io/kubernetes/pkg/api/v1/pod sttts 0
559 k8s.io/kubernetes/pkg/api/v1/service sttts 0
560 k8s.io/kubernetes/pkg/api/validation smarterclayton 1
561 k8s.io/kubernetes/pkg/api/validation/path luxas 1
562 k8s.io/kubernetes/pkg/apimachinery gmarek 1
790 k8s.io/kubernetes/pkg/registry/storage/storageclass brendandburns 1
791 k8s.io/kubernetes/pkg/registry/storage/storageclass/etcd eparis 1
792 k8s.io/kubernetes/pkg/runtime wojtek-t 0
793 k8s.io/kubernetes/pkg/runtime/schema sttts 0
794 k8s.io/kubernetes/pkg/runtime/serializer wojtek-t 0
795 k8s.io/kubernetes/pkg/runtime/serializer/json wojtek-t 0
796 k8s.io/kubernetes/pkg/runtime/serializer/protobuf wojtek-t 0