1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00
kyverno/pkg/webhooks/server.go

155 lines
5.5 KiB
Go
Raw Normal View History

2019-05-13 21:33:01 +03:00
package webhooks
import (
"context"
"crypto/tls"
"fmt"
"net/http"
"sync"
"time"
2020-03-17 11:05:20 -07:00
"github.com/go-logr/logr"
2020-04-27 18:38:03 +05:30
"github.com/julienschmidt/httprouter"
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/toggle"
"github.com/kyverno/kyverno/pkg/utils"
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
"github.com/kyverno/kyverno/pkg/webhookconfig"
"github.com/kyverno/kyverno/pkg/webhooks/handlers"
admissionv1 "k8s.io/api/admission/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
type Server interface {
// Run TLS server in separate thread and returns control immediately
Run(<-chan struct{})
// Stop TLS server and returns control after the server is shut down
Stop(context.Context)
}
type Handlers interface {
// Mutate performs the mutation of policy resources
Mutate(logr.Logger, *admissionv1.AdmissionRequest) *admissionv1.AdmissionResponse
// Validate performs the validation check on policy resources
Validate(logr.Logger, *admissionv1.AdmissionRequest) *admissionv1.AdmissionResponse
}
type server struct {
server *http.Server
webhookRegister *webhookconfig.Register
cleanUp chan<- struct{}
}
type TlsProvider func() ([]byte, []byte, error)
// NewServer creates new instance of server accordingly to given configuration
func NewServer(
policyHandlers Handlers,
resourceHandlers Handlers,
tlsProvider TlsProvider,
configuration config.Configuration,
register *webhookconfig.Register,
monitor *webhookconfig.Monitor,
2020-03-17 11:05:20 -07:00
cleanUp chan<- struct{},
) Server {
2020-04-27 18:38:03 +05:30
mux := httprouter.New()
resourceLogger := logger.WithName("resource")
policyLogger := logger.WithName("policy")
verifyLogger := logger.WithName("verify")
mux.HandlerFunc("POST", config.MutatingWebhookServicePath, admission(resourceLogger.WithName("mutate"), monitor, filter(configuration, resourceHandlers.Mutate)))
mux.HandlerFunc("POST", config.ValidatingWebhookServicePath, admission(resourceLogger.WithName("validate"), monitor, filter(configuration, resourceHandlers.Validate)))
mux.HandlerFunc("POST", config.PolicyMutatingWebhookServicePath, admission(policyLogger.WithName("mutate"), monitor, filter(configuration, policyHandlers.Mutate)))
mux.HandlerFunc("POST", config.PolicyValidatingWebhookServicePath, admission(policyLogger.WithName("validate"), monitor, filter(configuration, policyHandlers.Validate)))
mux.HandlerFunc("POST", config.VerifyMutatingWebhookServicePath, admission(verifyLogger.WithName("mutate"), monitor, handlers.Verify(monitor)))
mux.HandlerFunc("GET", config.LivenessServicePath, handlers.Probe(register.Check))
mux.HandlerFunc("GET", config.ReadinessServicePath, handlers.Probe(nil))
return &server{
server: &http.Server{
Addr: ":9443",
TLSConfig: &tls.Config{
GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
certPem, keyPem, err := tlsProvider()
if err != nil {
return nil, err
}
pair, err := tls.X509KeyPair(certPem, keyPem)
if err != nil {
return nil, err
}
return &pair, nil
},
MinVersion: tls.VersionTLS12,
},
Handler: mux,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
ReadHeaderTimeout: 30 * time.Second,
},
webhookRegister: register,
cleanUp: cleanUp,
}
}
func (s *server) Run(stopCh <-chan struct{}) {
go func() {
logger.V(3).Info("started serving requests", "addr", s.server.Addr)
if err := s.server.ListenAndServeTLS("", ""); err != http.ErrServerClosed {
logger.Error(err, "failed to listen to requests")
}
}()
logger.Info("starting service")
Feature/cosign (#2078) * add image verification * inline policy list Signed-off-by: Jim Bugwadia <jim@nirmata.com> * cosign version and dependencies updates Signed-off-by: Jim Bugwadia <jim@nirmata.com> * add registry initialization Signed-off-by: Jim Bugwadia <jim@nirmata.com> * add build tag to exclude k8schain for cloud providers Signed-off-by: Jim Bugwadia <jim@nirmata.com> * add build tag to exclude k8schain for cloud providers Signed-off-by: Jim Bugwadia <jim@nirmata.com> * generate deep copy and other fixtures Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix deep copy issues Signed-off-by: Jim Bugwadia <jim@nirmata.com> * mutate images to add digest Signed-off-by: Jim Bugwadia <jim@nirmata.com> * add certificates to Kyverno container for HTTPS lookups Signed-off-by: Jim Bugwadia <jim@nirmata.com> * align flag syntax Signed-off-by: Jim Bugwadia <jim@nirmata.com> * update docs Signed-off-by: Jim Bugwadia <jim@nirmata.com> * update dependencies Signed-off-by: Jim Bugwadia <jim@nirmata.com> * update dependencies Signed-off-by: Jim Bugwadia <jim@nirmata.com> * patch image with digest and fix checks Signed-off-by: Jim Bugwadia <jim@nirmata.com> * hardcode image for demos Signed-off-by: Jim Bugwadia <jim@nirmata.com> * add default registry (docker.io) before calling reference.Parse Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix definition Signed-off-by: Jim Bugwadia <jim@nirmata.com> * increase webhook timeout Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix args Signed-off-by: Jim Bugwadia <jim@nirmata.com> * run gofmt Signed-off-by: Jim Bugwadia <jim@nirmata.com> * rename for clarity Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix HasImageVerify check Signed-off-by: Jim Bugwadia <jim@nirmata.com> * align make test commands Signed-off-by: Jim Bugwadia <jim@nirmata.com> * align make test commands Signed-off-by: Jim Bugwadia <jim@nirmata.com> * align make test commands Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix linter error Signed-off-by: Jim Bugwadia <jim@nirmata.com> * format Signed-off-by: Jim Bugwadia <jim@nirmata.com> * handle API conflict and retry Signed-off-by: Jim Bugwadia <jim@nirmata.com> * format Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix reviewdog issues Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix make for unit tests Signed-off-by: Jim Bugwadia <jim@nirmata.com> * improve error message Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix durations Signed-off-by: Jim Bugwadia <jim@nirmata.com> * handle errors in tests Signed-off-by: Jim Bugwadia <jim@nirmata.com> * print policy name Signed-off-by: Jim Bugwadia <jim@nirmata.com> * update tests Signed-off-by: Jim Bugwadia <jim@nirmata.com> * add retries and duration to error log Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix time check in tests Signed-off-by: Jim Bugwadia <jim@nirmata.com> * round creation times in test Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix retry loop Signed-off-by: Jim Bugwadia <jim@nirmata.com> * remove timing check for policy creation Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix e2e error - policy not found Signed-off-by: Shuting Zhao <shutting06@gmail.com> * update string comparison method Signed-off-by: Shuting Zhao <shutting06@gmail.com> * fix test Generate_Namespace_Label_Actions Signed-off-by: Shuting Zhao <shutting06@gmail.com> * add debug info for e2e tests Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix error Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix generate bug Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix format Signed-off-by: Jim Bugwadia <jim@nirmata.com> * add check for update operations Signed-off-by: Jim Bugwadia <jim@nirmata.com> * increase time for deleteing a resource Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix check Signed-off-by: Jim Bugwadia <jim@nirmata.com> Co-authored-by: Shuting Zhao <shutting06@gmail.com>
2021-07-09 18:01:46 -07:00
}
func (s *server) Stop(ctx context.Context) {
s.cleanup(ctx)
err := s.server.Shutdown(ctx)
if err != nil {
logger.Error(err, "shutting down server")
err = s.server.Close()
if err != nil {
logger.Error(err, "server shut down failed")
}
}
}
func (s *server) cleanup(ctx context.Context) {
cleanupKyvernoResource := s.webhookRegister.ShouldCleanupKyvernoResource()
var wg sync.WaitGroup
wg.Add(2)
go s.webhookRegister.Remove(cleanupKyvernoResource, &wg)
go s.webhookRegister.ResetPolicyStatus(cleanupKyvernoResource, &wg)
wg.Wait()
close(s.cleanUp)
}
func protect(inner handlers.AdmissionHandler) handlers.AdmissionHandler {
return func(logger logr.Logger, request *admissionv1.AdmissionRequest) *admissionv1.AdmissionResponse {
if toggle.ProtectManagedResources.Enabled() {
newResource, oldResource, err := utils.ExtractResources(nil, request)
if err != nil {
logger.Error(err, "Failed to extract resources")
return admissionutils.ResponseFailure(err.Error())
}
for _, resource := range []unstructured.Unstructured{newResource, oldResource} {
resLabels := resource.GetLabels()
if resLabels["app.kubernetes.io/managed-by"] == "kyverno" {
if request.UserInfo.Username != fmt.Sprintf("system:serviceaccount:%s:%s", config.KyvernoNamespace(), config.KyvernoServiceAccountName()) {
logger.Info("Access to the resource not authorized, this is a kyverno managed resource and should be altered only by kyverno")
return admissionutils.ResponseFailure("A kyverno managed resource can only be modified by kyverno")
}
}
}
}
return inner(logger, request)
}
}
func filter(configuration config.Configuration, inner handlers.AdmissionHandler) handlers.AdmissionHandler {
return handlers.Filter(configuration, inner)
}
func admission(logger logr.Logger, monitor *webhookconfig.Monitor, inner handlers.AdmissionHandler) http.HandlerFunc {
return handlers.Monitor(monitor, handlers.Admission(logger, protect(inner)))
}