diff --git a/go.mod b/go.mod index 0cf02ba2..3fbf8df6 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,8 @@ replace ( k8s.io/cli-runtime => k8s.io/cli-runtime v0.26.4 k8s.io/client-go => k8s.io/client-go v0.26.4 k8s.io/component-base => k8s.io/component-base v0.26.4 + sigs.k8s.io/kustomize/api => sigs.k8s.io/kustomize/api v0.12.1 + sigs.k8s.io/kustomize/kyaml => sigs.k8s.io/kustomize/kyaml v0.13.9 ) require ( @@ -41,7 +43,7 @@ require ( ) require ( - github.com/accuknox/auto-policy-discovery/src v0.0.0-20230427074403-5d03ee9f3358 + github.com/accuknox/auto-policy-discovery/src v0.0.0-20230605060121-abe7d0b9770d github.com/cavaliergopher/grab/v3 v3.0.1 github.com/charmbracelet/bubbles v0.15.0 github.com/charmbracelet/bubbletea v0.23.2 @@ -62,7 +64,7 @@ require ( k8s.io/api v0.27.1 k8s.io/apiextensions-apiserver v0.27.1 k8s.io/apimachinery v0.27.1 - k8s.io/cli-runtime v0.26.4 + k8s.io/cli-runtime v0.27.1 k8s.io/client-go v0.27.1 k8s.io/utils v0.0.0-20230505201702-9f6742963106 ) @@ -173,7 +175,6 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect - github.com/golang/glog v1.1.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -185,7 +186,7 @@ require ( github.com/google/go-github/v45 v45.2.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect + github.com/google/pprof v0.0.0-20211008130755-947d60d73cc0 // indirect github.com/google/s2a-go v0.1.2 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/trillian v1.5.1 // indirect @@ -268,7 +269,7 @@ require ( github.com/shibumi/go-pathspec v1.3.0 // indirect github.com/shirou/gopsutil/v3 v3.23.3 // indirect github.com/sigstore/cosign v1.13.1 // indirect - github.com/sigstore/fulcio v1.0.0 // indirect + github.com/sigstore/fulcio v1.1.0 // indirect github.com/sigstore/k8s-manifest-sigstore v0.4.4 // indirect github.com/sigstore/rekor v1.0.1 // indirect github.com/sigstore/sigstore v1.5.2 // indirect @@ -330,12 +331,12 @@ require ( k8s.io/component-base v0.27.1 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect - k8s.io/kubectl v0.26.4 // indirect - k8s.io/pod-security-admission v0.26.4 // indirect + k8s.io/kubectl v0.27.1 // indirect + k8s.io/pod-security-admission v0.27.1 // indirect sigs.k8s.io/controller-runtime v0.14.6 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/kustomize/api v0.12.1 // indirect - sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect + sigs.k8s.io/kustomize/api v0.13.2 // indirect + sigs.k8s.io/kustomize/kyaml v0.14.1 // indirect sigs.k8s.io/release-utils v0.7.3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect ) diff --git a/go.sum b/go.sum index 95726e52..3e4b0d47 100644 --- a/go.sum +++ b/go.sum @@ -128,8 +128,8 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrU github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY+9ef8E= github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/accuknox/auto-policy-discovery/src v0.0.0-20230427074403-5d03ee9f3358 h1:X8XLPGOoQk0ZBxqkjcpZ8aD4Ez18HW6q3zjj7bjo/7s= -github.com/accuknox/auto-policy-discovery/src v0.0.0-20230427074403-5d03ee9f3358/go.mod h1:q1d217En1e+b4ZVx8Royu7kUhku5FP6hGU6WJqI2zQY= +github.com/accuknox/auto-policy-discovery/src v0.0.0-20230605060121-abe7d0b9770d h1:LeEH2EzbgfpdmsehXMsGDTv5bvM8egIf0A54s7xroWE= +github.com/accuknox/auto-policy-discovery/src v0.0.0-20230605060121-abe7d0b9770d/go.mod h1:GRLEabyrosgCvcFRoAPcKcHyv5XUKLP7gWxMuF1ip4g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= @@ -626,7 +626,6 @@ github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw= -github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -744,8 +743,9 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20211008130755-947d60d73cc0 h1:zHs+jv3LO743/zFGcByu2KmpbliCU2AhjcGgrdTwSG4= +github.com/google/pprof v0.0.0-20211008130755-947d60d73cc0/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.2 h1:WVtYAYuYxKeYajAmThMRYWP6K3wXkcqbGHeUgeubUHY= github.com/google/s2a-go v0.1.2/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM= @@ -849,6 +849,7 @@ github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63 github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -1359,8 +1360,8 @@ github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOms github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sigstore/cosign v1.13.1 h1:+5oF8jisEcDw2TuXxCADC1u5//HfdnJhGbpv9Isiwu4= github.com/sigstore/cosign v1.13.1/go.mod h1:PlfJODkovUOKsLrGI7Su57Ie/Eb/Ks7hRHw3tn5hQS4= -github.com/sigstore/fulcio v1.0.0 h1:hBZW6qg9GXTtCX8jOg1hmyjYLrmsEKZGeMwAbW3XNEg= -github.com/sigstore/fulcio v1.0.0/go.mod h1:j4MzLxX/Be0rHYh3JF2dgMorkWGzEMHBqIHwFU8I/Rw= +github.com/sigstore/fulcio v1.1.0 h1:mzzJ05Ccu8Y2inyioklNvc8MpzlGHxu8YqNeTm0dHfU= +github.com/sigstore/fulcio v1.1.0/go.mod h1:zv1ZQTXZbUwQdRwajlQksc34pRas+2aZYpIZoQBNev8= github.com/sigstore/k8s-manifest-sigstore v0.4.4 h1:7ae///+L0nqFBsRwr26anJc5bnJxoDXCGhVHXQB1GSo= github.com/sigstore/k8s-manifest-sigstore v0.4.4/go.mod h1:PJSnSF8Nh7PqV4xhU3BRweqEFwGvJq6Xi2B5yhYjxb0= github.com/sigstore/rekor v1.0.1 h1:rcESXSNkAPRWFYZel9rarspdvneET60F2ngNkadi89c= @@ -2356,10 +2357,10 @@ k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iL k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= -k8s.io/kubectl v0.26.4 h1:A0Oa0u/po4KxXnXsNCOwLojAe9cQR3TJNJabEIf7U1w= -k8s.io/kubectl v0.26.4/go.mod h1:cWtp/+I4p+h5En3s2zO1zCry9v3/6h37EQ2tF3jNRnM= -k8s.io/pod-security-admission v0.26.4 h1:BfjUrTdJ3jvOK4UW+nshcD7q+/AGLb1gSoqQKbUALnk= -k8s.io/pod-security-admission v0.26.4/go.mod h1:WjQF+oeXfuXz3iqYc/0XaBAoTOwZ5woLXOC1xswWpa0= +k8s.io/kubectl v0.27.1 h1:9T5c5KdpburYiW8XKQSH0Uly1kMNE90aGSnbYUZNdcA= +k8s.io/kubectl v0.27.1/go.mod h1:QsAkSmrRsKTPlAFzF8kODGDl4p35BIwQnc9XFhkcsy8= +k8s.io/pod-security-admission v0.27.1 h1:if4d1zzcpNOZNvljvJ0nTCshFPUmnkIsy7KYJg7FP08= +k8s.io/pod-security-admission v0.27.1/go.mod h1:dICAHAC4DE0q+yrGuPJ8kuJ5dEsWtqNkclzCDckHj/s= k8s.io/utils v0.0.0-20210111153108-fddb29f9d009/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= diff --git a/recommend/admissionControllerPolicy.go b/recommend/admissionControllerPolicy.go index 7e152d80..b1b05c28 100644 --- a/recommend/admissionControllerPolicy.go +++ b/recommend/admissionControllerPolicy.go @@ -3,12 +3,14 @@ package recommend import ( "context" "errors" + "fmt" "os" "strconv" "strings" "github.com/accuknox/auto-policy-discovery/src/libs" "github.com/accuknox/auto-policy-discovery/src/protobuf/v1/worker" + "github.com/accuknox/auto-policy-discovery/src/types" "github.com/clarketm/json" "github.com/fatih/color" "github.com/kubearmor/kubearmor-client/k8s" @@ -18,6 +20,7 @@ import ( "golang.org/x/exp/slices" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + "sigs.k8s.io/yaml" ) var connection *grpc.ClientConn @@ -74,7 +77,7 @@ func recommendAdmissionControllerPolicies(img ImageInfo) error { resp, err := client.Convert(context.Background(), &worker.WorkerRequest{ Labels: labels, Namespace: img.Namespace, - Policytype: "AdmissionControllerPolicy", + Policytype: types.PolicyTypeAdmissionController, }) if err != nil { color.Red(err.Error()) @@ -82,21 +85,65 @@ func recommendAdmissionControllerPolicies(img ImageInfo) error { } if resp.AdmissionControllerPolicy != nil { for _, policy := range resp.AdmissionControllerPolicy { - var kyvernoPolicy kyvernov1.Policy - err := json.Unmarshal(policy.Data, &kyvernoPolicy) + var kyvernoPolicyInterface kyvernov1.PolicyInterface + kyvernoPolicyInterface, err = getKyvernoPolicy(policy.Data) if err != nil { return err } - if namespaceMatches(kyvernoPolicy.Namespace) && matchAdmissionControllerPolicyTags(&kyvernoPolicy) { - img.writeAdmissionControllerPolicy(kyvernoPolicy) + if namespaceMatches(kyvernoPolicyInterface.GetNamespace()) && matchAdmissionControllerPolicyTags(kyvernoPolicyInterface.GetAnnotations()) { + img.writeAdmissionControllerPolicy(kyvernoPolicyInterface) } } } return nil } -func matchAdmissionControllerPolicyTags(policy *kyvernov1.Policy) bool { - policyTags := strings.Split(policy.Annotations["recommended-policies.kubearmor.io/tags"], ",") +func recommendGenericAdmissionControllerPolicies() error { + client := worker.NewWorkerClient(connection) + resp, err := client.Convert(context.Background(), &worker.WorkerRequest{ + Policytype: types.PolicyTypeAdmissionControllerGeneric, + }) + if err != nil { + color.Red(err.Error()) + return err + } + if resp.AdmissionControllerPolicy != nil { + reportStarted := false + for _, policy := range resp.AdmissionControllerPolicy { + var kyvernoPolicyInterface kyvernov1.PolicyInterface + kyvernoPolicyInterface, err = getKyvernoPolicy(policy.Data) + if err != nil { + if reportStarted { + err := ReportSectEnd() + if err != nil { + return err + } + } + return err + } + if matchAdmissionControllerPolicyTags(kyvernoPolicyInterface.GetAnnotations()) { + if !reportStarted { + err := ReportStartGenericAdmissionControllerPolicies() + if err != nil { + return err + } + reportStarted = true + } + writeGenericAdmissionControllerPolicy(kyvernoPolicyInterface) + } + } + if reportStarted { + err := ReportSectEnd() + if err != nil { + return err + } + } + } + return nil +} + +func matchAdmissionControllerPolicyTags(policyAnnotations map[string]string) bool { + policyTags := strings.Split(policyAnnotations[types.RecommendedPolicyTagsAnnotation], ",") if len(options.Tags) <= 0 { return true } @@ -111,3 +158,55 @@ func matchAdmissionControllerPolicyTags(policy *kyvernov1.Policy) bool { func namespaceMatches(policyNamespace string) bool { return options.Namespace == "" || options.Namespace == policyNamespace } + +func getKyvernoPolicy(policyYaml []byte) (kyvernov1.PolicyInterface, error) { + var policy map[string]interface{} + err := yaml.Unmarshal(policyYaml, &policy) + if err != nil { + return nil, err + } + policyKind := policy["kind"].(string) + + var kyvernoPolicyInterface kyvernov1.PolicyInterface + switch policyKind { + case "Policy": + var kyvernoPolicy kyvernov1.Policy + err = yaml.Unmarshal(policyYaml, &kyvernoPolicy) + if err != nil { + return nil, err + } + kyvernoPolicyInterface = &kyvernoPolicy + case "ClusterPolicy": + var kyvernoClusterPolicy kyvernov1.ClusterPolicy + err = yaml.Unmarshal(policyYaml, &kyvernoClusterPolicy) + if err != nil { + return nil, err + } + kyvernoPolicyInterface = &kyvernoClusterPolicy + default: + return nil, fmt.Errorf("unexpected policy kind: %s", policyKind) + } + return kyvernoPolicyInterface, nil +} + +func convertKyvernoPolicyInterfaceToJSON(policyInterface kyvernov1.PolicyInterface) ([]byte, error) { + var jsonBytes []byte + var err error + switch policyInterface.(type) { + case *kyvernov1.ClusterPolicy: + kyvernoClusterPolicy := policyInterface.(*kyvernov1.ClusterPolicy) + jsonBytes, err = json.Marshal(*kyvernoClusterPolicy) + if err != nil { + log.WithError(err).Error("json marshal failed") + return nil, err + } + case *kyvernov1.Policy: + kyvernoPolicy := policyInterface.(*kyvernov1.Policy) + jsonBytes, err = json.Marshal(*kyvernoPolicy) + if err != nil { + log.WithError(err).Error("json marshal failed") + return nil, err + } + } + return jsonBytes, nil +} diff --git a/recommend/html/css/main.css b/recommend/html/css/main.css index b3c45818..b9fd2483 100644 --- a/recommend/html/css/main.css +++ b/recommend/html/css/main.css @@ -99,6 +99,11 @@ body { position: fixed; overflow: hidden; } +.v38_7030 { + color: #0b4296; + font-family: 'Red Hat Display'; + font-weight: Bold; +} #wrapper { text-align: center; overflow: hidden; diff --git a/recommend/html/section.html b/recommend/html/section.html index 551474d3..ea347d79 100644 --- a/recommend/html/section.html +++ b/recommend/html/section.html @@ -2,14 +2,18 @@
- - {{range .ImgInfo}} - - - - - {{end}} -
{{.Key}}: {{.Val}}
+ {{if .GenericAdmissionControllerPolicy}} +

Generic Kyverno Policies

+ {{else}} + + {{range .ImgInfo}} + + + + + {{end}} +
{{.Key}}: {{.Val}}
+ {{end}}

diff --git a/recommend/imageHandler.go b/recommend/imageHandler.go index 2a91fcda..4d102244 100644 --- a/recommend/imageHandler.go +++ b/recommend/imageHandler.go @@ -542,7 +542,11 @@ func imageHandler(namespace, deployment string, labels LabelMap, imageName strin if !containsKyvernoPolicy && !containsKubeArmorPolicy { return fmt.Errorf("policy type not supported: %v", options.Policy) } - _ = ReportSectEnd(&img) + err := ReportSectEnd() + if err != nil { + log.WithError(err).Error("report section end failed") + return err + } return nil } diff --git a/recommend/policy.go b/recommend/policy.go index 3755b418..84f0e09b 100644 --- a/recommend/policy.go +++ b/recommend/policy.go @@ -136,30 +136,96 @@ func (img *ImageInfo) writePolicyFile(ms MatchSpec) { } -func (img *ImageInfo) writeAdmissionControllerPolicy(policy kyvernov1.Policy) { - policyName := strings.ReplaceAll(policy.Name, img.Name+"-", "") +func (img *ImageInfo) writeAdmissionControllerPolicy(policyInterface kyvernov1.PolicyInterface) { + policyName := strings.ReplaceAll(policyInterface.GetName(), img.Name+"-", "") outFile := img.getPolicyFile(policyName) - _ = os.MkdirAll(filepath.Dir(outFile), 0750) + err := os.MkdirAll(filepath.Dir(outFile), 0750) + if err != nil { + log.WithError(err).Error("create dir failed") + return + } f, err := os.Create(filepath.Clean(outFile)) if err != nil { log.WithError(err).Error(fmt.Sprintf("create file %s failed", outFile)) + return + } + var jsonBytes []byte + var yamlBytes []byte + jsonBytes, err = convertKyvernoPolicyInterfaceToJSON(policyInterface) + if err != nil { + log.WithError(err).Error("json marshal failed") + return + } + yamlBytes, err = yaml.JSONToYAML(jsonBytes) + if err != nil { + log.WithError(err).Error("yaml marshal failed") + return + } + if _, err := f.WriteString(string(yamlBytes)); err != nil { + log.WithError(err).Error("WriteString failed") + return + } + if err := f.Sync(); err != nil { + log.WithError(err).Error("file sync failed") + return + } + if err := f.Close(); err != nil { + log.WithError(err).Error("file close failed") + return + } + err = ReportAdmissionControllerRecord(outFile, string(policyInterface.GetSpec().ValidationFailureAction), policyInterface.GetAnnotations()) + if err != nil { + log.WithError(err).Error("report admission controller record failed") + } else { + color.Green("created policy %s ...", outFile) + } +} +func writeGenericAdmissionControllerPolicy(policyInterface kyvernov1.PolicyInterface) { + policyName := policyInterface.GetName() + outFile := filepath.Join(options.OutDir, "genericKyvernoPolicies", policyName+".yaml") + err := os.MkdirAll(filepath.Dir(outFile), 0750) + if err != nil { + log.WithError(err).Error("create dir failed") + return } - arr, _ := json.Marshal(policy) - yamlArr, _ := yaml.JSONToYAML(arr) - if _, err := f.WriteString(string(yamlArr)); err != nil { + f, err := os.Create(filepath.Clean(outFile)) + if err != nil { + log.WithError(err).Error(fmt.Sprintf("create file %s failed", outFile)) + return + } + var jsonBytes []byte + var yamlBytes []byte + jsonBytes, err = convertKyvernoPolicyInterfaceToJSON(policyInterface) + if err != nil { + log.WithError(err).Error("json marshal failed") + return + } + yamlBytes, err = yaml.JSONToYAML(jsonBytes) + if err != nil { + log.WithError(err).Error("yaml marshal failed") + return + } + if _, err := f.WriteString(string(yamlBytes)); err != nil { log.WithError(err).Error("WriteString failed") + return } if err := f.Sync(); err != nil { log.WithError(err).Error("file sync failed") + return } if err := f.Close(); err != nil { log.WithError(err).Error("file close failed") + return + } + err = ReportAdmissionControllerRecord(outFile, string(policyInterface.GetSpec().ValidationFailureAction), policyInterface.GetAnnotations()) + if err != nil { + log.WithError(err).Error("report admission controller record failed") + } else { + color.Green("created policy %s ...", outFile) } - _ = ReportAdmissionControllerRecord(outFile, string(policy.Spec.ValidationFailureAction), policy.Annotations) - color.Green("created policy %s ...", outFile) } func (img *ImageInfo) getPolicyFromImageInfo() { diff --git a/recommend/policyTemplates.go b/recommend/policyTemplates.go index b59527ec..b67e7f38 100644 --- a/recommend/policyTemplates.go +++ b/recommend/policyTemplates.go @@ -17,7 +17,6 @@ import ( "github.com/google/go-github/github" kg "github.com/kubearmor/KubeArmor/KubeArmor/log" pol "github.com/kubearmor/KubeArmor/pkg/KubeArmorController/api/security.kubearmor.com/v1" - kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" log "github.com/sirupsen/logrus" "sigs.k8s.io/yaml" ) @@ -209,12 +208,9 @@ func updatePolicyRules(filePath string) error { } apiVersion := policy["apiVersion"].(string) if strings.Contains(apiVersion, "kyverno") { - var kyvernoPolicy kyvernov1.Policy - err = yaml.Unmarshal(newYaml, &kyvernoPolicy) - if err != nil { - return err - } - ms.KyvernoPolicySpec = &kyvernoPolicy.Spec + // No need to add Kyverno policies to 'rules.yaml' + // Kyverno policies are fetched from discovery engine + continue } else if strings.Contains(apiVersion, "kubearmor") { var kubeArmorPolicy pol.KubeArmorPolicy err = yaml.Unmarshal(newYaml, &kubeArmorPolicy) diff --git a/recommend/recommend.go b/recommend/recommend.go index ea78d38b..e6675562 100644 --- a/recommend/recommend.go +++ b/recommend/recommend.go @@ -165,6 +165,20 @@ func Recommend(c *k8s.Client, o Options) error { } } + recommendKyvernoPolicies := false + for _, policy := range options.Policy { + if policy == KyvernoPolicy { + recommendKyvernoPolicies = true + } + } + + if recommendKyvernoPolicies { + err = recommendGenericAdmissionControllerPolicies() + if err != nil { + log.Error(err) + } + } + finalReport() return nil } diff --git a/recommend/report.go b/recommend/report.go index 17d2e779..1e52f28e 100644 --- a/recommend/report.go +++ b/recommend/report.go @@ -15,6 +15,16 @@ for every image { for every policy { ReportRecord() } + for every dynamic_admission_controller_policy { + ReportAdmissionControllerRecord() + } + ReportSectEnd() +} +if recommend_generic_admission_controller_policies { + ReportStartGenericAdmissionControllerPolicies() + for every generic_admission_controller_policy { + ReportAdmissionControllerRecord() + } ReportSectEnd() } ReportRender() @@ -46,6 +56,17 @@ func ReportStart(img *ImageInfo) error { return errors.New("unknown reporter type") } +// ReportStartGenericAdmissionControllerPolicies called once per generic admission controller policy at the start +func ReportStartGenericAdmissionControllerPolicies() error { + switch v := Handler.(type) { + case HTMLReport: + return v.StartGenericAdmissionControllerPolicies() + case TextReport: + return v.StartGenericAdmissionControllerPolicies() + } + return errors.New("unknown reporter type") +} + // ReportRecord called once per policy func ReportRecord(ms MatchSpec, policyName string) error { switch v := Handler.(type) { @@ -58,23 +79,23 @@ func ReportRecord(ms MatchSpec, policyName string) error { } // ReportAdmissionControllerRecord called once per admission controller policy -func ReportAdmissionControllerRecord(policyName, action string, annotations map[string]string) error { +func ReportAdmissionControllerRecord(policyFilePath, action string, annotations map[string]string) error { switch v := Handler.(type) { case HTMLReport: - return v.RecordAdmissionController(policyName, action, annotations) + return v.RecordAdmissionController(policyFilePath, action, annotations) case TextReport: - return v.RecordAdmissionController(policyName, action, annotations) + return v.RecordAdmissionController(policyFilePath, action, annotations) } return errors.New("unknown reporter type") } // ReportSectEnd called once per container image at the end -func ReportSectEnd(img *ImageInfo) error { +func ReportSectEnd() error { switch v := Handler.(type) { case HTMLReport: - return v.SectionEnd(img) + return v.SectionEnd() case TextReport: - return v.SectionEnd(img) + return v.SectionEnd() } return errors.New("unknown reporter type") } diff --git a/recommend/report_html.go b/recommend/report_html.go index efc46078..f5bdf371 100644 --- a/recommend/report_html.go +++ b/recommend/report_html.go @@ -13,6 +13,7 @@ import ( "strings" "time" + "github.com/accuknox/auto-policy-discovery/src/types" log "github.com/sirupsen/logrus" "golang.org/x/text/cases" "golang.org/x/text/language" @@ -72,8 +73,9 @@ type HeaderInfo struct { // SectionInfo Section information type SectionInfo struct { - HdrCols []Col - ImgInfo []Info + HdrCols []Col + ImgInfo []Info + GenericAdmissionControllerPolicy bool } // NewHTMLReport instantiation on new html report @@ -137,6 +139,21 @@ func (r HTMLReport) Start(img *ImageInfo) error { return nil } +func (r HTMLReport) StartGenericAdmissionControllerPolicies() error { + secInfo := SectionInfo{ + HdrCols: []Col{ + {Name: "POLICY"}, + {Name: "DESCRIPTION"}, + {Name: "SEVERITY"}, + {Name: "ACTION"}, + {Name: "TAGS"}, + }, + GenericAdmissionControllerPolicy: true, + } + _ = r.section.Execute(r.outString, secInfo) + return nil +} + // RecordInfo new row information in table type RecordInfo struct { RowID string @@ -174,34 +191,33 @@ func (r HTMLReport) Record(ms MatchSpec, policyName string) error { } // RecordAdmissionController addition of new HTML table row for admission controller policies -func (r HTMLReport) RecordAdmissionController(policyName, action string, annotations map[string]string) error { +func (r HTMLReport) RecordAdmissionController(policyFilePath, action string, annotations map[string]string) error { *r.RecordCnt = *r.RecordCnt + 1 - policy, err := os.ReadFile(filepath.Clean(policyName)) + policy, err := os.ReadFile(filepath.Clean(policyFilePath)) if err != nil { - log.WithError(err).Error(fmt.Sprintf("failed to read policy %s", policyName)) + log.WithError(err).Error(fmt.Sprintf("failed to read policy %s", policyFilePath)) } - policyName = policyName[strings.LastIndex(policyName, "/")+1:] + policyFilePath = policyFilePath[strings.LastIndex(policyFilePath, "/")+1:] reci := RecordInfo{ RowID: fmt.Sprintf("row%d", *r.RecordCnt), Rec: []Col{ - {Name: policyName}, - {Name: annotations["recommended-policies.kubearmor.io/description"]}, + {Name: policyFilePath}, + {Name: annotations[types.RecommendedPolicyTitleAnnotation]}, {Name: "-"}, {Name: cases.Title(language.English).String(action)}, - {Name: strings.Join(strings.Split(annotations["recommended-policies.kubearmor.io/tags"], ",")[:], "\n")}, + {Name: strings.Join(strings.Split(annotations[types.RecommendedPolicyTagsAnnotation], ",")[:], "\n")}, }, Policy: string(policy), PolicyType: "Kyverno Policy", - Description: annotations["recommended-policies.kubearmor.io/description-detailed"], + Description: annotations[types.RecommendedPolicyDescriptionAnnotation], // TODO: Figure out how to get the references, adding them to annotations would make them too long Refs: []Ref{}, } - _ = r.record.Execute(r.outString, reci) - return nil + return r.record.Execute(r.outString, reci) } // SectionEnd end of section of the HTML table -func (r HTMLReport) SectionEnd(img *ImageInfo) error { +func (r HTMLReport) SectionEnd() error { return r.sectend.Execute(r.outString, nil) } diff --git a/recommend/report_text.go b/recommend/report_text.go index 58e52099..15dc3118 100644 --- a/recommend/report_text.go +++ b/recommend/report_text.go @@ -9,6 +9,7 @@ import ( "os" "strings" + "github.com/accuknox/auto-policy-discovery/src/types" "github.com/olekukonko/tablewriter" log "github.com/sirupsen/logrus" ) @@ -54,8 +55,20 @@ func (r TextReport) Start(img *ImageInfo) error { return nil } +func (r TextReport) StartGenericAdmissionControllerPolicies() error { + t := tablewriter.NewWriter(r.outString) + t.SetBorder(false) + t.Rich([]string{"Generic Kyverno Policies"}, []tablewriter.Colors{{tablewriter.Bold}}) + t.Render() + + r.table.SetHeader([]string{"Policy", "Short Desc", "Severity", "Action", "Tags"}) + r.table.SetAlignment(tablewriter.ALIGN_LEFT) + r.table.SetRowLine(true) + return nil +} + // SectionEnd end of section of the text table -func (r TextReport) SectionEnd(img *ImageInfo) error { +func (r TextReport) SectionEnd() error { r.table.Render() r.table.ClearRows() r.outString.WriteString("\n") @@ -76,14 +89,14 @@ func (r TextReport) Record(ms MatchSpec, policyName string) error { } // RecordAdmissionController adds new row to table for admission controller policies -func (r TextReport) RecordAdmissionController(policyName, action string, annotations map[string]string) error { +func (r TextReport) RecordAdmissionController(policyFilePath, action string, annotations map[string]string) error { var rec []string - policyName = policyName[strings.LastIndex(policyName, "/")+1:] - rec = append(rec, wrapPolicyName(policyName, 35)) - rec = append(rec, annotations["recommended-policies.kubearmor.io/description"]) + policyFilePath = policyFilePath[strings.LastIndex(policyFilePath, "/")+1:] + rec = append(rec, wrapPolicyName(policyFilePath, 35)) + rec = append(rec, annotations[types.RecommendedPolicyTitleAnnotation]) rec = append(rec, "-") rec = append(rec, action) - rec = append(rec, strings.Join(strings.Split(annotations["recommended-policies.kubearmor.io/tags"], ",")[:], "\n")) + rec = append(rec, strings.Join(strings.Split(annotations[types.RecommendedPolicyTagsAnnotation], ",")[:], "\n")) r.table.Append(rec) return nil }