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:
parent
5595c23eeb
commit
277402ba4c
5 changed files with 113 additions and 74 deletions
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
15
pkg/webhooks/middleware.go
Normal file
15
pkg/webhooks/middleware.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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{
|
||||||
|
|
Loading…
Add table
Reference in a new issue