1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00

Feature - Add checks for k8s version when Kyverno starts (#831)

* Added k8s version check for mutating and validating'

* version check adde

* middelware added

* formate

* Added timeout flag value to webhook server timeout middelware and refactore kubernetes version check

* Fixed test cases

* Removed log

* Update kubernetes version check

* Added check for mutate and validate

* Skip Validation in handleValidateAdmissionRequest if kubernetes version is below 1.14

* Update return object AdmissionResponse

* fixed condition for skiping mutation

* Handle condition for skip feature in case of kubernetes version 1.14.2
This commit is contained in:
Yuvraj 2020-05-18 17:00:52 -07:00 committed by GitHub
parent 5595c23eeb
commit 277402ba4c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 113 additions and 74 deletions

View file

@ -7,14 +7,13 @@ import (
"flag" "flag"
"fmt" "fmt"
"os" "os"
"regexp"
"strconv"
"sync" "sync"
"time" "time"
"github.com/nirmata/kyverno/pkg/config" "github.com/nirmata/kyverno/pkg/config"
client "github.com/nirmata/kyverno/pkg/dclient" client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/signal" "github.com/nirmata/kyverno/pkg/signal"
"github.com/nirmata/kyverno/pkg/utils"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
rest "k8s.io/client-go/rest" rest "k8s.io/client-go/rest"
clientcmd "k8s.io/client-go/tools/clientcmd" clientcmd "k8s.io/client-go/tools/clientcmd"
@ -63,7 +62,9 @@ func main() {
// Exit for unsupported version of kubernetes cluster // Exit for unsupported version of kubernetes cluster
// https://github.com/nirmata/kyverno/issues/700 // https://github.com/nirmata/kyverno/issues/700
// - supported from v1.12.7+ // - supported from v1.12.7+
isVersionSupported(client) if !utils.CompareKubernetesVersion(client, log.Log, 1, 12, 7) {
os.Exit(1)
}
requests := []request{ requests := []request{
// Resource // Resource
@ -222,39 +223,3 @@ func merge(done <-chan struct{}, stopCh <-chan struct{}, processes ...<-chan err
}() }()
return out return out
} }
func isVersionSupported(client *client.Client) {
logger := log.Log
serverVersion, err := client.DiscoveryClient.GetServerVersion()
if err != nil {
logger.Error(err, "Failed to get kubernetes server version")
os.Exit(1)
}
exp := regexp.MustCompile(`v(\d*).(\d*).(\d*)`)
groups := exp.FindAllStringSubmatch(serverVersion.String(), -1)
if len(groups) != 1 || len(groups[0]) != 4 {
logger.Error(err, "Failed to extract kubernetes server version", "serverVersion", serverVersion)
os.Exit(1)
}
// convert string to int
// assuming the version are always intergers
major, err := strconv.Atoi(groups[0][1])
if err != nil {
logger.Error(err, "Failed to extract kubernetes major server version", "serverVersion", serverVersion)
os.Exit(1)
}
minor, err := strconv.Atoi(groups[0][2])
if err != nil {
logger.Error(err, "Failed to extract kubernetes minor server version", "serverVersion", serverVersion)
os.Exit(1)
}
sub, err := strconv.Atoi(groups[0][3])
if err != nil {
logger.Error(err, "Failed to extract kubernetes sub minor server version", "serverVersion", serverVersion)
os.Exit(1)
}
if major <= 1 && minor <= 12 && sub < 7 {
logger.Info("Unsupported kubernetes server version %s. Kyverno is supported from version v1.12.7+", "serverVersion", serverVersion)
os.Exit(1)
}
}

View file

@ -2,6 +2,8 @@ package utils
import ( import (
"reflect" "reflect"
"regexp"
"strconv"
"github.com/go-logr/logr" "github.com/go-logr/logr"
"github.com/minio/minio/pkg/wildcard" "github.com/minio/minio/pkg/wildcard"
@ -86,3 +88,40 @@ func CleanupOldCrd(client *dclient.Client, log logr.Logger) {
} }
} }
} }
// CompareKubernetesVersion compare kuberneates client version to user given version
func CompareKubernetesVersion(client *client.Client, log logr.Logger, k8smajor, k8sminor, k8ssub int) bool {
logger := log.WithName("CompareKubernetesVersion")
serverVersion, err := client.DiscoveryClient.GetServerVersion()
if err != nil {
logger.Error(err, "Failed to get kubernetes server version")
return false
}
exp := regexp.MustCompile(`v(\d*).(\d*).(\d*)`)
groups := exp.FindAllStringSubmatch(serverVersion.String(), -1)
if len(groups) != 1 || len(groups[0]) != 4 {
logger.Error(err, "Failed to extract kubernetes server version", "serverVersion", serverVersion)
return false
}
// convert string to int
// assuming the version are always intergers
major, err := strconv.Atoi(groups[0][1])
if err != nil {
logger.Error(err, "Failed to extract kubernetes major server version", "serverVersion", serverVersion)
return false
}
minor, err := strconv.Atoi(groups[0][2])
if err != nil {
logger.Error(err, "Failed to extract kubernetes minor server version", "serverVersion", serverVersion)
return false
}
sub, err := strconv.Atoi(groups[0][3])
if err != nil {
logger.Error(err, "Failed to extract kubernetes sub minor server version", "serverVersion", serverVersion)
return false
}
if major <= k8smajor && minor <= k8sminor && sub < k8ssub {
return false
}
return true
}

View file

@ -338,3 +338,8 @@ func (wrc *WebhookRegistrationClient) removePolicyValidatingWebhookConfiguration
logger.V(4).Info("successfully deleted policy validating webhook configutation") logger.V(4).Info("successfully deleted policy validating webhook configutation")
} }
// GetWebhookTimeOut returns the value of webhook timeout
func (wrc *WebhookRegistrationClient) GetWebhookTimeOut() time.Duration {
return time.Duration(wrc.timeoutSeconds)
}

View file

@ -0,0 +1,15 @@
package webhooks
import (
"net/http"
"time"
)
func timeoutHandler(h http.Handler, timeout time.Duration) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var timeoutHandler http.Handler
msg := "ok"
timeoutHandler = http.TimeoutHandler(h, timeout*time.Second, msg)
timeoutHandler.ServeHTTP(w, r)
}
}

View file

@ -11,8 +11,8 @@ import (
"time" "time"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
"github.com/nirmata/kyverno/pkg/openapi" "github.com/nirmata/kyverno/pkg/openapi"
"github.com/nirmata/kyverno/pkg/utils"
"github.com/go-logr/logr" "github.com/go-logr/logr"
"github.com/nirmata/kyverno/pkg/checker" "github.com/nirmata/kyverno/pkg/checker"
@ -131,12 +131,14 @@ func NewWebhookServer(
log: log, log: log,
openAPIController: openAPIController, openAPIController: openAPIController,
} }
mux := httprouter.New() mux := httprouter.New()
mux.HandlerFunc("POST", config.MutatingWebhookServicePath, ws.handlerFunc(ws.handleMutateAdmissionRequest, true))
mux.HandlerFunc("POST", config.ValidatingWebhookServicePath, ws.handlerFunc(ws.handleValidateAdmissionRequest, true)) mux.HandlerFunc("POST", config.ValidatingWebhookServicePath, timeoutHandler(ws.handlerFunc(ws.handleValidateAdmissionRequest, true), ws.webhookRegistrationClient.GetWebhookTimeOut()))
mux.HandlerFunc("POST", config.PolicyMutatingWebhookServicePath, ws.handlerFunc(ws.handlePolicyMutation, true)) mux.HandlerFunc("POST", config.MutatingWebhookServicePath, timeoutHandler(ws.handlerFunc(ws.handleMutateAdmissionRequest, true), ws.webhookRegistrationClient.GetWebhookTimeOut()))
mux.HandlerFunc("POST", config.PolicyValidatingWebhookServicePath, ws.handlerFunc(ws.handlePolicyValidation, true)) mux.HandlerFunc("POST", config.PolicyValidatingWebhookServicePath, timeoutHandler(ws.handlerFunc(ws.handlePolicyValidation, true), ws.webhookRegistrationClient.GetWebhookTimeOut()))
mux.HandlerFunc("POST", config.VerifyMutatingWebhookServicePath, ws.handlerFunc(ws.handleVerifyRequest, false)) mux.HandlerFunc("POST", config.PolicyMutatingWebhookServicePath, timeoutHandler(ws.handlerFunc(ws.handlePolicyMutation, true), ws.webhookRegistrationClient.GetWebhookTimeOut()))
mux.HandlerFunc("POST", config.VerifyMutatingWebhookServicePath, timeoutHandler(ws.handlerFunc(ws.handleVerifyRequest, false), ws.webhookRegistrationClient.GetWebhookTimeOut()))
ws.server = http.Server{ ws.server = http.Server{
Addr: ":443", // Listen on port for HTTPS requests Addr: ":443", // Listen on port for HTTPS requests
TLSConfig: &tlsConfig, TLSConfig: &tlsConfig,
@ -238,29 +240,37 @@ func (ws *WebhookServer) handleMutateAdmissionRequest(request *v1beta1.Admission
} }
} }
// MUTATION var patches, patchedResource []byte
// mutation failure should not block the resource creation
// any mutation failure is reported as the violation
patches := ws.HandleMutation(request, resource, policies, roles, clusterRoles)
// patch the resource with patches before handling validation rules versionCheck := utils.CompareKubernetesVersion(ws.client, ws.log, 1, 14, 0)
patchedResource := processResourceWithPatches(patches, request.Object.Raw, logger)
if ws.resourceWebhookWatcher != nil && ws.resourceWebhookWatcher.RunValidationInMutatingWebhook == "true" { if versionCheck {
// VALIDATION // MUTATION
ok, msg := ws.HandleValidation(request, policies, patchedResource, roles, clusterRoles) // mutation failure should not block the resource creation
if !ok { // any mutation failure is reported as the violation
logger.Info("admission request denied") patches = ws.HandleMutation(request, resource, policies, roles, clusterRoles)
return &v1beta1.AdmissionResponse{
Allowed: false, // patch the resource with patches before handling validation rules
Result: &metav1.Status{ patchedResource := processResourceWithPatches(patches, request.Object.Raw, logger)
Status: "Failure",
Message: msg, if ws.resourceWebhookWatcher != nil && ws.resourceWebhookWatcher.RunValidationInMutatingWebhook == "true" {
}, // VALIDATION
ok, msg := ws.HandleValidation(request, policies, patchedResource, roles, clusterRoles)
if !ok {
logger.Info("admission request denied")
return &v1beta1.AdmissionResponse{
Allowed: false,
Result: &metav1.Status{
Status: "Failure",
Message: msg,
},
}
} }
} }
} else {
// patch the resource with patches before handling validation rules
patchedResource = processResourceWithPatches(patches, request.Object.Raw, logger)
} }
// GENERATE // GENERATE
// Only applied during resource creation // Only applied during resource creation
// Success -> Generate Request CR created successsfully // Success -> Generate Request CR created successsfully
@ -278,6 +288,7 @@ func (ws *WebhookServer) handleMutateAdmissionRequest(request *v1beta1.Admission
} }
} }
} }
// Succesfful processing of mutation & validation rules in policy // Succesfful processing of mutation & validation rules in policy
patchType := v1beta1.PatchTypeJSONPatch patchType := v1beta1.PatchTypeJSONPatch
return &v1beta1.AdmissionResponse{ return &v1beta1.AdmissionResponse{
@ -288,6 +299,7 @@ func (ws *WebhookServer) handleMutateAdmissionRequest(request *v1beta1.Admission
Patch: patches, Patch: patches,
PatchType: &patchType, PatchType: &patchType,
} }
} }
func (ws *WebhookServer) handleValidateAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { func (ws *WebhookServer) handleValidateAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
@ -332,19 +344,22 @@ func (ws *WebhookServer) handleValidateAdmissionRequest(request *v1beta1.Admissi
} }
} }
// VALIDATION versionCheck := utils.CompareKubernetesVersion(ws.client, ws.log, 1, 14, 0)
ok, msg := ws.HandleValidation(request, policies, nil, roles, clusterRoles)
if !ok { if !versionCheck {
logger.Info("admission request denied") // VALIDATION
return &v1beta1.AdmissionResponse{ ok, msg := ws.HandleValidation(request, policies, nil, roles, clusterRoles)
Allowed: false, if !ok {
Result: &metav1.Status{ logger.Info("admission request denied")
Status: "Failure", return &v1beta1.AdmissionResponse{
Message: msg, Allowed: false,
}, Result: &metav1.Status{
Status: "Failure",
Message: msg,
},
}
} }
} }
return &v1beta1.AdmissionResponse{ return &v1beta1.AdmissionResponse{
Allowed: true, Allowed: true,
Result: &metav1.Status{ Result: &metav1.Status{