mirror of
https://github.com/kyverno/kyverno.git
synced 2025-04-08 10:04:25 +00:00
Merge pull request #860 from realshuting/744_deny_requests
[1.2.0] 744 deny requests
This commit is contained in:
commit
9536a0df22
29 changed files with 599 additions and 338 deletions
2
Makefile
2
Makefile
|
@ -8,7 +8,7 @@ GIT_BRANCH := $(shell git branch | grep \* | cut -d ' ' -f2)
|
|||
GIT_HASH := $(GIT_BRANCH)/$(shell git log -1 --pretty=format:"%H")
|
||||
TIMESTAMP := $(shell date '+%Y-%m-%d_%I:%M:%S%p')
|
||||
|
||||
REGISTRY=index.docker.io
|
||||
REGISTRY?=index.docker.io
|
||||
REPO=$(REGISTRY)/nirmata/kyverno
|
||||
IMAGE_TAG?=$(GIT_VERSION)
|
||||
GOOS ?= $(shell go env GOOS)
|
||||
|
|
|
@ -62,7 +62,7 @@ func main() {
|
|||
// Exit for unsupported version of kubernetes cluster
|
||||
// https://github.com/nirmata/kyverno/issues/700
|
||||
// - supported from v1.12.7+
|
||||
if !utils.CompareKubernetesVersion(client, log.Log, 1, 12, 7) {
|
||||
if !utils.HigherThanKubernetesVersion(client, log.Log, 1, 12, 7) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ spec:
|
|||
type: string
|
||||
name:
|
||||
type: string
|
||||
Namespace:
|
||||
namespace:
|
||||
type: string
|
||||
resources:
|
||||
type: object
|
||||
|
@ -133,7 +133,7 @@ spec:
|
|||
type: string
|
||||
name:
|
||||
type: string
|
||||
Namespace:
|
||||
namespace:
|
||||
type: string
|
||||
resources:
|
||||
type: object
|
||||
|
@ -210,6 +210,28 @@ spec:
|
|||
AnyValue: {}
|
||||
anyPattern:
|
||||
AnyValue: {}
|
||||
deny:
|
||||
properties:
|
||||
conditions:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- key # can be of any type
|
||||
- operator # typed
|
||||
- value # can be of any type
|
||||
properties:
|
||||
operator:
|
||||
type: string
|
||||
enum:
|
||||
- Equal
|
||||
- Equals
|
||||
- NotEqual
|
||||
- NotEquals
|
||||
key:
|
||||
type: string
|
||||
value:
|
||||
type: string
|
||||
generate:
|
||||
type: object
|
||||
required:
|
||||
|
|
|
@ -133,7 +133,7 @@ spec:
|
|||
type: string
|
||||
name:
|
||||
type: string
|
||||
Namespace:
|
||||
namespace:
|
||||
type: string
|
||||
resources:
|
||||
type: object
|
||||
|
@ -210,6 +210,28 @@ spec:
|
|||
AnyValue: {}
|
||||
anyPattern:
|
||||
AnyValue: {}
|
||||
deny:
|
||||
properties:
|
||||
conditions:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- key # can be of any type
|
||||
- operator # typed
|
||||
- value # can be of any type
|
||||
properties:
|
||||
operator:
|
||||
type: string
|
||||
enum:
|
||||
- Equal
|
||||
- Equals
|
||||
- NotEqual
|
||||
- NotEquals
|
||||
key:
|
||||
type: string
|
||||
value:
|
||||
type: string
|
||||
generate:
|
||||
type: object
|
||||
required:
|
||||
|
|
|
@ -155,9 +155,11 @@ type ConditionOperator string
|
|||
|
||||
const (
|
||||
//Equal for Equal operator
|
||||
Equal ConditionOperator = "Equal"
|
||||
Equal ConditionOperator = "Equal"
|
||||
Equals ConditionOperator = "Equals"
|
||||
//NotEqual for NotEqual operator
|
||||
NotEqual ConditionOperator = "NotEqual"
|
||||
NotEqual ConditionOperator = "NotEqual"
|
||||
NotEquals ConditionOperator = "NotEquals"
|
||||
//In for In operator
|
||||
In ConditionOperator = "In"
|
||||
//NotIn for NotIn operator
|
||||
|
@ -211,6 +213,11 @@ type Validation struct {
|
|||
Message string `json:"message,omitempty"`
|
||||
Pattern interface{} `json:"pattern,omitempty"`
|
||||
AnyPattern []interface{} `json:"anyPattern,omitempty"`
|
||||
Deny *Deny `json:"deny,omitempty"`
|
||||
}
|
||||
|
||||
type Deny struct {
|
||||
Conditions []Condition `json:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
// Generation describes which resources will be created when other resource is created
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"k8s.io/api/admission/v1beta1"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/go-logr/logr"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||
|
@ -63,6 +65,21 @@ func (ctx *Context) AddJSON(dataRaw []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ctx *Context) AddRequest(request *v1beta1.AdmissionRequest) error {
|
||||
modifiedResource := struct {
|
||||
Request interface{} `json:"request"`
|
||||
}{
|
||||
Request: request,
|
||||
}
|
||||
|
||||
objRaw, err := json.Marshal(modifiedResource)
|
||||
if err != nil {
|
||||
ctx.log.Error(err, "failed to marshal the request")
|
||||
return err
|
||||
}
|
||||
return ctx.AddJSON(objRaw)
|
||||
}
|
||||
|
||||
//AddResource data at path: request.object
|
||||
func (ctx *Context) AddResource(dataRaw []byte) error {
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package context
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
jmespath "github.com/jmespath/go-jmespath"
|
||||
)
|
||||
|
@ -11,7 +12,7 @@ import (
|
|||
func (ctx *Context) Query(query string) (interface{}, error) {
|
||||
var emptyResult interface{}
|
||||
// check for white-listed variables
|
||||
if ctx.isWhiteListed(query) {
|
||||
if !ctx.isWhiteListed(query) {
|
||||
return emptyResult, fmt.Errorf("variable %s cannot be used", query)
|
||||
}
|
||||
|
||||
|
@ -40,8 +41,11 @@ func (ctx *Context) Query(query string) (interface{}, error) {
|
|||
}
|
||||
|
||||
func (ctx *Context) isWhiteListed(variable string) bool {
|
||||
if len(ctx.whiteListVars) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, wVar := range ctx.whiteListVars {
|
||||
if wVar == variable {
|
||||
if strings.HasPrefix(variable, wVar) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,7 +159,7 @@ func Test_variableSubstitutionPathNotExist(t *testing.T) {
|
|||
Context: ctx,
|
||||
NewResource: *resourceUnstructured}
|
||||
er := Mutate(policyContext)
|
||||
expectedErrorStr := "[failed to resolve request.object.metadata.name1 at path /spec/name]"
|
||||
expectedErrorStr := "could not find variable request.object.metadata.name1 at path /spec/name"
|
||||
t.Log(er.PolicyResponse.Rules[0].Message)
|
||||
assert.Equal(t, er.PolicyResponse.Rules[0].Message, expectedErrorStr)
|
||||
}
|
||||
|
|
|
@ -114,6 +114,14 @@ func matchSubjects(ruleSubjects []rbacv1.Subject, userInfo authenticationv1.User
|
|||
const SaPrefix = "system:serviceaccount:"
|
||||
|
||||
userGroups := append(userInfo.Groups, userInfo.Username)
|
||||
|
||||
// TODO: see issue https://github.com/nirmata/kyverno/issues/861
|
||||
ruleSubjects = append(ruleSubjects,
|
||||
rbacv1.Subject{Kind: "Group", Name: "system:serviceaccounts:kube-system"},
|
||||
rbacv1.Subject{Kind: "Group", Name: "system:nodes"},
|
||||
rbacv1.Subject{Kind: "Group", Name: "system:kube-scheduler"},
|
||||
)
|
||||
|
||||
for _, subject := range ruleSubjects {
|
||||
switch subject.Kind {
|
||||
case "ServiceAccount":
|
||||
|
@ -163,7 +171,8 @@ func MatchesResourceDescription(resourceRef unstructured.Unstructured, ruleRef k
|
|||
}
|
||||
|
||||
// checking if resource matches the rule
|
||||
if !reflect.DeepEqual(rule.MatchResources.ResourceDescription, kyverno.ResourceDescription{}) {
|
||||
if !reflect.DeepEqual(rule.MatchResources.ResourceDescription, kyverno.ResourceDescription{}) ||
|
||||
!reflect.DeepEqual(rule.MatchResources.UserInfo, kyverno.UserInfo{}) {
|
||||
matchErrs := doesResourceMatchConditionBlock(rule.MatchResources.ResourceDescription, rule.MatchResources.UserInfo, admissionInfo, resource)
|
||||
reasonsForFailure = append(reasonsForFailure, matchErrs...)
|
||||
} else {
|
||||
|
@ -171,7 +180,8 @@ func MatchesResourceDescription(resourceRef unstructured.Unstructured, ruleRef k
|
|||
}
|
||||
|
||||
// checking if resource has been excluded
|
||||
if !reflect.DeepEqual(rule.ExcludeResources.ResourceDescription, kyverno.ResourceDescription{}) {
|
||||
if !reflect.DeepEqual(rule.ExcludeResources.ResourceDescription, kyverno.ResourceDescription{}) ||
|
||||
!reflect.DeepEqual(rule.ExcludeResources.UserInfo, kyverno.UserInfo{}) {
|
||||
excludeErrs := doesResourceMatchConditionBlock(rule.ExcludeResources.ResourceDescription, rule.ExcludeResources.UserInfo, admissionInfo, resource)
|
||||
if excludeErrs == nil {
|
||||
reasonsForFailure = append(reasonsForFailure, fmt.Errorf("resource has been excluded since it matches the exclude block"))
|
||||
|
|
|
@ -25,42 +25,51 @@ func Validate(policyContext PolicyContext) (resp response.EngineResponse) {
|
|||
ctx := policyContext.Context
|
||||
admissionInfo := policyContext.AdmissionInfo
|
||||
logger := log.Log.WithName("Validate").WithValues("policy", policy.Name, "kind", newR.GetKind(), "namespace", newR.GetNamespace(), "name", newR.GetName())
|
||||
|
||||
// policy information
|
||||
logger.V(4).Info("start processing", "startTime", startTime)
|
||||
|
||||
// Process new & old resource
|
||||
if reflect.DeepEqual(oldR, unstructured.Unstructured{}) {
|
||||
// Create Mode
|
||||
// Operate on New Resource only
|
||||
resp := validateResource(logger, ctx, policy, newR, admissionInfo)
|
||||
startResultResponse(resp, policy, newR)
|
||||
defer endResultResponse(logger, resp, startTime)
|
||||
// set PatchedResource with origin resource if empty
|
||||
// in order to create policy violation
|
||||
if reflect.DeepEqual(resp.PatchedResource, unstructured.Unstructured{}) {
|
||||
resp.PatchedResource = newR
|
||||
defer func() {
|
||||
if reflect.DeepEqual(resp, response.EngineResponse{}) {
|
||||
return
|
||||
}
|
||||
var resource unstructured.Unstructured
|
||||
if reflect.DeepEqual(resp.PatchedResource, unstructured.Unstructured{}) {
|
||||
// for delete requests patched resource will be oldR since newR is empty
|
||||
if reflect.DeepEqual(newR, unstructured.Unstructured{}) {
|
||||
resource = oldR
|
||||
} else {
|
||||
resource = newR
|
||||
}
|
||||
}
|
||||
for i := range resp.PolicyResponse.Rules {
|
||||
messageInterface, err := variables.SubstituteVars(logger, ctx, resp.PolicyResponse.Rules[i].Message)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
resp.PolicyResponse.Rules[i].Message, _ = messageInterface.(string)
|
||||
}
|
||||
resp.PatchedResource = resource
|
||||
startResultResponse(&resp, policy, resource)
|
||||
endResultResponse(logger, &resp, startTime)
|
||||
}()
|
||||
|
||||
// If request is delete, newR will be empty
|
||||
if reflect.DeepEqual(newR, unstructured.Unstructured{}) {
|
||||
return *isRequestDenied(logger, ctx, policy, oldR, admissionInfo)
|
||||
} else {
|
||||
if denyResp := isRequestDenied(logger, ctx, policy, newR, admissionInfo); !denyResp.IsSuccesful() {
|
||||
return *denyResp
|
||||
}
|
||||
return *resp
|
||||
}
|
||||
// Update Mode
|
||||
// Operate on New and Old Resource only
|
||||
// New resource
|
||||
|
||||
if reflect.DeepEqual(oldR, unstructured.Unstructured{}) {
|
||||
return *validateResource(logger, ctx, policy, newR, admissionInfo)
|
||||
}
|
||||
|
||||
oldResponse := validateResource(logger, ctx, policy, oldR, admissionInfo)
|
||||
newResponse := validateResource(logger, ctx, policy, newR, admissionInfo)
|
||||
|
||||
// if the old and new response is same then return empty response
|
||||
if !isSameResponse(oldResponse, newResponse) {
|
||||
// there are changes send response
|
||||
startResultResponse(newResponse, policy, newR)
|
||||
defer endResultResponse(logger, newResponse, startTime)
|
||||
if reflect.DeepEqual(newResponse.PatchedResource, unstructured.Unstructured{}) {
|
||||
newResponse.PatchedResource = newR
|
||||
}
|
||||
return *newResponse
|
||||
}
|
||||
// if there are no changes with old and new response then sent empty response
|
||||
// skip processing
|
||||
return response.EngineResponse{}
|
||||
}
|
||||
|
||||
|
@ -85,6 +94,44 @@ func incrementAppliedCount(resp *response.EngineResponse) {
|
|||
resp.PolicyResponse.RulesAppliedCount++
|
||||
}
|
||||
|
||||
func isRequestDenied(log logr.Logger, ctx context.EvalInterface, policy kyverno.ClusterPolicy, resource unstructured.Unstructured, admissionInfo kyverno.RequestInfo) *response.EngineResponse {
|
||||
resp := &response.EngineResponse{}
|
||||
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if !rule.HasValidate() {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := MatchesResourceDescription(resource, rule, admissionInfo); err != nil {
|
||||
log.V(4).Info("resource fails the match description")
|
||||
continue
|
||||
}
|
||||
|
||||
preconditionsCopy := copyConditions(rule.Conditions)
|
||||
|
||||
if !variables.EvaluateConditions(log, ctx, preconditionsCopy) {
|
||||
log.V(4).Info("resource fails the preconditions")
|
||||
continue
|
||||
}
|
||||
|
||||
if rule.Validation.Deny != nil {
|
||||
denyConditionsCopy := copyConditions(rule.Validation.Deny.Conditions)
|
||||
if len(rule.Validation.Deny.Conditions) == 0 || variables.EvaluateConditions(log, ctx, denyConditionsCopy) {
|
||||
ruleResp := response.RuleResponse{
|
||||
Name: rule.Name,
|
||||
Type: utils.Validation.String(),
|
||||
Message: rule.Validation.Message,
|
||||
Success: false,
|
||||
}
|
||||
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, ruleResp)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func validateResource(log logr.Logger, ctx context.EvalInterface, policy kyverno.ClusterPolicy, resource unstructured.Unstructured, admissionInfo kyverno.RequestInfo) *response.EngineResponse {
|
||||
resp := &response.EngineResponse{}
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
|
@ -96,15 +143,15 @@ func validateResource(log logr.Logger, ctx context.EvalInterface, policy kyverno
|
|||
// TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
|
||||
// dont statisfy a policy rule resource description
|
||||
if err := MatchesResourceDescription(resource, rule, admissionInfo); err != nil {
|
||||
log.V(4).Info("resource fails the match description")
|
||||
log.V(4).Info("resource fails the match description", "reason", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
// operate on the copy of the conditions, as we perform variable substitution
|
||||
copyConditions := copyConditions(rule.Conditions)
|
||||
preconditionsCopy := copyConditions(rule.Conditions)
|
||||
// evaluate pre-conditions
|
||||
// - handle variable subsitutions
|
||||
if !variables.EvaluateConditions(log, ctx, copyConditions) {
|
||||
if !variables.EvaluateConditions(log, ctx, preconditionsCopy) {
|
||||
log.V(4).Info("resource fails the preconditions")
|
||||
continue
|
||||
}
|
||||
|
@ -114,6 +161,7 @@ func validateResource(log logr.Logger, ctx context.EvalInterface, policy kyverno
|
|||
incrementAppliedCount(resp)
|
||||
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, ruleResponse)
|
||||
}
|
||||
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -26,6 +26,10 @@ func CreateOperatorHandler(log logr.Logger, ctx context.EvalInterface, op kyvern
|
|||
return NewEqualHandler(log, ctx, subHandler)
|
||||
case kyverno.NotEqual:
|
||||
return NewNotEqualHandler(log, ctx, subHandler)
|
||||
case kyverno.Equals:
|
||||
return NewEqualHandler(log, ctx, subHandler)
|
||||
case kyverno.NotEquals:
|
||||
return NewNotEqualHandler(log, ctx, subHandler)
|
||||
default:
|
||||
log.Info("operator not supported", "operator", string(op))
|
||||
}
|
||||
|
|
|
@ -11,23 +11,20 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
variableRegex = `\{\{([^{}]*)\}\}`
|
||||
singleVarRegex = `^\{\{([^{}]*)\}\}$`
|
||||
variableRegex = `\{\{([^{}]*)\}\}`
|
||||
)
|
||||
|
||||
//SubstituteVars replaces the variables with the values defined in the context
|
||||
// - if any variable is invaid or has nil value, it is considered as a failed varable substitution
|
||||
func SubstituteVars(log logr.Logger, ctx context.EvalInterface, pattern interface{}) (interface{}, error) {
|
||||
errs := []error{}
|
||||
pattern = subVars(log, ctx, pattern, "", &errs)
|
||||
if len(errs) == 0 {
|
||||
// no error while parsing the pattern
|
||||
return pattern, nil
|
||||
pattern, err := subVars(log, ctx, pattern, "")
|
||||
if err != nil {
|
||||
return pattern, err
|
||||
}
|
||||
return pattern, fmt.Errorf("%v", errs)
|
||||
return pattern, nil
|
||||
}
|
||||
|
||||
func subVars(log logr.Logger, ctx context.EvalInterface, pattern interface{}, path string, errs *[]error) interface{} {
|
||||
func subVars(log logr.Logger, ctx context.EvalInterface, pattern interface{}, path string) (interface{}, error) {
|
||||
switch typedPattern := pattern.(type) {
|
||||
case map[string]interface{}:
|
||||
mapCopy := make(map[string]interface{})
|
||||
|
@ -35,141 +32,85 @@ func subVars(log logr.Logger, ctx context.EvalInterface, pattern interface{}, pa
|
|||
mapCopy[k] = v
|
||||
}
|
||||
|
||||
return subMap(log, ctx, mapCopy, path, errs)
|
||||
return subMap(log, ctx, mapCopy, path)
|
||||
case []interface{}:
|
||||
sliceCopy := make([]interface{}, len(typedPattern))
|
||||
copy(sliceCopy, typedPattern)
|
||||
|
||||
return subArray(log, ctx, sliceCopy, path, errs)
|
||||
return subArray(log, ctx, sliceCopy, path)
|
||||
case string:
|
||||
return subValR(log, ctx, typedPattern, path, errs)
|
||||
return subValR(ctx, typedPattern, path)
|
||||
default:
|
||||
return pattern
|
||||
return pattern, nil
|
||||
}
|
||||
}
|
||||
|
||||
func subMap(log logr.Logger, ctx context.EvalInterface, patternMap map[string]interface{}, path string, errs *[]error) map[string]interface{} {
|
||||
func subMap(log logr.Logger, ctx context.EvalInterface, patternMap map[string]interface{}, path string) (map[string]interface{}, error) {
|
||||
for key, patternElement := range patternMap {
|
||||
curPath := path + "/" + key
|
||||
value := subVars(log, ctx, patternElement, curPath, errs)
|
||||
value, err := subVars(log, ctx, patternElement, curPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
patternMap[key] = value
|
||||
|
||||
}
|
||||
return patternMap
|
||||
return patternMap, nil
|
||||
}
|
||||
|
||||
func subArray(log logr.Logger, ctx context.EvalInterface, patternList []interface{}, path string, errs *[]error) []interface{} {
|
||||
func subArray(log logr.Logger, ctx context.EvalInterface, patternList []interface{}, path string) ([]interface{}, error) {
|
||||
for idx, patternElement := range patternList {
|
||||
curPath := path + "/" + strconv.Itoa(idx)
|
||||
value := subVars(log, ctx, patternElement, curPath, errs)
|
||||
value, err := subVars(log, ctx, patternElement, curPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
patternList[idx] = value
|
||||
}
|
||||
return patternList
|
||||
return patternList, nil
|
||||
}
|
||||
|
||||
type NotFoundVariableErr struct {
|
||||
variable string
|
||||
path string
|
||||
}
|
||||
|
||||
func (n NotFoundVariableErr) Error() string {
|
||||
return fmt.Sprintf("could not find variable %v at path %v", n.variable, n.path)
|
||||
}
|
||||
|
||||
// subValR resolves the variables if defined
|
||||
func subValR(log logr.Logger, ctx context.EvalInterface, valuePattern string, path string, errs *[]error) interface{} {
|
||||
func subValR(ctx context.EvalInterface, valuePattern string, path string) (interface{}, error) {
|
||||
originalPattern := valuePattern
|
||||
|
||||
// variable values can be scalar values(string,int, float) or they can be obects(map,slice)
|
||||
// - {{variable}}
|
||||
// there is a single variable resolution so the value can be scalar or object
|
||||
// - {{variable1--{{variable2}}}}}
|
||||
// variable2 is evaluted first as an individual variable and can be have scalar or object values
|
||||
// but resolving the outer variable, {{variable--<value>}}
|
||||
// if <value> is scalar then it can replaced, but for object types its tricky
|
||||
// as object cannot be directy replaced, if the object is stringyfied then it loses it structure.
|
||||
// since this might be a potential place for error, required better error reporting and handling
|
||||
|
||||
// object values are only suported for single variable substitution
|
||||
if ok, retVal := processIfSingleVariable(log, ctx, valuePattern, path, errs); ok {
|
||||
return retVal
|
||||
}
|
||||
// var emptyInterface interface{}
|
||||
var failedVars []string
|
||||
// process type string
|
||||
regex := regexp.MustCompile(`\{\{([^{}]*)\}\}`)
|
||||
for {
|
||||
valueStr := valuePattern
|
||||
if len(failedVars) != 0 {
|
||||
log.Info("failed to resolve variablesl short-circuiting")
|
||||
if vars := regex.FindAllString(valuePattern, -1); len(vars) > 0 {
|
||||
for _, variable := range vars {
|
||||
underlyingVariable := strings.ReplaceAll(strings.ReplaceAll(variable, "}}", ""), "{{", "")
|
||||
substitutedVar, err := ctx.Query(underlyingVariable)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to resolve %v at path %s", underlyingVariable, path)
|
||||
}
|
||||
if val, ok := substitutedVar.(string); ok {
|
||||
valuePattern = strings.Replace(valuePattern, variable, val, -1)
|
||||
} else {
|
||||
if substitutedVar != nil {
|
||||
if originalPattern == variable {
|
||||
return substitutedVar, nil
|
||||
}
|
||||
return nil, fmt.Errorf("failed to resolve %v at path %s", underlyingVariable, path)
|
||||
}
|
||||
return nil, NotFoundVariableErr{
|
||||
variable: underlyingVariable,
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
// get variables at this level
|
||||
validRegex := regexp.MustCompile(variableRegex)
|
||||
groups := validRegex.FindAllStringSubmatch(valueStr, -1)
|
||||
if len(groups) == 0 {
|
||||
// there was no match
|
||||
// not variable defined
|
||||
break
|
||||
}
|
||||
subs := map[string]interface{}{}
|
||||
for _, group := range groups {
|
||||
if _, ok := subs[group[0]]; ok {
|
||||
// value has already been substituted
|
||||
continue
|
||||
}
|
||||
// here we do the querying of the variables from the context
|
||||
variable, err := ctx.Query(group[1])
|
||||
if err != nil {
|
||||
// error while evaluating
|
||||
failedVars = append(failedVars, group[1])
|
||||
continue
|
||||
}
|
||||
// path not found in context and value stored in null/nill
|
||||
if variable == nil {
|
||||
failedVars = append(failedVars, group[1])
|
||||
continue
|
||||
}
|
||||
// get values for each and replace
|
||||
subs[group[0]] = variable
|
||||
}
|
||||
// perform substitutions
|
||||
newVal := valueStr
|
||||
for k, v := range subs {
|
||||
// if value is of type string then cast else consider it as direct replacement
|
||||
if val, ok := v.(string); ok {
|
||||
newVal = strings.Replace(newVal, k, val, -1)
|
||||
continue
|
||||
}
|
||||
// if type is not scalar then consider this as a failed variable
|
||||
log.Info("variable resolves to non-scalar value. Non-Scalar values are not supported for nested variables", "variable", k, "value", v)
|
||||
failedVars = append(failedVars, k)
|
||||
}
|
||||
valuePattern = newVal
|
||||
}
|
||||
// update errors if any
|
||||
if len(failedVars) > 0 {
|
||||
*errs = append(*errs, fmt.Errorf("failed to resolve %v at path %s", failedVars, path))
|
||||
}
|
||||
|
||||
return valuePattern
|
||||
}
|
||||
|
||||
// processIfSingleVariable will process the evaluation of single variables
|
||||
// {{variable-{{variable}}}} -> compound/nested variables
|
||||
// {{variable}}{{variable}} -> multiple variables
|
||||
// {{variable}} -> single variable
|
||||
// if the value can be evaluted return the value
|
||||
// -> return value can be scalar or object type
|
||||
// -> if the variable is not present in the context then add an error and dont process further
|
||||
func processIfSingleVariable(log logr.Logger, ctx context.EvalInterface, valuePattern interface{}, path string, errs *[]error) (bool, interface{}) {
|
||||
valueStr, ok := valuePattern.(string)
|
||||
if !ok {
|
||||
log.Info("failed to convert to string", "pattern", valuePattern)
|
||||
return false, nil
|
||||
}
|
||||
// get variables at this level
|
||||
validRegex := regexp.MustCompile(singleVarRegex)
|
||||
groups := validRegex.FindAllStringSubmatch(valueStr, -1)
|
||||
if len(groups) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
// as there will be exactly one variable based on the above regex
|
||||
group := groups[0]
|
||||
variable, err := ctx.Query(group[1])
|
||||
if err != nil || variable == nil {
|
||||
*errs = append(*errs, fmt.Errorf("failed to resolve %v at path %s", group[1], path))
|
||||
// return the same value pattern, and add un-resolvable variable error
|
||||
return true, valuePattern
|
||||
}
|
||||
return true, variable
|
||||
return valuePattern, nil
|
||||
}
|
||||
|
|
|
@ -152,6 +152,5 @@ func Test_SubvarRecursive(t *testing.T) {
|
|||
|
||||
ctx := context.NewContext()
|
||||
assert.Assert(t, ctx.AddResource(resourceRaw))
|
||||
errs := []error{}
|
||||
subValR(log.Log, ctx, string(patternRaw), "/", &errs)
|
||||
subValR(ctx, string(patternRaw), "/")
|
||||
}
|
||||
|
|
|
@ -9,57 +9,76 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
//ContainsUserInfo returns error is userInfo is defined
|
||||
func ContainsUserInfo(policy kyverno.ClusterPolicy) error {
|
||||
//ContainsVariablesOtherThanObject returns error if variable that does not start from request.object
|
||||
func ContainsVariablesOtherThanObject(policy kyverno.ClusterPolicy) error {
|
||||
var err error
|
||||
// iterate of the policy rules to identify if userInfo is used
|
||||
for idx, rule := range policy.Spec.Rules {
|
||||
if path := userInfoDefined(rule.MatchResources.UserInfo); path != "" {
|
||||
return fmt.Errorf("userInfo variable used at path: spec/rules[%d]/match/%s", idx, path)
|
||||
return fmt.Errorf("invalid variable used at path: spec/rules[%d]/match/%s", idx, path)
|
||||
}
|
||||
|
||||
if path := userInfoDefined(rule.ExcludeResources.UserInfo); path != "" {
|
||||
return fmt.Errorf("userInfo variable used at path: spec/rules[%d]/exclude/%s", idx, path)
|
||||
return fmt.Errorf("invalid variable used at path: spec/rules[%d]/exclude/%s", idx, path)
|
||||
}
|
||||
|
||||
// variable defined with user information
|
||||
// - condition.key
|
||||
// - condition.value
|
||||
// - mutate.overlay
|
||||
// - validate.pattern
|
||||
// - validate.anyPattern[*]
|
||||
// variables to filter
|
||||
// - request.userInfo*
|
||||
// - serviceAccountName
|
||||
// - serviceAccountNamespace
|
||||
|
||||
filterVars := []string{"request.userInfo*", "serviceAccountName", "serviceAccountNamespace"}
|
||||
filterVars := []string{"request.object"}
|
||||
ctx := context.NewContext(filterVars...)
|
||||
for condIdx, condition := range rule.Conditions {
|
||||
if condition.Key, err = variables.SubstituteVars(log.Log, ctx, condition.Key); err != nil {
|
||||
return fmt.Errorf("userInfo variable used at spec/rules[%d]/condition[%d]/key", idx, condIdx)
|
||||
if condition.Key, err = variables.SubstituteVars(log.Log, ctx, condition.Key); !checkNotFoundErr(err) {
|
||||
return fmt.Errorf("invalid variable used at spec/rules[%d]/condition[%d]/key", idx, condIdx)
|
||||
}
|
||||
|
||||
if condition.Value, err = variables.SubstituteVars(log.Log, ctx, condition.Value); err != nil {
|
||||
return fmt.Errorf("userInfo variable used at spec/rules[%d]/condition[%d]/value", idx, condIdx)
|
||||
if condition.Value, err = variables.SubstituteVars(log.Log, ctx, condition.Value); !checkNotFoundErr(err) {
|
||||
return fmt.Errorf("invalid variable used at spec/rules[%d]/condition[%d]/value", idx, condIdx)
|
||||
}
|
||||
}
|
||||
|
||||
if rule.Mutation.Overlay, err = variables.SubstituteVars(log.Log, ctx, rule.Mutation.Overlay); err != nil {
|
||||
return fmt.Errorf("userInfo variable used at spec/rules[%d]/mutate/overlay", idx)
|
||||
if rule.Mutation.Overlay != nil {
|
||||
if rule.Mutation.Overlay, err = variables.SubstituteVars(log.Log, ctx, rule.Mutation.Overlay); !checkNotFoundErr(err) {
|
||||
return fmt.Errorf("invalid variable used at spec/rules[%d]/mutate/overlay", idx)
|
||||
}
|
||||
}
|
||||
if rule.Validation.Pattern, err = variables.SubstituteVars(log.Log, ctx, rule.Validation.Pattern); err != nil {
|
||||
return fmt.Errorf("userInfo variable used at spec/rules[%d]/validate/pattern", idx)
|
||||
|
||||
if rule.Validation.Pattern != nil {
|
||||
if rule.Validation.Pattern, err = variables.SubstituteVars(log.Log, ctx, rule.Validation.Pattern); !checkNotFoundErr(err) {
|
||||
return fmt.Errorf("invalid variable used at spec/rules[%d]/validate/pattern", idx)
|
||||
}
|
||||
}
|
||||
for idx2, pattern := range rule.Validation.AnyPattern {
|
||||
if rule.Validation.AnyPattern[idx2], err = variables.SubstituteVars(log.Log, ctx, pattern); err != nil {
|
||||
return fmt.Errorf("userInfo variable used at spec/rules[%d]/validate/anyPattern[%d]", idx, idx2)
|
||||
if rule.Validation.AnyPattern[idx2], err = variables.SubstituteVars(log.Log, ctx, pattern); !checkNotFoundErr(err) {
|
||||
return fmt.Errorf("invalid variable used at spec/rules[%d]/validate/anyPattern[%d]", idx, idx2)
|
||||
}
|
||||
}
|
||||
if _, err = variables.SubstituteVars(log.Log, ctx, rule.Validation.Message); !checkNotFoundErr(err) {
|
||||
return fmt.Errorf("invalid variable used at spec/rules[%d]/validate/message", idx)
|
||||
}
|
||||
if rule.Validation.Deny != nil {
|
||||
for i := range rule.Validation.Deny.Conditions {
|
||||
if _, err = variables.SubstituteVars(log.Log, ctx, rule.Validation.Deny.Conditions[i].Key); !checkNotFoundErr(err) {
|
||||
return fmt.Errorf("invalid variable used at spec/rules[%d]/validate/deny/conditions[%d]/key", idx, i)
|
||||
}
|
||||
if _, err = variables.SubstituteVars(log.Log, ctx, rule.Validation.Deny.Conditions[i].Value); !checkNotFoundErr(err) {
|
||||
return fmt.Errorf("invalid variable used at spec/rules[%d]/validate/deny/conditions[%d]/value", idx, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkNotFoundErr(err error) bool {
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case variables.NotFoundVariableErr:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func userInfoDefined(ui kyverno.UserInfo) string {
|
||||
if len(ui.Roles) > 0 {
|
||||
return "roles"
|
||||
|
|
|
@ -158,7 +158,7 @@ func (pc *PolicyController) addPolicy(obj interface{}) {
|
|||
// TODO: code might seem vague, awaiting resolution of issue https://github.com/nirmata/kyverno/issues/598
|
||||
if p.Spec.Background == nil {
|
||||
// if userInfo is not defined in policy we process the policy
|
||||
if err := ContainsUserInfo(*p); err != nil {
|
||||
if err := ContainsVariablesOtherThanObject(*p); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
@ -167,7 +167,7 @@ func (pc *PolicyController) addPolicy(obj interface{}) {
|
|||
}
|
||||
// If userInfo is used then skip the policy
|
||||
// ideally this should be handled by background flag only
|
||||
if err := ContainsUserInfo(*p); err != nil {
|
||||
if err := ContainsVariablesOtherThanObject(*p); err != nil {
|
||||
// contains userInfo used in policy
|
||||
return
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ func (pc *PolicyController) updatePolicy(old, cur interface{}) {
|
|||
// TODO: code might seem vague, awaiting resolution of issue https://github.com/nirmata/kyverno/issues/598
|
||||
if curP.Spec.Background == nil {
|
||||
// if userInfo is not defined in policy we process the policy
|
||||
if err := ContainsUserInfo(*curP); err != nil {
|
||||
if err := ContainsVariablesOtherThanObject(*curP); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
@ -202,7 +202,7 @@ func (pc *PolicyController) updatePolicy(old, cur interface{}) {
|
|||
}
|
||||
// If userInfo is used then skip the policy
|
||||
// ideally this should be handled by background flag only
|
||||
if err := ContainsUserInfo(*curP); err != nil {
|
||||
if err := ContainsVariablesOtherThanObject(*curP); err != nil {
|
||||
// contains userInfo used in policy
|
||||
return
|
||||
}
|
||||
|
|
|
@ -30,16 +30,9 @@ func Validate(policyRaw []byte, client *dclient.Client, mock bool, openAPIContro
|
|||
if path, err := validateUniqueRuleName(p); err != nil {
|
||||
return fmt.Errorf("path: spec.%s: %v", path, err)
|
||||
}
|
||||
if p.Spec.Background == nil {
|
||||
//skipped policy mutation default -> skip validation -> will not be processed for background processing
|
||||
return nil
|
||||
}
|
||||
if *p.Spec.Background {
|
||||
if err := ContainsUserInfo(p); err != nil {
|
||||
// policy.spec.background -> "true"
|
||||
// - cannot use variables with request.userInfo
|
||||
// - cannot define userInfo(roles, cluserRoles, subjects) for filtering (match & exclude)
|
||||
return fmt.Errorf("userInfo is not allowed in match or exclude when backgroud policy mode is true. Set spec.background=false to disable background mode for this policy rule. %s ", err)
|
||||
if p.Spec.Background == nil || (p.Spec.Background != nil && *p.Spec.Background) {
|
||||
if err := ContainsVariablesOtherThanObject(p); err != nil {
|
||||
return fmt.Errorf("only variables referring request.object are allowed in background mode. Set spec.background=false to disable background mode for this policy rule. %s ", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,8 +49,8 @@ func (v *Validate) Validate() (string, error) {
|
|||
// validateOverlayPattern checks one of pattern/anyPattern must exist
|
||||
func (v *Validate) validateOverlayPattern() error {
|
||||
rule := v.rule
|
||||
if rule.Pattern == nil && len(rule.AnyPattern) == 0 {
|
||||
return fmt.Errorf("a pattern or anyPattern must be specified")
|
||||
if rule.Pattern == nil && len(rule.AnyPattern) == 0 && rule.Deny == nil {
|
||||
return fmt.Errorf("pattern, anyPattern or deny must be specified")
|
||||
}
|
||||
|
||||
if rule.Pattern != nil && len(rule.AnyPattern) != 0 {
|
||||
|
|
|
@ -608,8 +608,8 @@ func Test_BackGroundUserInfo_match_roles(t *testing.T) {
|
|||
err = json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = ContainsUserInfo(*policy)
|
||||
assert.Equal(t, err.Error(), "userInfo variable used at path: spec/rules[0]/match/roles")
|
||||
err = ContainsVariablesOtherThanObject(*policy)
|
||||
assert.Equal(t, err.Error(), "invalid variable used at path: spec/rules[0]/match/roles")
|
||||
}
|
||||
|
||||
func Test_BackGroundUserInfo_match_clusterRoles(t *testing.T) {
|
||||
|
@ -640,9 +640,9 @@ func Test_BackGroundUserInfo_match_clusterRoles(t *testing.T) {
|
|||
err = json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = ContainsUserInfo(*policy)
|
||||
err = ContainsVariablesOtherThanObject(*policy)
|
||||
|
||||
assert.Equal(t, err.Error(), "userInfo variable used at path: spec/rules[0]/match/clusterRoles")
|
||||
assert.Equal(t, err.Error(), "invalid variable used at path: spec/rules[0]/match/clusterRoles")
|
||||
}
|
||||
|
||||
func Test_BackGroundUserInfo_match_subjects(t *testing.T) {
|
||||
|
@ -676,9 +676,9 @@ func Test_BackGroundUserInfo_match_subjects(t *testing.T) {
|
|||
err = json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = ContainsUserInfo(*policy)
|
||||
err = ContainsVariablesOtherThanObject(*policy)
|
||||
|
||||
assert.Equal(t, err.Error(), "userInfo variable used at path: spec/rules[0]/match/subjects")
|
||||
assert.Equal(t, err.Error(), "invalid variable used at path: spec/rules[0]/match/subjects")
|
||||
}
|
||||
|
||||
func Test_BackGroundUserInfo_mutate_overlay1(t *testing.T) {
|
||||
|
@ -708,9 +708,9 @@ func Test_BackGroundUserInfo_mutate_overlay1(t *testing.T) {
|
|||
err = json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = ContainsUserInfo(*policy)
|
||||
err = ContainsVariablesOtherThanObject(*policy)
|
||||
|
||||
if err.Error() != "userInfo variable used at spec/rules[0]/mutate/overlay" {
|
||||
if err.Error() != "invalid variable used at spec/rules[0]/mutate/overlay" {
|
||||
t.Log(err)
|
||||
t.Error("Incorrect Path")
|
||||
}
|
||||
|
@ -743,9 +743,9 @@ func Test_BackGroundUserInfo_mutate_overlay2(t *testing.T) {
|
|||
err = json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = ContainsUserInfo(*policy)
|
||||
err = ContainsVariablesOtherThanObject(*policy)
|
||||
|
||||
if err.Error() != "userInfo variable used at spec/rules[0]/mutate/overlay" {
|
||||
if err.Error() != "invalid variable used at spec/rules[0]/mutate/overlay" {
|
||||
t.Log(err)
|
||||
t.Error("Incorrect Path")
|
||||
}
|
||||
|
@ -778,9 +778,9 @@ func Test_BackGroundUserInfo_validate_pattern(t *testing.T) {
|
|||
err = json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = ContainsUserInfo(*policy)
|
||||
err = ContainsVariablesOtherThanObject(*policy)
|
||||
|
||||
if err.Error() != "userInfo variable used at spec/rules[0]/validate/pattern" {
|
||||
if err.Error() != "invalid variable used at spec/rules[0]/validate/pattern" {
|
||||
t.Log(err)
|
||||
t.Error("Incorrect Path")
|
||||
}
|
||||
|
@ -817,9 +817,9 @@ func Test_BackGroundUserInfo_validate_anyPattern(t *testing.T) {
|
|||
err = json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = ContainsUserInfo(*policy)
|
||||
err = ContainsVariablesOtherThanObject(*policy)
|
||||
|
||||
if err.Error() != "userInfo variable used at spec/rules[0]/validate/anyPattern[1]" {
|
||||
if err.Error() != "invalid variable used at spec/rules[0]/validate/anyPattern[1]" {
|
||||
t.Log(err)
|
||||
t.Error("Incorrect Path")
|
||||
}
|
||||
|
@ -856,9 +856,9 @@ func Test_BackGroundUserInfo_validate_anyPattern_multiple_var(t *testing.T) {
|
|||
err = json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = ContainsUserInfo(*policy)
|
||||
err = ContainsVariablesOtherThanObject(*policy)
|
||||
|
||||
if err.Error() != "userInfo variable used at spec/rules[0]/validate/anyPattern[1]" {
|
||||
if err.Error() != "invalid variable used at spec/rules[0]/validate/anyPattern[1]" {
|
||||
t.Log(err)
|
||||
t.Error("Incorrect Path")
|
||||
}
|
||||
|
@ -895,9 +895,9 @@ func Test_BackGroundUserInfo_validate_anyPattern_serviceAccount(t *testing.T) {
|
|||
err = json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = ContainsUserInfo(*policy)
|
||||
err = ContainsVariablesOtherThanObject(*policy)
|
||||
|
||||
if err.Error() != "userInfo variable used at spec/rules[0]/validate/anyPattern[1]" {
|
||||
if err.Error() != "invalid variable used at spec/rules[0]/validate/anyPattern[1]" {
|
||||
t.Log(err)
|
||||
t.Error("Incorrect Path")
|
||||
}
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
engineutils "github.com/nirmata/kyverno/pkg/engine/utils"
|
||||
"k8s.io/api/admission/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/minio/minio/pkg/wildcard"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
|
@ -89,8 +94,51 @@ 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 {
|
||||
// extracts the new and old resource as unstructured
|
||||
func ExtractResources(newRaw []byte, request *v1beta1.AdmissionRequest) (unstructured.Unstructured, unstructured.Unstructured, error) {
|
||||
var emptyResource unstructured.Unstructured
|
||||
var newResource unstructured.Unstructured
|
||||
var oldResource unstructured.Unstructured
|
||||
var err error
|
||||
|
||||
// New Resource
|
||||
if newRaw == nil {
|
||||
newRaw = request.Object.Raw
|
||||
}
|
||||
|
||||
if newRaw != nil {
|
||||
newResource, err = ConvertResource(newRaw, request.Kind.Group, request.Kind.Version, request.Kind.Kind, request.Namespace)
|
||||
if err != nil {
|
||||
return emptyResource, emptyResource, fmt.Errorf("failed to convert new raw to unstructured: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Old Resource
|
||||
oldRaw := request.OldObject.Raw
|
||||
if oldRaw != nil {
|
||||
oldResource, err = ConvertResource(oldRaw, request.Kind.Group, request.Kind.Version, request.Kind.Kind, request.Namespace)
|
||||
if err != nil {
|
||||
return emptyResource, emptyResource, fmt.Errorf("failed to convert old raw to unstructured: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return newResource, oldResource, err
|
||||
}
|
||||
|
||||
// convertResource converts raw bytes to an unstructured object
|
||||
func ConvertResource(raw []byte, group, version, kind, namespace string) (unstructured.Unstructured, error) {
|
||||
obj, err := engineutils.ConvertToUnstructured(raw)
|
||||
if err != nil {
|
||||
return unstructured.Unstructured{}, fmt.Errorf("failed to convert raw to unstructured: %v", err)
|
||||
}
|
||||
|
||||
obj.SetGroupVersionKind(schema.GroupVersionKind{Group: group, Version: version, Kind: kind})
|
||||
obj.SetNamespace(namespace)
|
||||
return *obj, nil
|
||||
}
|
||||
|
||||
// HigherThanKubernetesVersion compare kuberneates client version to user given version
|
||||
func HigherThanKubernetesVersion(client *client.Client, log logr.Logger, k8smajor, k8sminor, k8ssub int) bool {
|
||||
logger := log.WithName("CompareKubernetesVersion")
|
||||
serverVersion, err := client.DiscoveryClient.GetServerVersion()
|
||||
if err != nil {
|
||||
|
|
|
@ -102,7 +102,7 @@ func (wrc *WebhookRegistrationClient) constructDebugValidatingWebhookConfig(caDa
|
|||
"*/*",
|
||||
"*",
|
||||
"*",
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update},
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update, admregapi.Delete},
|
||||
),
|
||||
},
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ func (wrc *WebhookRegistrationClient) constructValidatingWebhookConfig(caData []
|
|||
"*/*",
|
||||
"*",
|
||||
"*",
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update},
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update, admregapi.Delete},
|
||||
),
|
||||
},
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"k8s.io/api/admission/v1beta1"
|
||||
)
|
||||
|
||||
func (ws *WebhookServer) handleVerifyRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
func (ws *WebhookServer) verifyHandler(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
logger := ws.log.WithValues("action", "verify", "uid", request.UID, "kind", request.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
|
||||
logger.V(4).Info("incoming request")
|
||||
return &v1beta1.AdmissionResponse{
|
||||
|
|
|
@ -5,10 +5,10 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
yamlv2 "gopkg.in/yaml.v2"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||
"github.com/nirmata/kyverno/pkg/engine/response"
|
||||
engineutils "github.com/nirmata/kyverno/pkg/engine/utils"
|
||||
yamlv2 "gopkg.in/yaml.v2"
|
||||
"k8s.io/api/admission/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
@ -167,3 +167,13 @@ func convertResource(raw []byte, group, version, kind, namespace string) (unstru
|
|||
obj.SetNamespace(namespace)
|
||||
return *obj, nil
|
||||
}
|
||||
|
||||
func excludeKyvernoResources(kind string) bool {
|
||||
switch kind {
|
||||
case "ClusterPolicy", "ClusterPolicyViolation", "PolicyViolation", "GenerateRequest":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
)
|
||||
|
||||
//HandleGenerate handles admission-requests for policies with generate rules
|
||||
func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, policies []kyverno.ClusterPolicy, patchedResource []byte, roles, clusterRoles []string) (bool, string) {
|
||||
func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, policies []kyverno.ClusterPolicy, ctx *context.Context, userRequestInfo kyverno.RequestInfo) (bool, string) {
|
||||
logger := ws.log.WithValues("action", "generation", "uid", request.UID, "kind", request.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
|
||||
logger.V(4).Info("incoming request")
|
||||
var engineResponses []response.EngineResponse
|
||||
|
@ -31,27 +31,6 @@ func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, polic
|
|||
|
||||
// CREATE resources, do not have name, assigned in admission-request
|
||||
|
||||
userRequestInfo := kyverno.RequestInfo{
|
||||
Roles: roles,
|
||||
ClusterRoles: clusterRoles,
|
||||
AdmissionUserInfo: request.UserInfo}
|
||||
// build context
|
||||
ctx := context.NewContext()
|
||||
// load incoming resource into the context
|
||||
err = ctx.AddResource(request.Object.Raw)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to load incoming resource in context")
|
||||
}
|
||||
err = ctx.AddUserInfo(userRequestInfo)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to load userInfo in context")
|
||||
}
|
||||
// load service account in context
|
||||
err = ctx.AddSA(userRequestInfo.AdmissionUserInfo.Username)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to load service account in context")
|
||||
}
|
||||
|
||||
policyContext := engine.PolicyContext{
|
||||
NewResource: *resource,
|
||||
AdmissionInfo: userRequestInfo,
|
||||
|
|
|
@ -21,7 +21,9 @@ import (
|
|||
func (ws *WebhookServer) HandleMutation(
|
||||
request *v1beta1.AdmissionRequest,
|
||||
resource unstructured.Unstructured,
|
||||
policies []kyverno.ClusterPolicy, roles, clusterRoles []string) []byte {
|
||||
policies []kyverno.ClusterPolicy,
|
||||
ctx *context.Context,
|
||||
userRequestInfo kyverno.RequestInfo) []byte {
|
||||
|
||||
resourceName := request.Kind.Kind + "/" + request.Name
|
||||
if request.Namespace != "" {
|
||||
|
@ -32,30 +34,6 @@ func (ws *WebhookServer) HandleMutation(
|
|||
|
||||
var patches [][]byte
|
||||
var engineResponses []response.EngineResponse
|
||||
|
||||
userRequestInfo := kyverno.RequestInfo{
|
||||
Roles: roles,
|
||||
ClusterRoles: clusterRoles,
|
||||
AdmissionUserInfo: request.UserInfo}
|
||||
|
||||
// build context
|
||||
ctx := context.NewContext()
|
||||
var err error
|
||||
// load incoming resource into the context
|
||||
err = ctx.AddResource(request.Object.Raw)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to load incoming resource in context")
|
||||
}
|
||||
|
||||
err = ctx.AddUserInfo(userRequestInfo)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to load userInfo in context")
|
||||
}
|
||||
err = ctx.AddSA(userRequestInfo.AdmissionUserInfo.Username)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to load service account in context")
|
||||
}
|
||||
|
||||
policyContext := engine.PolicyContext{
|
||||
NewResource: resource,
|
||||
AdmissionInfo: userRequestInfo,
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (ws *WebhookServer) handlePolicyMutation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
func (ws *WebhookServer) policyMutation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
logger := ws.log.WithValues("action", "policymutation", "uid", request.UID, "kind", request.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
|
||||
var policy *kyverno.ClusterPolicy
|
||||
raw := request.Object.Raw
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
//HandlePolicyValidation performs the validation check on policy resource
|
||||
func (ws *WebhookServer) handlePolicyValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
func (ws *WebhookServer) policyValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
//TODO: can this happen? wont this be picked by OpenAPI spec schema ?
|
||||
if err := policyvalidate.Validate(request.Object.Raw, ws.client, false, ws.openAPIController); err != nil {
|
||||
return &v1beta1.AdmissionResponse{
|
||||
|
|
|
@ -10,23 +10,24 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/nirmata/kyverno/pkg/openapi"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||
"github.com/nirmata/kyverno/pkg/checker"
|
||||
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
|
||||
kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1"
|
||||
kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1"
|
||||
"github.com/nirmata/kyverno/pkg/config"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
context2 "github.com/nirmata/kyverno/pkg/engine/context"
|
||||
"github.com/nirmata/kyverno/pkg/event"
|
||||
"github.com/nirmata/kyverno/pkg/openapi"
|
||||
"github.com/nirmata/kyverno/pkg/policystatus"
|
||||
"github.com/nirmata/kyverno/pkg/policystore"
|
||||
"github.com/nirmata/kyverno/pkg/policyviolation"
|
||||
tlsutils "github.com/nirmata/kyverno/pkg/tls"
|
||||
userinfo "github.com/nirmata/kyverno/pkg/userinfo"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
"github.com/nirmata/kyverno/pkg/webhookconfig"
|
||||
"github.com/nirmata/kyverno/pkg/webhooks/generate"
|
||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||
|
@ -133,12 +134,13 @@ func NewWebhookServer(
|
|||
}
|
||||
|
||||
mux := httprouter.New()
|
||||
webhookTimeout := ws.webhookRegistrationClient.GetWebhookTimeOut()
|
||||
mux.HandlerFunc("POST", config.MutatingWebhookServicePath, timeoutHandler(ws.handlerFunc(ws.resourceMutation, true), webhookTimeout))
|
||||
mux.HandlerFunc("POST", config.ValidatingWebhookServicePath, timeoutHandler(ws.handlerFunc(ws.resourceValidation, true), webhookTimeout))
|
||||
mux.HandlerFunc("POST", config.PolicyMutatingWebhookServicePath, timeoutHandler(ws.handlerFunc(ws.policyMutation, true), webhookTimeout))
|
||||
mux.HandlerFunc("POST", config.PolicyValidatingWebhookServicePath, timeoutHandler(ws.handlerFunc(ws.policyValidation, true), webhookTimeout))
|
||||
mux.HandlerFunc("POST", config.VerifyMutatingWebhookServicePath, timeoutHandler(ws.handlerFunc(ws.verifyHandler, false), webhookTimeout))
|
||||
|
||||
mux.HandlerFunc("POST", config.ValidatingWebhookServicePath, timeoutHandler(ws.handlerFunc(ws.handleValidateAdmissionRequest, true), ws.webhookRegistrationClient.GetWebhookTimeOut()))
|
||||
mux.HandlerFunc("POST", config.MutatingWebhookServicePath, timeoutHandler(ws.handlerFunc(ws.handleMutateAdmissionRequest, true), ws.webhookRegistrationClient.GetWebhookTimeOut()))
|
||||
mux.HandlerFunc("POST", config.PolicyValidatingWebhookServicePath, timeoutHandler(ws.handlerFunc(ws.handlePolicyValidation, true), ws.webhookRegistrationClient.GetWebhookTimeOut()))
|
||||
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{
|
||||
Addr: ":443", // Listen on port for HTTPS requests
|
||||
TLSConfig: &tlsConfig,
|
||||
|
@ -162,9 +164,6 @@ func (ws *WebhookServer) handlerFunc(handler func(request *v1beta1.AdmissionRequ
|
|||
}
|
||||
|
||||
logger := ws.log.WithValues("kind", admissionReview.Request.Kind, "namespace", admissionReview.Request.Namespace, "name", admissionReview.Request.Name)
|
||||
defer func() {
|
||||
logger.V(4).Info("request processed", "processingTime", time.Since(startTime))
|
||||
}()
|
||||
|
||||
admissionReview.Response = &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
|
@ -180,6 +179,8 @@ func (ws *WebhookServer) handlerFunc(handler func(request *v1beta1.AdmissionRequ
|
|||
|
||||
admissionReview.Response = handler(request)
|
||||
writeResponse(rw, admissionReview)
|
||||
logger.V(4).Info("request processed", "processingTime", time.Since(startTime).Milliseconds())
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -197,8 +198,17 @@ func writeResponse(rw http.ResponseWriter, admissionReview *v1beta1.AdmissionRev
|
|||
}
|
||||
}
|
||||
|
||||
func (ws *WebhookServer) handleMutateAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
logger := ws.log.WithName("handleMutateAdmissionRequest").WithValues("uid", request.UID, "kind", request.Kind.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
|
||||
func (ws *WebhookServer) resourceMutation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
if excludeKyvernoResources(request.Kind.Kind) {
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
Result: &metav1.Status{
|
||||
Status: "Success",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
logger := ws.log.WithName("resourceMutation").WithValues("uid", request.UID, "kind", request.Kind.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
|
||||
policies, err := ws.pMetaStore.ListAll()
|
||||
if err != nil {
|
||||
// Unable to connect to policy Lister to access policies
|
||||
|
@ -206,9 +216,8 @@ func (ws *WebhookServer) handleMutateAdmissionRequest(request *v1beta1.Admission
|
|||
return &v1beta1.AdmissionResponse{Allowed: true}
|
||||
}
|
||||
|
||||
var roles, clusterRoles []string
|
||||
|
||||
// getRoleRef only if policy has roles/clusterroles defined
|
||||
var roles, clusterRoles []string
|
||||
if containRBACinfo(policies) {
|
||||
roles, clusterRoles, err = userinfo.GetRoleRef(ws.rbLister, ws.crbLister, request)
|
||||
if err != nil {
|
||||
|
@ -218,7 +227,7 @@ func (ws *WebhookServer) handleMutateAdmissionRequest(request *v1beta1.Admission
|
|||
}
|
||||
|
||||
// convert RAW to unstructured
|
||||
resource, err := convertResource(request.Object.Raw, request.Kind.Group, request.Kind.Version, request.Kind.Kind, request.Namespace)
|
||||
resource, err := utils.ConvertResource(request.Object.Raw, request.Kind.Group, request.Kind.Version, request.Kind.Kind, request.Namespace)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to convert RAW resource to unstructured format")
|
||||
|
||||
|
@ -240,22 +249,43 @@ func (ws *WebhookServer) handleMutateAdmissionRequest(request *v1beta1.Admission
|
|||
}
|
||||
}
|
||||
|
||||
var patches, patchedResource []byte
|
||||
userRequestInfo := v1.RequestInfo{
|
||||
Roles: roles,
|
||||
ClusterRoles: clusterRoles,
|
||||
AdmissionUserInfo: request.UserInfo}
|
||||
|
||||
versionCheck := utils.CompareKubernetesVersion(ws.client, ws.log, 1, 14, 0)
|
||||
// build context
|
||||
ctx := context2.NewContext()
|
||||
err = ctx.AddRequest(request)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to load incoming request in context")
|
||||
}
|
||||
|
||||
if versionCheck {
|
||||
err = ctx.AddUserInfo(userRequestInfo)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to load userInfo in context")
|
||||
}
|
||||
err = ctx.AddSA(userRequestInfo.AdmissionUserInfo.Username)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to load service account in context")
|
||||
}
|
||||
|
||||
var patches []byte
|
||||
patchedResource := request.Object.Raw
|
||||
|
||||
higherVersion := utils.HigherThanKubernetesVersion(ws.client, ws.log, 1, 14, 0)
|
||||
if higherVersion {
|
||||
// MUTATION
|
||||
// mutation failure should not block the resource creation
|
||||
// any mutation failure is reported as the violation
|
||||
patches = ws.HandleMutation(request, resource, policies, roles, clusterRoles)
|
||||
patches := ws.HandleMutation(request, resource, policies, ctx, userRequestInfo)
|
||||
|
||||
// patch the resource with patches before handling validation rules
|
||||
patchedResource := processResourceWithPatches(patches, request.Object.Raw, logger)
|
||||
patchedResource = processResourceWithPatches(patches, request.Object.Raw, logger)
|
||||
|
||||
if ws.resourceWebhookWatcher != nil && ws.resourceWebhookWatcher.RunValidationInMutatingWebhook == "true" {
|
||||
// VALIDATION
|
||||
ok, msg := ws.HandleValidation(request, policies, patchedResource, roles, clusterRoles)
|
||||
ok, msg := ws.HandleValidation(request, policies, patchedResource, ctx, userRequestInfo)
|
||||
if !ok {
|
||||
logger.Info("admission request denied")
|
||||
return &v1beta1.AdmissionResponse{
|
||||
|
@ -267,16 +297,14 @@ func (ws *WebhookServer) handleMutateAdmissionRequest(request *v1beta1.Admission
|
|||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// patch the resource with patches before handling validation rules
|
||||
patchedResource = processResourceWithPatches(patches, request.Object.Raw, logger)
|
||||
}
|
||||
|
||||
// GENERATE
|
||||
// Only applied during resource creation
|
||||
// Success -> Generate Request CR created successsfully
|
||||
// Failed -> Failed to create Generate Request CR
|
||||
if request.Operation == v1beta1.Create {
|
||||
ok, msg := ws.HandleGenerate(request, policies, patchedResource, roles, clusterRoles)
|
||||
ok, msg := ws.HandleGenerate(request, policies, ctx, userRequestInfo)
|
||||
if !ok {
|
||||
logger.Info("admission request denied")
|
||||
return &v1beta1.AdmissionResponse{
|
||||
|
@ -302,8 +330,17 @@ func (ws *WebhookServer) handleMutateAdmissionRequest(request *v1beta1.Admission
|
|||
|
||||
}
|
||||
|
||||
func (ws *WebhookServer) handleValidateAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
logger := ws.log.WithName("handleValidateAdmissionRequest").WithValues("uid", request.UID, "kind", request.Kind.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
|
||||
func (ws *WebhookServer) resourceValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
if excludeKyvernoResources(request.Kind.Kind) {
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
Result: &metav1.Status{
|
||||
Status: "Success",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
logger := ws.log.WithName("resourceValidation").WithValues("uid", request.UID, "kind", request.Kind.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
|
||||
policies, err := ws.pMetaStore.ListAll()
|
||||
if err != nil {
|
||||
// Unable to connect to policy Lister to access policies
|
||||
|
@ -322,7 +359,33 @@ func (ws *WebhookServer) handleValidateAdmissionRequest(request *v1beta1.Admissi
|
|||
}
|
||||
}
|
||||
|
||||
resource, err := convertResource(request.Object.Raw, request.Kind.Group, request.Kind.Version, request.Kind.Kind, request.Namespace)
|
||||
userRequestInfo := v1.RequestInfo{
|
||||
Roles: roles,
|
||||
ClusterRoles: clusterRoles,
|
||||
AdmissionUserInfo: request.UserInfo}
|
||||
|
||||
// build context
|
||||
ctx := context2.NewContext()
|
||||
err = ctx.AddRequest(request)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to load incoming request in context")
|
||||
}
|
||||
|
||||
err = ctx.AddUserInfo(userRequestInfo)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to load userInfo in context")
|
||||
}
|
||||
err = ctx.AddSA(userRequestInfo.AdmissionUserInfo.Username)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to load service account in context")
|
||||
}
|
||||
|
||||
raw := request.Object.Raw
|
||||
if request.Operation == v1beta1.Delete {
|
||||
raw = request.OldObject.Raw
|
||||
}
|
||||
|
||||
resource, err := convertResource(raw, request.Kind.Group, request.Kind.Version, request.Kind.Kind, request.Namespace)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to convert RAW resource to unstructured format")
|
||||
|
||||
|
@ -344,11 +407,9 @@ func (ws *WebhookServer) handleValidateAdmissionRequest(request *v1beta1.Admissi
|
|||
}
|
||||
}
|
||||
|
||||
versionCheck := utils.CompareKubernetesVersion(ws.client, ws.log, 1, 14, 0)
|
||||
|
||||
if !versionCheck {
|
||||
// VALIDATION
|
||||
ok, msg := ws.HandleValidation(request, policies, nil, roles, clusterRoles)
|
||||
higherVersion := utils.HigherThanKubernetesVersion(ws.client, ws.log, 1, 14, 0)
|
||||
if higherVersion {
|
||||
ok, msg := ws.HandleValidation(request, policies, nil, ctx, userRequestInfo)
|
||||
if !ok {
|
||||
logger.Info("admission request denied")
|
||||
return &v1beta1.AdmissionResponse{
|
||||
|
@ -360,6 +421,7 @@ func (ws *WebhookServer) handleValidateAdmissionRequest(request *v1beta1.Admissi
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
Result: &metav1.Status{
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
|
@ -20,7 +22,9 @@ import (
|
|||
func (ws *WebhookServer) HandleValidation(
|
||||
request *v1beta1.AdmissionRequest,
|
||||
policies []kyverno.ClusterPolicy,
|
||||
patchedResource []byte, roles, clusterRoles []string) (bool, string) {
|
||||
patchedResource []byte,
|
||||
ctx *context.Context,
|
||||
userRequestInfo kyverno.RequestInfo) (bool, string) {
|
||||
|
||||
resourceName := request.Kind.Kind + "/" + request.Name
|
||||
if request.Namespace != "" {
|
||||
|
@ -30,41 +34,20 @@ func (ws *WebhookServer) HandleValidation(
|
|||
logger := ws.log.WithValues("action", "validate", "resource", resourceName, "operation", request.Operation)
|
||||
|
||||
// Get new and old resource
|
||||
newR, oldR, err := extractResources(patchedResource, request)
|
||||
newR, oldR, err := utils.ExtractResources(patchedResource, request)
|
||||
if err != nil {
|
||||
// as resource cannot be parsed, we skip processing
|
||||
logger.Error(err, "failed to extract resource")
|
||||
return true, ""
|
||||
}
|
||||
|
||||
userRequestInfo := kyverno.RequestInfo{
|
||||
Roles: roles,
|
||||
ClusterRoles: clusterRoles,
|
||||
AdmissionUserInfo: request.UserInfo}
|
||||
// build context
|
||||
ctx := context.NewContext()
|
||||
// load incoming resource into the context
|
||||
err = ctx.AddResource(request.Object.Raw)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to load incoming resource in context")
|
||||
}
|
||||
|
||||
err = ctx.AddUserInfo(userRequestInfo)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to load userInfo in context")
|
||||
}
|
||||
|
||||
err = ctx.AddSA(userRequestInfo.AdmissionUserInfo.Username)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to load service account in context")
|
||||
}
|
||||
|
||||
policyContext := engine.PolicyContext{
|
||||
NewResource: newR,
|
||||
OldResource: oldR,
|
||||
Context: ctx,
|
||||
AdmissionInfo: userRequestInfo,
|
||||
}
|
||||
|
||||
var engineResponses []response.EngineResponse
|
||||
for _, policy := range policies {
|
||||
logger.V(3).Info("evaluating policy", "policy", policy.Name)
|
||||
|
|
Loading…
Add table
Reference in a new issue