Skip to content
This repository has been archived by the owner on Feb 9, 2022. It is now read-only.

Commit

Permalink
allow both http for normal access and https for external admission we…
Browse files Browse the repository at this point in the history
…bhook
  • Loading branch information
jmuk committed Sep 26, 2017
1 parent e7cf31b commit e7e16c9
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 17 deletions.
2 changes: 1 addition & 1 deletion cmd/server/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func GetRootCmd(args []string, info map[string]template.Info, adapters []adapter
rootCmd.AddCommand(adapterCmd(printf))
rootCmd.AddCommand(serverCmd(info, adapters, printf, fatalf))
rootCmd.AddCommand(crdCmd(info, adapters, printf, fatalf))
rootCmd.AddCommand(validatorCmd(info, adapters, fatalf))
rootCmd.AddCommand(validatorCmd(info, adapters, printf, fatalf))
rootCmd.AddCommand(shared.VersionCmd(printf))

return rootCmd
Expand Down
23 changes: 17 additions & 6 deletions cmd/server/cmd/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,26 +35,28 @@ type validatorConfig struct {
targetNamespaces []string
resources map[string]proto.Message
port uint16
httpPort uint16
secretName string
certsDir string
}

func validatorCmd(info map[string]template.Info, adapters []adapter.InfoFn, fatalf shared.FormatFn) *cobra.Command {
func validatorCmd(info map[string]template.Info, adapters []adapter.InfoFn, printf, fatalf shared.FormatFn) *cobra.Command {
vc := &validatorConfig{
resources: runtime.KindMap(config.InventoryMap(adapters), info),
}
validatorCmd := &cobra.Command{
Use: "validator",
Short: "Runs an https server for validations. Works as an external admission webhook for k8s",
Run: func(cmd *cobra.Command, args []string) {
runValidator(vc, fatalf)
runValidator(vc, printf, fatalf)
},
}
validatorCmd.PersistentFlags().StringVar(&vc.namespace, "namespace", "default", "the namespace where this webhook is deployed")
validatorCmd.PersistentFlags().StringVar(&vc.name, "webhook-name", "istio-mixer-webhook", "the name of the webhook")
validatorCmd.PersistentFlags().StringArrayVar(&vc.targetNamespaces, "target-namespaces", []string{},
"the list of namespaces where changes should be validated. Empty means to validate everything.")
validatorCmd.PersistentFlags().Uint16VarP(&vc.port, "port", "p", 9099, "the port number of the server")
validatorCmd.PersistentFlags().Uint16Var(&vc.httpPort, "http-port", 9199, "the port number to access to the server through plain http")
validatorCmd.PersistentFlags().StringVar(&vc.certsDir, "certs", "/etc/certs", "the directory name where cert files are stored")
validatorCmd.PersistentFlags().StringVar(&vc.secretName, "secret-name", "", "The name of k8s secret where the certificates are stored")
return validatorCmd
Expand Down Expand Up @@ -90,16 +92,25 @@ func createCertProvider(vc *validatorConfig, client *kubernetes.Clientset) crd.C
return crd.NewFileCertProvider(vc.certsDir)
}

func runValidator(vc *validatorConfig, fatalf shared.FormatFn) {
func runValidator(vc *validatorConfig, printf, fatalf shared.FormatFn) {
client, err := createK8sClient()
if err != nil {
fatalf("Failed to create kubernetes client: %v", err)
printf("Failed to create kubernetes client: %v", err)
printf("Starting plain http server, but external admission hook is not enabled")
client = nil
}
vs, err := createValidatorServer(vc, client)
if err != nil {
fatalf("Failed to create validator server: %v", err)
}
if err = vs.Start(vc.port, createCertProvider(vc, client)); err != nil {
fatalf("Failed to start validator server: %v", err)
if client != nil {
go func() {
if err = vs.StartWebhook(vc.port, createCertProvider(vc, client)); err != nil {
fatalf("Failed to start validator server: %v", err)
}
}()
}
if err = vs.StartHTTP(vc.httpPort); err != nil {
fatalf("Failed to start plain http server: %v", err)
}
}
52 changes: 42 additions & 10 deletions pkg/config/crd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package crd

import (
"crypto/tls"
"crypto/x509"
"fmt"
"net/http"
"net/url"
Expand Down Expand Up @@ -105,29 +106,60 @@ func Register(builders map[string]store.Store2Builder) {
builders["kubernetes"] = NewStore
}

// Start starts the validation server.
func (v *ValidatorServer) Start(port uint16, certProvider CertProvider) error {
// StartHTTP starts the validation server as a normal http server.
func (v *ValidatorServer) StartHTTP(port uint16) error {
server := &http.Server{
Addr: fmt.Sprintf(":%d", port),
Handler: v,
}
glog.Infof("HTTP server starting with port %d", port)
return server.ListenAndServe()
}

// retrieve the CA cert that will signed the cert used by the
// "GenericAdmissionWebhook" plugin admission controller.
func (v *ValidatorServer) getAPIServerCert() ([]byte, error) {
c, err := v.client.CoreV1().ConfigMaps("kube-system").Get("extension-apiserver-authentication", metav1.GetOptions{})
if err != nil {
return nil, err
}

pem, ok := c.Data["requestheader-client-ca-file"]
if !ok {
return nil, fmt.Errorf("cannot find the ca.crt in the configmap, configMap.Data is %#v", c.Data)
}
return []byte(pem), nil
}

// StartWebhook starts the validation server as an external admission hook.
func (v *ValidatorServer) StartWebhook(port uint16, certProvider CertProvider) error {
key, cert, caCert, err := certProvider.Get()
if err != nil {
return err
}
apiServerCert, err := v.getAPIServerCert()
if err != nil {
return err
}
apiserverCA := x509.NewCertPool()
apiserverCA.AppendCertsFromPEM(apiServerCert)
sCert, err := tls.X509KeyPair(cert, key)
if err != nil {
return err
}
// Unlike the webhook example code, this validation server does not take the kube-system's certificate and does not
// require client cert, since istioctl (which is not a kube-system client) will take the role of the validation
// if the external admission webhook isn't ready.
cfg := &tls.Config{Certificates: []tls.Certificate{sCert}}
server := &http.Server{
Addr: fmt.Sprintf(":%d", port),
Handler: v,
TLSConfig: cfg,
Addr: fmt.Sprintf(":%d", port),
Handler: v,
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{sCert},
ClientCAs: apiserverCA,
ClientAuth: tls.RequireAndVerifyClientCert,
},
}
// ensureRegistration can fail if external admission webhook is not yet ready.
if err = v.ensureRegistration(caCert); err != nil {
glog.V(3).Infof("Failed to register the validation server as the webhook: %v", err)
}
glog.Infof("server starting with port %d", port)
glog.Infof("External admission webhook starting with port %d", port)
return server.ListenAndServeTLS("", "")
}

0 comments on commit e7e16c9

Please sign in to comment.