2020-07-10 18:12:19 +05:30
|
|
|
package policymutation
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/go-logr/logr"
|
2021-10-29 18:13:20 +02:00
|
|
|
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
|
2022-03-02 00:19:31 +01:00
|
|
|
"github.com/kyverno/kyverno/pkg/autogen"
|
2020-10-07 11:12:31 -07:00
|
|
|
"github.com/kyverno/kyverno/pkg/common"
|
2022-03-28 16:01:27 +02:00
|
|
|
"github.com/kyverno/kyverno/pkg/toggle"
|
2022-03-31 18:34:52 +02:00
|
|
|
jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
|
2020-07-10 18:12:19 +05:30
|
|
|
)
|
|
|
|
|
2020-11-17 13:07:30 -08:00
|
|
|
// GenerateJSONPatchesForDefaults generates default JSON patches for
|
|
|
|
// - ValidationFailureAction
|
|
|
|
// - Background
|
|
|
|
// - auto-gen annotation and rules
|
2022-03-31 08:44:00 +02:00
|
|
|
func GenerateJSONPatchesForDefaults(policy kyverno.PolicyInterface, log logr.Logger) ([]byte, []string) {
|
2020-07-10 18:12:19 +05:30
|
|
|
var patches [][]byte
|
|
|
|
var updateMsgs []string
|
2022-03-31 08:44:00 +02:00
|
|
|
spec := policy.GetSpec()
|
2020-07-10 18:12:19 +05:30
|
|
|
// default 'ValidationFailureAction'
|
2022-03-31 08:44:00 +02:00
|
|
|
if patch, updateMsg := defaultvalidationFailureAction(spec, log); patch != nil {
|
2020-07-10 18:12:19 +05:30
|
|
|
patches = append(patches, patch)
|
|
|
|
updateMsgs = append(updateMsgs, updateMsg)
|
|
|
|
}
|
|
|
|
|
|
|
|
// default 'Background'
|
2022-03-31 08:44:00 +02:00
|
|
|
if patch, updateMsg := defaultBackgroundFlag(spec, log); patch != nil {
|
2020-07-10 18:12:19 +05:30
|
|
|
patches = append(patches, patch)
|
|
|
|
updateMsgs = append(updateMsgs, updateMsg)
|
|
|
|
}
|
|
|
|
|
2022-03-31 08:44:00 +02:00
|
|
|
if patch, updateMsg := defaultFailurePolicy(spec, log); patch != nil {
|
2021-10-05 00:15:09 -07:00
|
|
|
patches = append(patches, patch)
|
|
|
|
updateMsgs = append(updateMsgs, updateMsg)
|
|
|
|
}
|
|
|
|
|
2022-03-28 16:01:27 +02:00
|
|
|
// if autogenInternals is enabled, we don't mutate rules in the webhook
|
|
|
|
if !toggle.AutogenInternals() {
|
2022-03-31 08:44:00 +02:00
|
|
|
patch, errs := GeneratePodControllerRule(policy, log)
|
2022-03-28 16:01:27 +02:00
|
|
|
if len(errs) > 0 {
|
|
|
|
var errMsgs []string
|
|
|
|
for _, err := range errs {
|
|
|
|
errMsgs = append(errMsgs, err.Error())
|
|
|
|
log.Error(err, "failed to generate pod controller rule")
|
|
|
|
}
|
|
|
|
updateMsgs = append(updateMsgs, strings.Join(errMsgs, ";"))
|
2020-07-10 18:12:19 +05:30
|
|
|
}
|
2022-03-28 16:01:27 +02:00
|
|
|
patches = append(patches, patch...)
|
2020-07-10 18:12:19 +05:30
|
|
|
}
|
|
|
|
|
2021-08-12 23:26:35 +05:30
|
|
|
formatedGVK, errs := checkForGVKFormatPatch(policy, log)
|
|
|
|
if len(errs) > 0 {
|
|
|
|
var errMsgs []string
|
|
|
|
for _, err := range errs {
|
|
|
|
errMsgs = append(errMsgs, err.Error())
|
|
|
|
log.Error(err, "failed to format the kind")
|
|
|
|
}
|
|
|
|
updateMsgs = append(updateMsgs, strings.Join(errMsgs, ";"))
|
|
|
|
}
|
|
|
|
patches = append(patches, formatedGVK...)
|
|
|
|
|
2022-03-31 18:34:52 +02:00
|
|
|
return jsonutils.JoinPatches(patches), updateMsgs
|
2020-07-10 18:12:19 +05:30
|
|
|
}
|
|
|
|
|
2022-03-31 08:44:00 +02:00
|
|
|
func checkForGVKFormatPatch(policy kyverno.PolicyInterface, log logr.Logger) (patches [][]byte, errs []error) {
|
2021-08-12 23:26:35 +05:30
|
|
|
patches = make([][]byte, 0)
|
2022-03-28 16:01:27 +02:00
|
|
|
for i, rule := range autogen.ComputeRules(policy) {
|
2021-09-14 19:46:04 -07:00
|
|
|
patchByte, err := convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/match/resources/kinds", strconv.Itoa(i)), rule.MatchResources.Kinds, log)
|
|
|
|
if err == nil && patchByte != nil {
|
|
|
|
patches = append(patches, patchByte)
|
|
|
|
} else if err != nil {
|
2022-03-31 08:44:00 +02:00
|
|
|
errs = append(errs, fmt.Errorf("failed to GVK for rule '%s/%s/%d/match': %v", policy.GetName(), rule.Name, i, err))
|
2021-08-12 23:26:35 +05:30
|
|
|
}
|
2021-09-14 19:46:04 -07:00
|
|
|
|
|
|
|
for j, matchAll := range rule.MatchResources.All {
|
|
|
|
patchByte, err := convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/match/all/%s/resources/kinds", strconv.Itoa(i), strconv.Itoa(j)), matchAll.ResourceDescription.Kinds, log)
|
|
|
|
if err == nil && patchByte != nil {
|
|
|
|
patches = append(patches, patchByte)
|
|
|
|
} else if err != nil {
|
2022-03-31 08:44:00 +02:00
|
|
|
errs = append(errs, fmt.Errorf("failed to convert GVK for rule '%s/%s/%d/match/all/%d': %v", policy.GetName(), rule.Name, i, j, err))
|
2021-09-14 19:46:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, matchAny := range rule.MatchResources.Any {
|
|
|
|
patchByte, err := convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/match/any/%s/resources/kinds", strconv.Itoa(i), strconv.Itoa(k)), matchAny.ResourceDescription.Kinds, log)
|
|
|
|
if err == nil && patchByte != nil {
|
|
|
|
patches = append(patches, patchByte)
|
|
|
|
} else if err != nil {
|
2022-03-31 08:44:00 +02:00
|
|
|
errs = append(errs, fmt.Errorf("failed to convert GVK for rule '%s/%s/%d/match/any/%d': %v", policy.GetName(), rule.Name, i, k, err))
|
2021-09-14 19:46:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
patchByte, err = convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/exclude/resources/kinds", strconv.Itoa(i)), rule.ExcludeResources.Kinds, log)
|
|
|
|
if err == nil && patchByte != nil {
|
|
|
|
patches = append(patches, patchByte)
|
|
|
|
} else if err != nil {
|
2022-03-31 08:44:00 +02:00
|
|
|
errs = append(errs, fmt.Errorf("failed to convert GVK for rule '%s/%s/%d/exclude': %v", policy.GetName(), rule.Name, i, err))
|
2021-08-12 23:26:35 +05:30
|
|
|
}
|
2021-09-14 19:46:04 -07:00
|
|
|
|
|
|
|
for j, excludeAll := range rule.ExcludeResources.All {
|
|
|
|
patchByte, err := convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/exclude/all/%s/resources/kinds", strconv.Itoa(i), strconv.Itoa(j)), excludeAll.ResourceDescription.Kinds, log)
|
|
|
|
if err == nil && patchByte != nil {
|
|
|
|
patches = append(patches, patchByte)
|
|
|
|
} else if err != nil {
|
2022-03-31 08:44:00 +02:00
|
|
|
errs = append(errs, fmt.Errorf("failed to convert GVK for rule '%s/%s/%d/exclude/all/%d': %v", policy.GetName(), rule.Name, i, j, err))
|
2021-09-14 19:46:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, excludeAny := range rule.ExcludeResources.Any {
|
|
|
|
patchByte, err := convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/exclude/any/%s/resources/kinds", strconv.Itoa(i), strconv.Itoa(k)), excludeAny.ResourceDescription.Kinds, log)
|
|
|
|
if err == nil && patchByte != nil {
|
|
|
|
patches = append(patches, patchByte)
|
|
|
|
} else if err != nil {
|
2022-03-31 08:44:00 +02:00
|
|
|
errs = append(errs, fmt.Errorf("failed to convert GVK for rule '%s/%s/%d/exclude/any/%d': %v", policy.GetName(), rule.Name, i, k, err))
|
2021-09-14 19:46:04 -07:00
|
|
|
}
|
2021-08-12 23:26:35 +05:30
|
|
|
}
|
|
|
|
}
|
2021-09-14 19:46:04 -07:00
|
|
|
|
2021-08-12 23:26:35 +05:30
|
|
|
return patches, errs
|
|
|
|
}
|
|
|
|
|
2021-09-14 19:46:04 -07:00
|
|
|
func convertGVKForKinds(path string, kinds []string, log logr.Logger) ([]byte, error) {
|
|
|
|
kindList := []string{}
|
|
|
|
for _, k := range kinds {
|
|
|
|
gvk := common.GetFormatedKind(k)
|
|
|
|
if gvk == k {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
kindList = append(kindList, gvk)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(kindList) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
p, err := buildReplaceJsonPatch(path, kindList)
|
|
|
|
log.V(4).WithName("convertGVKForKinds").Info("generated patch", "patch", string(p))
|
|
|
|
return p, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func buildReplaceJsonPatch(path string, kindList []string) ([]byte, error) {
|
|
|
|
jsonPatch := struct {
|
|
|
|
Path string `json:"path"`
|
|
|
|
Op string `json:"op"`
|
|
|
|
Value []string `json:"value"`
|
|
|
|
}{
|
|
|
|
path,
|
|
|
|
"replace",
|
|
|
|
kindList,
|
|
|
|
}
|
|
|
|
return json.Marshal(jsonPatch)
|
|
|
|
}
|
|
|
|
|
2022-03-01 23:42:19 +01:00
|
|
|
func defaultBackgroundFlag(spec *kyverno.Spec, log logr.Logger) ([]byte, string) {
|
2020-07-10 18:12:19 +05:30
|
|
|
// set 'Background' flag to 'true' if not specified
|
|
|
|
defaultVal := true
|
2022-03-01 23:42:19 +01:00
|
|
|
if spec.Background == nil {
|
2020-07-11 18:12:35 +05:30
|
|
|
log.V(4).Info("setting default value", "spec.background", true)
|
2020-07-10 18:12:19 +05:30
|
|
|
jsonPatch := struct {
|
|
|
|
Path string `json:"path"`
|
|
|
|
Op string `json:"op"`
|
|
|
|
Value *bool `json:"value"`
|
|
|
|
}{
|
|
|
|
"/spec/background",
|
|
|
|
"add",
|
|
|
|
&defaultVal,
|
|
|
|
}
|
|
|
|
|
|
|
|
patchByte, err := json.Marshal(jsonPatch)
|
|
|
|
if err != nil {
|
2020-07-11 18:12:35 +05:30
|
|
|
log.Error(err, "failed to set default value", "spec.background", true)
|
2020-07-10 18:12:19 +05:30
|
|
|
return nil, ""
|
|
|
|
}
|
|
|
|
|
2020-07-11 18:12:35 +05:30
|
|
|
log.V(3).Info("generated JSON Patch to set default", "spec.background", true)
|
2020-07-10 18:12:19 +05:30
|
|
|
return patchByte, fmt.Sprintf("default 'Background' to '%s'", strconv.FormatBool(true))
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, ""
|
|
|
|
}
|
|
|
|
|
2022-03-01 23:42:19 +01:00
|
|
|
func defaultvalidationFailureAction(spec *kyverno.Spec, log logr.Logger) ([]byte, string) {
|
2020-07-10 18:12:19 +05:30
|
|
|
// set ValidationFailureAction to "audit" if not specified
|
2022-03-23 09:59:41 +01:00
|
|
|
Audit := kyverno.Audit
|
2022-03-01 23:42:19 +01:00
|
|
|
if spec.ValidationFailureAction == "" {
|
2020-08-05 23:53:27 +05:30
|
|
|
log.V(4).Info("setting default value", "spec.validationFailureAction", Audit)
|
2020-07-10 18:12:19 +05:30
|
|
|
|
|
|
|
jsonPatch := struct {
|
|
|
|
Path string `json:"path"`
|
|
|
|
Op string `json:"op"`
|
|
|
|
Value string `json:"value"`
|
|
|
|
}{
|
|
|
|
"/spec/validationFailureAction",
|
|
|
|
"add",
|
2022-03-23 09:59:41 +01:00
|
|
|
string(Audit),
|
2020-07-10 18:12:19 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
patchByte, err := json.Marshal(jsonPatch)
|
|
|
|
if err != nil {
|
2020-07-11 17:52:52 +05:30
|
|
|
log.Error(err, "failed to default value", "spec.validationFailureAction", Audit)
|
2020-07-10 18:12:19 +05:30
|
|
|
return nil, ""
|
|
|
|
}
|
|
|
|
|
2020-07-11 17:52:52 +05:30
|
|
|
log.V(3).Info("generated JSON Patch to set default", "spec.validationFailureAction", Audit)
|
2020-07-10 18:12:19 +05:30
|
|
|
|
|
|
|
return patchByte, fmt.Sprintf("default 'ValidationFailureAction' to '%s'", Audit)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, ""
|
|
|
|
}
|
2022-03-01 23:42:19 +01:00
|
|
|
|
|
|
|
func defaultFailurePolicy(spec *kyverno.Spec, log logr.Logger) ([]byte, string) {
|
2021-10-05 00:15:09 -07:00
|
|
|
// set failurePolicy to Fail if not present
|
|
|
|
failurePolicy := string(kyverno.Fail)
|
2022-03-01 23:42:19 +01:00
|
|
|
if spec.FailurePolicy == nil {
|
2021-10-05 00:15:09 -07:00
|
|
|
log.V(4).Info("setting default value", "spec.failurePolicy", failurePolicy)
|
|
|
|
jsonPatch := struct {
|
|
|
|
Path string `json:"path"`
|
|
|
|
Op string `json:"op"`
|
|
|
|
Value string `json:"value"`
|
|
|
|
}{
|
|
|
|
"/spec/failurePolicy",
|
|
|
|
"add",
|
|
|
|
string(kyverno.Fail),
|
|
|
|
}
|
|
|
|
|
|
|
|
patchByte, err := json.Marshal(jsonPatch)
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err, "failed to set default value", "spec.failurePolicy", failurePolicy)
|
|
|
|
return nil, ""
|
|
|
|
}
|
|
|
|
|
|
|
|
log.V(3).Info("generated JSON Patch to set default", "spec.failurePolicy", failurePolicy)
|
|
|
|
return patchByte, fmt.Sprintf("default failurePolicy to '%s'", failurePolicy)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, ""
|
|
|
|
}
|
2020-07-10 18:12:19 +05:30
|
|
|
|
|
|
|
// podControllersKey annotation could be:
|
|
|
|
// scenario A: not exist, set default to "all", which generates on all pod controllers
|
|
|
|
// - if name / selector exist in resource description -> skip
|
|
|
|
// as these fields may not be applicable to pod controllers
|
|
|
|
// scenario B: "none", user explicitly disable this feature -> skip
|
|
|
|
// scenario C: some certain controllers that user set -> generate on defined controllers
|
2020-12-01 22:50:40 -08:00
|
|
|
// copy entire match / exclude block, it's users' responsibility to
|
|
|
|
// make sure all fields are applicable to pod controllers
|
2020-07-10 18:12:19 +05:30
|
|
|
|
|
|
|
// GeneratePodControllerRule returns two patches: rulePatches and annotation patch(if necessary)
|
2022-03-31 08:44:00 +02:00
|
|
|
func GeneratePodControllerRule(policy kyverno.PolicyInterface, log logr.Logger) (patches [][]byte, errs []error) {
|
|
|
|
spec := policy.GetSpec()
|
|
|
|
applyAutoGen, desiredControllers := autogen.CanAutoGen(spec, log)
|
2021-04-29 14:59:37 -07:00
|
|
|
|
2022-01-19 23:12:31 +05:30
|
|
|
if !applyAutoGen {
|
|
|
|
desiredControllers = "none"
|
|
|
|
}
|
|
|
|
|
2020-07-10 18:12:19 +05:30
|
|
|
ann := policy.GetAnnotations()
|
2022-03-09 14:48:04 +01:00
|
|
|
actualControllers, ok := ann[kyverno.PodControllersAnnotation]
|
2020-07-10 18:12:19 +05:30
|
|
|
|
2021-04-29 14:59:37 -07:00
|
|
|
// - scenario A
|
|
|
|
// - predefined controllers are invalid, overwrite the value
|
|
|
|
if !ok || !applyAutoGen {
|
|
|
|
actualControllers = desiredControllers
|
2022-03-28 16:01:27 +02:00
|
|
|
annPatch, err := defaultPodControllerAnnotation(ann, actualControllers)
|
|
|
|
if err != nil {
|
2022-03-31 08:44:00 +02:00
|
|
|
errs = append(errs, fmt.Errorf("failed to generate pod controller annotation for policy '%s': %v", policy.GetName(), err))
|
2022-03-28 16:01:27 +02:00
|
|
|
} else {
|
|
|
|
patches = append(patches, annPatch)
|
2020-07-10 18:12:19 +05:30
|
|
|
}
|
2021-04-29 14:59:37 -07:00
|
|
|
} else {
|
|
|
|
if !applyAutoGen {
|
|
|
|
actualControllers = desiredControllers
|
|
|
|
}
|
2020-07-10 18:12:19 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
// scenario B
|
2021-04-29 14:59:37 -07:00
|
|
|
if actualControllers == "none" {
|
|
|
|
return patches, nil
|
2020-07-10 18:12:19 +05:30
|
|
|
}
|
|
|
|
|
2021-04-29 14:59:37 -07:00
|
|
|
log.V(3).Info("auto generating rule for pod controllers", "controllers", actualControllers)
|
2020-07-10 18:12:19 +05:30
|
|
|
|
2022-03-31 08:44:00 +02:00
|
|
|
p, err := autogen.GenerateRulePatches(spec, actualControllers, log)
|
2020-07-10 18:12:19 +05:30
|
|
|
patches = append(patches, p...)
|
|
|
|
errs = append(errs, err...)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-09-01 09:11:20 -07:00
|
|
|
// defaultPodControllerAnnotation inserts an annotation
|
2021-04-29 14:59:37 -07:00
|
|
|
// "pod-policies.kyverno.io/autogen-controllers=<controllers>" to policy
|
|
|
|
func defaultPodControllerAnnotation(ann map[string]string, controllers string) ([]byte, error) {
|
2020-07-10 18:12:19 +05:30
|
|
|
if ann == nil {
|
|
|
|
ann = make(map[string]string)
|
2022-03-09 14:48:04 +01:00
|
|
|
ann[kyverno.PodControllersAnnotation] = controllers
|
2020-07-10 18:12:19 +05:30
|
|
|
jsonPatch := struct {
|
|
|
|
Path string `json:"path"`
|
|
|
|
Op string `json:"op"`
|
|
|
|
Value interface{} `json:"value"`
|
|
|
|
}{
|
|
|
|
"/metadata/annotations",
|
|
|
|
"add",
|
|
|
|
ann,
|
|
|
|
}
|
|
|
|
|
|
|
|
patchByte, err := json.Marshal(jsonPatch)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return patchByte, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
jsonPatch := struct {
|
|
|
|
Path string `json:"path"`
|
|
|
|
Op string `json:"op"`
|
|
|
|
Value interface{} `json:"value"`
|
|
|
|
}{
|
|
|
|
"/metadata/annotations/pod-policies.kyverno.io~1autogen-controllers",
|
|
|
|
"add",
|
2021-04-29 14:59:37 -07:00
|
|
|
controllers,
|
2020-07-10 18:12:19 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
patchByte, err := json.Marshal(jsonPatch)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return patchByte, nil
|
|
|
|
}
|