2020-05-29 15:32:29 +05:30
|
|
|
package common
|
|
|
|
|
|
|
|
import (
|
2020-09-01 09:38:49 -07:00
|
|
|
"bufio"
|
|
|
|
"bytes"
|
2020-05-29 15:32:29 +05:30
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2020-09-01 09:38:49 -07:00
|
|
|
"io"
|
2020-05-29 15:32:29 +05:30
|
|
|
"io/ioutil"
|
2020-10-19 12:36:55 -07:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2020-08-31 19:32:00 +05:30
|
|
|
|
2020-07-29 21:41:58 +05:30
|
|
|
jsonpatch "github.com/evanphx/json-patch"
|
|
|
|
"github.com/go-logr/logr"
|
2020-10-07 11:12:31 -07:00
|
|
|
v1 "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
2020-11-17 13:07:30 -08:00
|
|
|
sanitizederror "github.com/kyverno/kyverno/pkg/kyverno/sanitizedError"
|
2020-10-07 11:12:31 -07:00
|
|
|
"github.com/kyverno/kyverno/pkg/policymutation"
|
|
|
|
"github.com/kyverno/kyverno/pkg/utils"
|
2020-11-17 13:07:30 -08:00
|
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
|
|
"k8s.io/apimachinery/pkg/util/yaml"
|
|
|
|
yaml_v2 "sigs.k8s.io/yaml"
|
2020-05-29 15:32:29 +05:30
|
|
|
)
|
|
|
|
|
2020-06-01 16:49:40 +05:30
|
|
|
// GetPolicies - Extracting the policies from multiple YAML
|
2020-12-06 11:12:54 -08:00
|
|
|
func GetPolicies(paths []string) (policies []*v1.ClusterPolicy, error error) {
|
2020-11-03 01:25:32 +05:30
|
|
|
for _, path := range paths {
|
|
|
|
path = filepath.Clean(path)
|
|
|
|
fileDesc, err := os.Stat(path)
|
2020-10-21 20:05:05 +05:30
|
|
|
if err != nil {
|
2020-12-06 11:12:54 -08:00
|
|
|
return nil, err
|
2020-10-21 20:05:05 +05:30
|
|
|
}
|
2020-11-03 01:25:32 +05:30
|
|
|
if fileDesc.IsDir() {
|
|
|
|
files, err := ioutil.ReadDir(path)
|
2020-10-07 06:20:53 +05:30
|
|
|
if err != nil {
|
2020-12-06 11:12:54 -08:00
|
|
|
return nil, sanitizederror.NewWithError(fmt.Sprintf("failed to parse %v", path), err)
|
2020-10-07 06:20:53 +05:30
|
|
|
}
|
2020-11-03 01:25:32 +05:30
|
|
|
listOfFiles := make([]string, 0)
|
|
|
|
for _, file := range files {
|
|
|
|
listOfFiles = append(listOfFiles, filepath.Join(path, file.Name()))
|
2020-11-01 20:44:25 +05:30
|
|
|
}
|
2020-12-06 11:12:54 -08:00
|
|
|
policiesFromDir, err := GetPolicies(listOfFiles)
|
2020-11-01 23:18:58 +05:30
|
|
|
if err != nil {
|
2020-12-06 11:12:54 -08:00
|
|
|
return nil, sanitizederror.NewWithError(fmt.Sprintf("failed to extract policies from %v", listOfFiles), err)
|
2020-11-01 23:18:58 +05:30
|
|
|
}
|
|
|
|
|
2020-11-03 01:25:32 +05:30
|
|
|
policies = append(policies, policiesFromDir...)
|
|
|
|
} else {
|
2020-12-06 11:12:54 -08:00
|
|
|
file, err := ioutil.ReadFile(path)
|
2020-11-01 23:18:58 +05:30
|
|
|
if err != nil {
|
2020-12-06 11:12:54 -08:00
|
|
|
return nil, sanitizederror.NewWithError(fmt.Sprintf("failed to load file %v", path), err)
|
2020-11-03 01:25:32 +05:30
|
|
|
}
|
2020-12-06 11:12:54 -08:00
|
|
|
getPolicies, getErrors := utils.GetPolicy(file)
|
|
|
|
var errString string
|
|
|
|
for _, err := range getErrors {
|
|
|
|
if err != nil {
|
|
|
|
errString += err.Error() + "\n"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if errString != "" {
|
|
|
|
fmt.Printf("failed to extract policies: %s\n", errString)
|
|
|
|
os.Exit(2)
|
2020-11-01 23:18:58 +05:30
|
|
|
}
|
2020-10-21 20:05:05 +05:30
|
|
|
|
2020-12-06 11:12:54 -08:00
|
|
|
policies = append(policies, getPolicies...)
|
2020-11-01 23:18:58 +05:30
|
|
|
}
|
2020-10-21 20:05:05 +05:30
|
|
|
}
|
|
|
|
|
2020-12-06 11:12:54 -08:00
|
|
|
return policies, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//ValidateAndGetPolicies - validating policies
|
|
|
|
func ValidateAndGetPolicies(policyPaths []string) ([]*v1.ClusterPolicy, error) {
|
|
|
|
policies, err := GetPolicies(policyPaths)
|
|
|
|
if err != nil {
|
|
|
|
if !sanitizederror.IsErrorSanitized(err) {
|
|
|
|
return nil, sanitizederror.NewWithError((fmt.Sprintf("failed to parse %v path/s.", policyPaths)), err)
|
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return policies, nil
|
2020-05-29 15:32:29 +05:30
|
|
|
}
|
2020-07-07 16:20:55 +05:30
|
|
|
|
2020-08-24 03:41:03 +05:30
|
|
|
// PolicyHasVariables - check for variables in the policy
|
2020-11-19 15:03:15 +05:30
|
|
|
func PolicyHasVariables(policy v1.ClusterPolicy) [][]string {
|
2020-08-24 03:41:03 +05:30
|
|
|
policyRaw, _ := json.Marshal(policy)
|
2020-11-17 13:07:30 -08:00
|
|
|
matches := RegexVariables.FindAllStringSubmatch(string(policyRaw), -1)
|
2020-11-19 15:03:15 +05:30
|
|
|
return matches
|
2020-08-24 03:41:03 +05:30
|
|
|
}
|
|
|
|
|
2020-10-19 12:36:55 -07:00
|
|
|
// PolicyHasNonAllowedVariables - checks for unexpected variables in the policy
|
2020-08-24 03:41:03 +05:30
|
|
|
func PolicyHasNonAllowedVariables(policy v1.ClusterPolicy) bool {
|
|
|
|
policyRaw, _ := json.Marshal(policy)
|
|
|
|
|
2020-11-17 13:07:30 -08:00
|
|
|
matchesAll := RegexVariables.FindAllStringSubmatch(string(policyRaw), -1)
|
|
|
|
matchesAllowed := AllowedVariables.FindAllStringSubmatch(string(policyRaw), -1)
|
2020-08-24 03:41:03 +05:30
|
|
|
|
2020-10-19 12:36:55 -07:00
|
|
|
if len(matchesAll) > len(matchesAllowed) {
|
2020-09-23 02:41:49 +05:30
|
|
|
// If rules contains Context then skip this validation
|
|
|
|
for _, rule := range policy.Spec.Rules {
|
|
|
|
if len(rule.Context) > 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2020-10-19 12:36:55 -07:00
|
|
|
|
2020-08-24 03:41:03 +05:30
|
|
|
return true
|
|
|
|
}
|
2020-10-19 12:36:55 -07:00
|
|
|
|
2020-08-24 03:41:03 +05:30
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-07-29 21:41:58 +05:30
|
|
|
// MutatePolicy - applies mutation to a policy
|
|
|
|
func MutatePolicy(policy *v1.ClusterPolicy, logger logr.Logger) (*v1.ClusterPolicy, error) {
|
|
|
|
patches, _ := policymutation.GenerateJSONPatchesForDefaults(policy, logger)
|
2020-08-19 15:11:21 -07:00
|
|
|
if len(patches) == 0 {
|
|
|
|
return policy, nil
|
|
|
|
}
|
|
|
|
|
2020-07-29 21:41:58 +05:30
|
|
|
type jsonPatch struct {
|
|
|
|
Path string `json:"path"`
|
|
|
|
Op string `json:"op"`
|
|
|
|
Value interface{} `json:"value"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var jsonPatches []jsonPatch
|
|
|
|
err := json.Unmarshal(patches, &jsonPatches)
|
|
|
|
if err != nil {
|
2020-11-17 13:07:30 -08:00
|
|
|
return nil, sanitizederror.NewWithError(fmt.Sprintf("failed to unmarshal patches for %s policy", policy.Name), err)
|
2020-07-29 21:41:58 +05:30
|
|
|
}
|
2020-10-21 20:05:05 +05:30
|
|
|
|
2020-07-29 21:41:58 +05:30
|
|
|
patch, err := jsonpatch.DecodePatch(patches)
|
|
|
|
if err != nil {
|
2020-11-17 13:07:30 -08:00
|
|
|
return nil, sanitizederror.NewWithError(fmt.Sprintf("failed to decode patch for %s policy", policy.Name), err)
|
2020-07-29 21:41:58 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
policyBytes, _ := json.Marshal(policy)
|
|
|
|
if err != nil {
|
2020-11-17 13:07:30 -08:00
|
|
|
return nil, sanitizederror.NewWithError(fmt.Sprintf("failed to marshal %s policy", policy.Name), err)
|
2020-07-29 21:41:58 +05:30
|
|
|
}
|
|
|
|
modifiedPolicy, err := patch.Apply(policyBytes)
|
|
|
|
if err != nil {
|
2020-11-17 13:07:30 -08:00
|
|
|
return nil, sanitizederror.NewWithError(fmt.Sprintf("failed to apply %s policy", policy.Name), err)
|
2020-07-29 21:41:58 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
var p v1.ClusterPolicy
|
|
|
|
err = json.Unmarshal(modifiedPolicy, &p)
|
|
|
|
if err != nil {
|
2020-11-17 13:07:30 -08:00
|
|
|
return nil, sanitizederror.NewWithError(fmt.Sprintf("failed to unmarshal %s policy", policy.Name), err)
|
2020-07-29 21:41:58 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
return &p, nil
|
|
|
|
}
|
2020-08-19 10:21:32 +05:30
|
|
|
|
2020-08-31 17:33:01 +05:30
|
|
|
// GetCRDs - Extracting the crds from multiple YAML
|
|
|
|
func GetCRDs(paths []string) (unstructuredCrds []*unstructured.Unstructured, err error) {
|
2020-08-31 18:43:58 +05:30
|
|
|
unstructuredCrds = make([]*unstructured.Unstructured, 0)
|
2020-08-31 17:33:01 +05:30
|
|
|
for _, path := range paths {
|
|
|
|
path = filepath.Clean(path)
|
|
|
|
|
|
|
|
fileDesc, err := os.Stat(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if fileDesc.IsDir() {
|
|
|
|
files, err := ioutil.ReadDir(path)
|
|
|
|
if err != nil {
|
2020-11-17 13:07:30 -08:00
|
|
|
return nil, sanitizederror.NewWithError(fmt.Sprintf("failed to parse %v", path), err)
|
2020-08-31 17:33:01 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
listOfFiles := make([]string, 0)
|
|
|
|
for _, file := range files {
|
|
|
|
listOfFiles = append(listOfFiles, filepath.Join(path, file.Name()))
|
|
|
|
}
|
|
|
|
|
|
|
|
policiesFromDir, err := GetCRDs(listOfFiles)
|
|
|
|
if err != nil {
|
2020-11-17 13:07:30 -08:00
|
|
|
return nil, sanitizederror.NewWithError(fmt.Sprintf("failed to extract crds from %v", listOfFiles), err)
|
2020-08-31 17:33:01 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
unstructuredCrds = append(unstructuredCrds, policiesFromDir...)
|
|
|
|
} else {
|
|
|
|
getCRDs, err := GetCRD(path)
|
|
|
|
if err != nil {
|
2020-10-10 03:49:28 +05:30
|
|
|
fmt.Printf("\nError: failed to extract crds from %s. \nCause: %s\n", path, err)
|
2020-08-31 19:32:00 +05:30
|
|
|
os.Exit(2)
|
2020-08-31 17:33:01 +05:30
|
|
|
}
|
|
|
|
unstructuredCrds = append(unstructuredCrds, getCRDs...)
|
|
|
|
}
|
|
|
|
}
|
2020-08-31 18:43:58 +05:30
|
|
|
return unstructuredCrds, nil
|
2020-08-31 17:33:01 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
// GetCRD - Extracts crds from a YAML
|
|
|
|
func GetCRD(path string) (unstructuredCrds []*unstructured.Unstructured, err error) {
|
2020-08-19 10:21:32 +05:30
|
|
|
path = filepath.Clean(path)
|
2020-08-31 18:43:58 +05:30
|
|
|
unstructuredCrds = make([]*unstructured.Unstructured, 0)
|
2020-08-31 17:33:01 +05:30
|
|
|
yamlbytes, err := ioutil.ReadFile(path)
|
2020-08-19 10:21:32 +05:30
|
|
|
if err != nil {
|
2020-08-30 20:43:05 +05:30
|
|
|
return nil, err
|
2020-08-19 10:21:32 +05:30
|
|
|
}
|
|
|
|
|
2020-08-31 17:33:01 +05:30
|
|
|
buf := bytes.NewBuffer(yamlbytes)
|
|
|
|
reader := yaml.NewYAMLReader(bufio.NewReader(buf))
|
2020-08-19 10:21:32 +05:30
|
|
|
|
2020-08-31 17:33:01 +05:30
|
|
|
for {
|
|
|
|
// Read one YAML document at a time, until io.EOF is returned
|
|
|
|
b, err := reader.Read()
|
|
|
|
if err == io.EOF || len(b) == 0 {
|
|
|
|
break
|
|
|
|
} else if err != nil {
|
2020-10-10 03:49:28 +05:30
|
|
|
fmt.Printf("\nError: unable to read crd from %s. Cause: %s\n", path, err)
|
|
|
|
os.Exit(2)
|
2020-08-31 17:33:01 +05:30
|
|
|
}
|
2020-08-31 18:43:58 +05:30
|
|
|
var u unstructured.Unstructured
|
2020-08-31 17:33:01 +05:30
|
|
|
err = yaml_v2.Unmarshal(b, &u)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
unstructuredCrds = append(unstructuredCrds, &u)
|
2020-08-19 10:21:32 +05:30
|
|
|
}
|
2020-08-31 18:43:58 +05:30
|
|
|
|
2020-08-31 17:33:01 +05:30
|
|
|
return unstructuredCrds, nil
|
2020-08-19 10:21:32 +05:30
|
|
|
}
|
2020-10-07 06:20:53 +05:30
|
|
|
|
|
|
|
// IsInputFromPipe - check if input is passed using pipe
|
|
|
|
func IsInputFromPipe() bool {
|
|
|
|
fileInfo, _ := os.Stdin.Stat()
|
|
|
|
return fileInfo.Mode()&os.ModeCharDevice == 0
|
|
|
|
}
|