Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 10 additions & 72 deletions cmd/webhook/main.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
package main

import (
"context"
"flag"
"net/http"
"os"
"os/signal"
"syscall"
"time"

"github.com/zalando/skipper/dataclients/kubernetes/admission"
"github.com/zalando/skipper"

"github.com/prometheus/client_golang/prometheus/promhttp"
log "github.com/sirupsen/logrus"
"github.com/zalando/skipper/dataclients/kubernetes/definitions"
)

const (
Expand All @@ -35,16 +28,6 @@ func (c *config) parse() {
flag.StringVar(&c.address, "address", defaultHTTPSAddress, "The address to listen on")
flag.Parse()

if (c.certFile != "" || c.keyFile != "") && !(c.certFile != "" && c.keyFile != "") {
log.Fatal("Config parse error: both of TLS cert & key must be provided or neither (for testing )")
return
}

// support non-HTTPS for local testing
if (c.certFile == "" && c.keyFile == "") && c.address == defaultHTTPSAddress {
c.address = defaultHTTPAddress
}

if c.debug {
log.SetLevel(log.DebugLevel)
}
Expand All @@ -54,62 +37,17 @@ func main() {
var cfg = &config{}
cfg.parse()

rgAdmitter := &admission.RouteGroupAdmitter{
RouteGroupValidator: &definitions.RouteGroupValidator{
EnableAdvancedValidation: false, // can't start advanced validation in the webhook binary
},
}
ingressAdmitter := &admission.IngressAdmitter{
IngressValidator: &definitions.IngressV1Validator{
EnableAdvancedValidation: false, // can't start advanced validation in the webhook binary
},
}
handler := http.NewServeMux()
handler.Handle("/routegroups", admission.Handler(rgAdmitter))
handler.Handle("/ingresses", admission.Handler(ingressAdmitter))
handler.Handle("/metrics", promhttp.Handler())
handler.HandleFunc("/healthz", healthCheck)

// One can use generate_cert.go in https://golang.org/pkg/crypto/tls
// to generate cert.pem and key.pem.
serve(cfg, handler)
}

func healthCheck(writer http.ResponseWriter, _ *http.Request) {
writer.WriteHeader(http.StatusOK)
if _, err := writer.Write([]byte("ok")); err != nil {
log.Errorf("Failed to write health check: %v", err)
}

}

func serve(cfg *config, handler http.Handler) {
server := &http.Server{
Addr: cfg.address,
Handler: handler,
ReadTimeout: 1 * time.Minute,
ReadHeaderTimeout: 1 * time.Minute,
skpOptions := skipper.Options{
ValidationWebhookEnabled: true,
ValidationWebhookCertFile: cfg.certFile,
ValidationWebhookKeyFile: cfg.keyFile,
ValidationWebhookAddress: cfg.address,
EnableAdvancedValidation: false,
}

log.Infof("Starting server on %s", cfg.address)

sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGTERM)
go func() {
<-sig
log.Info("Shutting down...")
server.Shutdown(context.Background())
}()

var err error
if cfg.certFile != "" && cfg.keyFile != "" {
err = server.ListenAndServeTLS(cfg.certFile, cfg.keyFile)
} else {
// support non-HTTPS for local testing
err = server.ListenAndServe()
if err := skipper.Run(skpOptions); err != nil {
log.Fatalf("Failed to start skipper binary in validation mode %v", err)
return
}

if err != nil && err != http.ErrServerClosed {
log.Fatalf("Failed to listen: %v", err)
}
}
3 changes: 2 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/zalando/skipper/otel"
"github.com/zalando/skipper/proxy"
"github.com/zalando/skipper/swarm"
"github.com/zalando/skipper/validation"
)

type Config struct {
Expand Down Expand Up @@ -543,7 +544,7 @@ func NewConfig() *Config {
flag.BoolVar(&cfg.Oauth2GrantInsecure, "oauth2-grant-insecure", false, "omits Secure attribute of the token cookie and uses http scheme for callback url")
flag.DurationVar(&cfg.WebhookTimeout, "webhook-timeout", 2*time.Second, "sets the webhook request timeout duration")
flag.BoolVar(&cfg.ValidationWebhookEnabled, "validation-webhook-enabled", false, "enables validation webhook for incoming requests")
flag.StringVar(&cfg.ValidationWebhookAddress, "validation-webhook-address", ":9000", "address of the validation webhook service")
flag.StringVar(&cfg.ValidationWebhookAddress, "validation-webhook-address", validation.DefaultHTTPSAddress, "address of the validation webhook service")
flag.StringVar(&cfg.ValidationWebhookCertFile, "validation-webhook-cert-file", "", "path to the certificate file for the validation webhook")
flag.StringVar(&cfg.ValidationWebhookKeyFile, "validation-webhook-key-file", "", "path to the key file for the validation webhook")
flag.BoolVar(&cfg.EnableAdvancedValidation, "enable-advanced-validation", false, "enables advanced validation logic for Kubernetes resources")
Expand Down
25 changes: 19 additions & 6 deletions validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,27 @@ import (
)

const (
readTimeout = time.Minute
readHeaderTimeout = time.Minute
readTimeout = time.Minute
readHeaderTimeout = time.Minute
DefaultHTTPSAddress = ":9443"
DefaultHTTPAddress = ":9080"
)

// StartValidation launches the validation webhook server and keeps serving until the
// returned listener encounters an unrecoverable error, or the process shuts down.
func StartValidation(address, certFile, keyFile string, enableAdvancedValidation bool, filterRegistry filters.Registry, predicateSpecs []routing.PredicateSpec, mtr metrics.Metrics) error {
if certFile == "" || keyFile == "" {
err := errors.New("validation webhook requires TLS: cert file or key file not provided")

if (certFile != "" || keyFile != "") && !(certFile != "" && keyFile != "") {
err := errors.New("config parse error: both of TLS cert & key must be provided or neither (for testing)")
log.Fatal(err)
return err
}

// support non-HTTPS for local testing
if (certFile == "" && keyFile == "") && address != DefaultHTTPAddress {
address = DefaultHTTPAddress
}

handler := newValidationHandler(enableAdvancedValidation, filterRegistry, predicateSpecs, mtr)

server := &http.Server{
Expand All @@ -53,8 +61,13 @@ func StartValidation(address, certFile, keyFile string, enableAdvancedValidation
return
}
}()

err := server.ListenAndServeTLS(certFile, keyFile)
var err error
if certFile != "" && keyFile != "" {
err = server.ListenAndServeTLS(certFile, keyFile)
} else {
// support non-HTTPS for local testing
err = server.ListenAndServe()
}

if err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("Failed to listen: %v", err)
Expand Down
18 changes: 0 additions & 18 deletions validation/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,6 @@ import (
"github.com/zalando/skipper/routing"
)

func TestStartValidationRequiresTLS(t *testing.T) {
patchLogrusExit(t)

err := StartValidation(":0", "", "", false, nil, nil, nil)
require.Error(t, err)
assert.Contains(t, err.Error(), "requires TLS")
}

func TestValidationHandlers(t *testing.T) {
testCases := []struct {
name string
Expand Down Expand Up @@ -271,16 +263,6 @@ func TestValidationHandlers(t *testing.T) {
}
}

func patchLogrusExit(t *testing.T) {
t.Helper()
logger := log.StandardLogger()
original := logger.ExitFunc
logger.ExitFunc = func(int) {}
t.Cleanup(func() {
logger.ExitFunc = original
})
}

func init() {
log.SetFormatter(&log.TextFormatter{DisableTimestamp: true})
log.SetLevel(log.WarnLevel)
Expand Down
Loading