2019-06-18 11:47:45 -07:00
|
|
|
package webhooks
|
|
|
|
|
|
|
|
import (
|
2019-07-22 18:50:09 -04:00
|
|
|
"encoding/json"
|
2019-07-15 16:07:56 -07:00
|
|
|
"fmt"
|
2019-07-19 12:47:20 -07:00
|
|
|
"reflect"
|
2019-06-18 11:47:45 -07:00
|
|
|
"strings"
|
2019-06-19 14:05:23 -07:00
|
|
|
|
2019-07-19 12:47:20 -07:00
|
|
|
"github.com/golang/glog"
|
2019-06-19 14:05:23 -07:00
|
|
|
"github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
2019-07-15 16:07:56 -07:00
|
|
|
"github.com/nirmata/kyverno/pkg/info"
|
2019-07-19 12:47:20 -07:00
|
|
|
v1beta1 "k8s.io/api/admission/v1beta1"
|
|
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
2019-06-18 11:47:45 -07:00
|
|
|
)
|
|
|
|
|
2019-07-15 16:07:56 -07:00
|
|
|
const policyKind = "Policy"
|
|
|
|
|
|
|
|
func isAdmSuccesful(policyInfos []*info.PolicyInfo) (bool, string) {
|
|
|
|
var admSuccess = true
|
|
|
|
var errMsgs []string
|
|
|
|
for _, pi := range policyInfos {
|
|
|
|
if !pi.IsSuccessful() {
|
|
|
|
admSuccess = false
|
|
|
|
errMsgs = append(errMsgs, fmt.Sprintf("\nPolicy %s failed with following rules", pi.Name))
|
|
|
|
// Get the error rules
|
|
|
|
errorRules := pi.ErrorRules()
|
|
|
|
errMsgs = append(errMsgs, errorRules)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return admSuccess, strings.Join(errMsgs, ";")
|
|
|
|
}
|
|
|
|
|
2019-06-18 11:47:45 -07:00
|
|
|
//StringInSlice checks if string is present in slice of strings
|
|
|
|
func StringInSlice(kind string, list []string) bool {
|
|
|
|
for _, b := range list {
|
|
|
|
if b == kind {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
//parseKinds parses the kinds if a single string contains comma seperated kinds
|
|
|
|
// {"1,2,3","4","5"} => {"1","2","3","4","5"}
|
|
|
|
func parseKinds(list []string) []string {
|
|
|
|
kinds := []string{}
|
|
|
|
for _, k := range list {
|
|
|
|
args := strings.Split(k, ",")
|
|
|
|
for _, arg := range args {
|
|
|
|
if arg != "" {
|
|
|
|
kinds = append(kinds, strings.TrimSpace(arg))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return kinds
|
|
|
|
}
|
|
|
|
|
|
|
|
type ArrayFlags []string
|
|
|
|
|
|
|
|
func (i *ArrayFlags) String() string {
|
|
|
|
var sb strings.Builder
|
|
|
|
for _, str := range *i {
|
|
|
|
sb.WriteString(str)
|
|
|
|
}
|
|
|
|
return sb.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *ArrayFlags) Set(value string) error {
|
|
|
|
*i = append(*i, value)
|
|
|
|
return nil
|
|
|
|
}
|
2019-06-19 14:05:23 -07:00
|
|
|
|
|
|
|
// extract the kinds that the policy rules apply to
|
|
|
|
func getApplicableKindsForPolicy(p *v1alpha1.Policy) []string {
|
|
|
|
kindsMap := map[string]interface{}{}
|
|
|
|
kinds := []string{}
|
|
|
|
// iterate over the rules an identify all kinds
|
|
|
|
for _, rule := range p.Spec.Rules {
|
|
|
|
for _, k := range rule.ResourceDescription.Kinds {
|
|
|
|
kindsMap[k] = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the kinds
|
|
|
|
for k := range kindsMap {
|
|
|
|
kinds = append(kinds, k)
|
|
|
|
}
|
|
|
|
return kinds
|
|
|
|
}
|
2019-07-15 19:14:42 -07:00
|
|
|
|
|
|
|
// Policy Reporting Modes
|
|
|
|
const (
|
2019-07-16 15:53:14 -07:00
|
|
|
BlockChanges = "block"
|
|
|
|
ReportViolation = "report"
|
2019-07-15 19:14:42 -07:00
|
|
|
)
|
2019-07-16 15:53:14 -07:00
|
|
|
|
2019-07-18 10:22:20 -07:00
|
|
|
// returns true -> if there is even one policy that blocks resource requst
|
|
|
|
// returns false -> if all the policies are meant to report only, we dont block resource request
|
2019-07-16 15:53:14 -07:00
|
|
|
func toBlock(pis []*info.PolicyInfo) bool {
|
|
|
|
for _, pi := range pis {
|
|
|
|
if pi.ValidationFailureAction != ReportViolation {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2019-07-19 12:47:20 -07:00
|
|
|
|
|
|
|
func checkIfOnlyAnnotationsUpdate(request *v1beta1.AdmissionRequest) bool {
|
2019-07-19 13:53:36 -07:00
|
|
|
// process only if its for existing resources
|
|
|
|
if request.Operation != v1beta1.Update {
|
|
|
|
return false
|
|
|
|
}
|
2019-07-19 12:47:20 -07:00
|
|
|
// updated resoruce
|
|
|
|
obj := request.Object
|
|
|
|
objUnstr := unstructured.Unstructured{}
|
2019-07-22 18:50:09 -04:00
|
|
|
objUnstr.SetKind(request.Kind.Kind)
|
|
|
|
//TODO: hack, set kind for unmarshalling and observed generation
|
|
|
|
obj.Raw = setKindForObject(obj.Raw, request.Kind.Kind)
|
|
|
|
obj.Raw = setObserverdGenerationAsZero(obj.Raw)
|
2019-07-19 12:47:20 -07:00
|
|
|
err := objUnstr.UnmarshalJSON(obj.Raw)
|
|
|
|
if err != nil {
|
|
|
|
glog.Error(err)
|
|
|
|
return false
|
|
|
|
}
|
2019-07-22 18:50:09 -04:00
|
|
|
objUnstr.SetSelfLink("")
|
2019-07-19 12:47:20 -07:00
|
|
|
objUnstr.SetAnnotations(nil)
|
|
|
|
objUnstr.SetGeneration(0)
|
2019-07-22 18:50:09 -04:00
|
|
|
objUnstr.SetResourceVersion("")
|
|
|
|
|
2019-07-19 12:47:20 -07:00
|
|
|
oldobj := request.OldObject
|
|
|
|
oldobjUnstr := unstructured.Unstructured{}
|
2019-07-22 18:50:09 -04:00
|
|
|
oldobj.Raw = setKindForObject(oldobj.Raw, request.Kind.Kind)
|
|
|
|
oldobj.Raw = setObserverdGenerationAsZero(oldobj.Raw)
|
2019-07-19 12:47:20 -07:00
|
|
|
err = oldobjUnstr.UnmarshalJSON(oldobj.Raw)
|
|
|
|
if err != nil {
|
|
|
|
glog.Error(err)
|
|
|
|
return false
|
|
|
|
}
|
2019-07-22 18:50:09 -04:00
|
|
|
oldobjUnstr.SetSelfLink("")
|
2019-07-19 12:47:20 -07:00
|
|
|
oldobjUnstr.SetAnnotations(nil)
|
|
|
|
oldobjUnstr.SetGeneration(0)
|
2019-07-22 18:50:09 -04:00
|
|
|
oldobjUnstr.SetResourceVersion("")
|
|
|
|
|
2019-07-19 12:47:20 -07:00
|
|
|
if reflect.DeepEqual(objUnstr, oldobjUnstr) {
|
2019-07-22 18:50:09 -04:00
|
|
|
glog.Info("only annoations added")
|
2019-07-19 12:47:20 -07:00
|
|
|
return true
|
|
|
|
}
|
2019-07-22 18:50:09 -04:00
|
|
|
glog.Info("more than annotations changed")
|
2019-07-19 12:47:20 -07:00
|
|
|
return false
|
|
|
|
}
|
2019-07-22 18:50:09 -04:00
|
|
|
|
|
|
|
func setKindForObject(bytes []byte, kind string) []byte {
|
|
|
|
var objectJSON map[string]interface{}
|
|
|
|
json.Unmarshal(bytes, &objectJSON)
|
|
|
|
objectJSON["kind"] = kind
|
|
|
|
data, err := json.Marshal(objectJSON)
|
|
|
|
if err != nil {
|
|
|
|
glog.Error(err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
|
|
|
func setObserverdGenerationAsZero(bytes []byte) []byte {
|
|
|
|
var objectJSON map[string]interface{}
|
|
|
|
json.Unmarshal(bytes, &objectJSON)
|
|
|
|
status, ok := objectJSON["status"].(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
glog.Error("type mismatch")
|
|
|
|
}
|
|
|
|
status["observedGeneration"] = 0
|
|
|
|
objectJSON["status"] = status
|
|
|
|
data, err := json.Marshal(objectJSON)
|
|
|
|
if err != nil {
|
|
|
|
glog.Error(err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return data
|
|
|
|
|
|
|
|
}
|