2019-08-13 13:15:04 -07:00
package policy
import (
2021-03-25 12:28:03 -07:00
"context"
2019-08-13 13:15:04 -07:00
"fmt"
2021-03-25 12:28:03 -07:00
"strings"
"time"
2019-08-13 13:15:04 -07:00
2020-03-17 16:25:34 -07:00
"github.com/go-logr/logr"
2021-08-21 19:35:17 +02:00
v1alpha2 "github.com/kyverno/kyverno/pkg/api/policyreport/v1alpha2"
2021-03-25 12:28:03 -07:00
kyvernoclient "github.com/kyverno/kyverno/pkg/client/clientset/versioned"
2021-08-21 19:35:17 +02:00
changerequestlister "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1alpha2"
policyreportlister "github.com/kyverno/kyverno/pkg/client/listers/policyreport/v1alpha2"
2021-07-14 09:57:16 -07:00
"github.com/kyverno/kyverno/pkg/config"
2020-10-07 11:12:31 -07:00
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/event"
2020-11-09 11:26:12 -08:00
"github.com/kyverno/kyverno/pkg/policyreport"
2021-03-25 12:28:03 -07:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
2019-08-13 13:15:04 -07:00
)
2021-06-30 00:43:11 +03:00
func ( pc * PolicyController ) report ( engineResponses [ ] * response . EngineResponse , logger logr . Logger ) {
eventInfos := generateFailEvents ( logger , engineResponses )
2019-11-12 14:41:29 -08:00
pc . eventGen . Add ( eventInfos ... )
2020-12-21 11:04:19 -08:00
2021-06-30 00:43:11 +03:00
if pc . configHandler . GetGenerateSuccessEvents ( ) {
successEventInfos := generateSuccessEvents ( logger , engineResponses )
pc . eventGen . Add ( successEventInfos ... )
}
2020-11-09 11:26:12 -08:00
pvInfos := policyreport . GeneratePRsFromEngineResponse ( engineResponses , logger )
2020-02-26 00:26:09 +05:30
2020-12-21 11:04:19 -08:00
// as engineResponses holds the results for all matched resources in one namespace
// we can merge pvInfos into a single object to reduce update frequency (throttling request) on RCR
info := mergePvInfos ( pvInfos )
pc . prGenerator . Add ( info )
logger . V ( 4 ) . Info ( "added a request to RCR generator" , "key" , info . ToKey ( ) )
2019-11-12 14:41:29 -08:00
}
2021-03-25 12:28:03 -07:00
// forceReconciliation forces a background scan by adding all policies to the workqueue
func ( pc * PolicyController ) forceReconciliation ( reconcileCh <- chan bool , stopCh <- chan struct { } ) {
logger := pc . log . WithName ( "forceReconciliation" )
ticker := time . NewTicker ( pc . reconcilePeriod )
for {
select {
case <- ticker . C :
logger . Info ( "performing the background scan" , "scan interval" , pc . reconcilePeriod . String ( ) )
2021-07-14 09:57:16 -07:00
if err := pc . policyReportEraser . CleanupReportChangeRequests ( cleanupReportChangeRequests ) ; err != nil {
logger . Error ( err , "failed to cleanup report change requests" )
}
2021-03-25 12:28:03 -07:00
if err := pc . policyReportEraser . EraseResultsEntries ( eraseResultsEntries ) ; err != nil {
logger . Error ( err , "continue reconciling policy reports" )
}
pc . requeuePolicies ( )
case erase := <- reconcileCh :
logger . Info ( "received the reconcile signal, reconciling policy report" )
2021-07-14 09:57:16 -07:00
if err := pc . policyReportEraser . CleanupReportChangeRequests ( cleanupReportChangeRequests ) ; err != nil {
logger . Error ( err , "failed to cleanup report change requests" )
}
2021-03-25 12:28:03 -07:00
if erase {
if err := pc . policyReportEraser . EraseResultsEntries ( eraseResultsEntries ) ; err != nil {
logger . Error ( err , "continue reconciling policy reports" )
}
}
pc . requeuePolicies ( )
case <- stopCh :
return
}
}
}
2021-07-14 09:57:16 -07:00
func cleanupReportChangeRequests ( pclient * kyvernoclient . Clientset , rcrLister changerequestlister . ReportChangeRequestLister , crcrLister changerequestlister . ClusterReportChangeRequestLister ) error {
var errors [ ] string
var gracePeriod int64 = 0
deleteOptions := metav1 . DeleteOptions { GracePeriodSeconds : & gracePeriod }
2021-08-21 19:35:17 +02:00
err := pclient . KyvernoV1alpha2 ( ) . ClusterReportChangeRequests ( ) . DeleteCollection ( context . TODO ( ) , deleteOptions , metav1 . ListOptions { } )
2021-07-14 09:57:16 -07:00
if err != nil {
errors = append ( errors , err . Error ( ) )
}
2021-08-21 19:35:17 +02:00
err = pclient . KyvernoV1alpha2 ( ) . ReportChangeRequests ( config . KyvernoNamespace ) . DeleteCollection ( context . TODO ( ) , deleteOptions , metav1 . ListOptions { } )
2021-07-14 09:57:16 -07:00
if err != nil {
errors = append ( errors , err . Error ( ) )
}
if len ( errors ) == 0 {
return nil
}
return fmt . Errorf ( "%v" , strings . Join ( errors , ";" ) )
}
2021-03-25 12:28:03 -07:00
func eraseResultsEntries ( pclient * kyvernoclient . Clientset , reportLister policyreportlister . PolicyReportLister , clusterReportLister policyreportlister . ClusterPolicyReportLister ) error {
var errors [ ] string
if polrs , err := reportLister . List ( labels . Everything ( ) ) ; err != nil {
errors = append ( errors , err . Error ( ) )
} else {
for _ , polr := range polrs {
2021-08-21 19:35:17 +02:00
polr . Results = [ ] * v1alpha2 . PolicyReportResult { }
polr . Summary = v1alpha2 . PolicyReportSummary { }
if _ , err = pclient . Wgpolicyk8sV1alpha2 ( ) . PolicyReports ( polr . GetNamespace ( ) ) . Update ( context . TODO ( ) , polr , metav1 . UpdateOptions { } ) ; err != nil {
2021-03-25 12:28:03 -07:00
errors = append ( errors , fmt . Sprintf ( "%s/%s/%s: %v" , polr . Kind , polr . Namespace , polr . Name , err ) )
}
}
}
if cpolrs , err := clusterReportLister . List ( labels . Everything ( ) ) ; err != nil {
errors = append ( errors , err . Error ( ) )
} else {
for _ , cpolr := range cpolrs {
2021-08-21 19:35:17 +02:00
cpolr . Results = [ ] * v1alpha2 . PolicyReportResult { }
cpolr . Summary = v1alpha2 . PolicyReportSummary { }
if _ , err = pclient . Wgpolicyk8sV1alpha2 ( ) . ClusterPolicyReports ( ) . Update ( context . TODO ( ) , cpolr , metav1 . UpdateOptions { } ) ; err != nil {
2021-03-25 12:28:03 -07:00
errors = append ( errors , fmt . Sprintf ( "%s/%s: %v" , cpolr . Kind , cpolr . Name , err ) )
}
}
}
if len ( errors ) == 0 {
return nil
}
return fmt . Errorf ( "failed to erase results entries %v" , strings . Join ( errors , ";" ) )
}
func ( pc * PolicyController ) requeuePolicies ( ) {
logger := pc . log . WithName ( "requeuePolicies" )
if cpols , err := pc . pLister . List ( labels . Everything ( ) ) ; err == nil {
for _ , cpol := range cpols {
if ! pc . canBackgroundProcess ( cpol ) {
continue
}
pc . enqueuePolicy ( cpol )
}
} else {
logger . Error ( err , "unable to list ClusterPolicies" )
}
namespaces , err := pc . nsLister . List ( labels . Everything ( ) )
if err != nil {
logger . Error ( err , "unable to list namespaces" )
return
}
for _ , ns := range namespaces {
pols , err := pc . npLister . Policies ( ns . GetName ( ) ) . List ( labels . Everything ( ) )
if err != nil {
logger . Error ( err , "unable to list Policies" , "namespace" , ns . GetName ( ) )
continue
}
for _ , p := range pols {
pol := ConvertPolicyToClusterPolicy ( p )
if ! pc . canBackgroundProcess ( pol ) {
continue
}
pc . enqueuePolicy ( pol )
}
}
}
2021-06-30 00:43:11 +03:00
func generateSuccessEvents ( log logr . Logger , ers [ ] * response . EngineResponse ) ( eventInfos [ ] event . Info ) {
2019-11-12 14:41:29 -08:00
for _ , er := range ers {
2021-06-30 00:43:11 +03:00
logger := log . WithValues ( "policy" , er . PolicyResponse . Policy , "kind" , er . PolicyResponse . Resource . Kind , "namespace" , er . PolicyResponse . Resource . Namespace , "name" , er . PolicyResponse . Resource . Name )
logger . V ( 4 ) . Info ( "reporting success results for policy" )
if ! er . IsFailed ( ) {
// generate event on policy for success rules
logger . V ( 4 ) . Info ( "generating event on policy for success rules" )
e := event . Info { }
kind := "ClusterPolicy"
if er . PolicyResponse . Policy . Namespace != "" {
kind = "Policy"
}
e . Kind = kind
e . Namespace = er . PolicyResponse . Policy . Namespace
e . Name = er . PolicyResponse . Policy . Name
e . Reason = event . PolicyApplied . String ( )
e . Source = event . PolicyController
e . Message = fmt . Sprintf ( "rules '%v' successfully applied on resource '%s/%s/%s'" , er . GetSuccessRules ( ) , er . PolicyResponse . Resource . Kind , er . PolicyResponse . Resource . Namespace , er . PolicyResponse . Resource . Name )
eventInfos = append ( eventInfos , e )
2019-11-12 14:41:29 -08:00
}
}
return eventInfos
}
2019-08-13 13:15:04 -07:00
2021-06-30 00:43:11 +03:00
func generateFailEvents ( log logr . Logger , ers [ ] * response . EngineResponse ) ( eventInfos [ ] event . Info ) {
for _ , er := range ers {
eventInfos = append ( eventInfos , generateFailEventsPerEr ( log , er ) ... )
}
return eventInfos
}
func generateFailEventsPerEr ( log logr . Logger , er * response . EngineResponse ) [ ] event . Info {
2019-11-12 14:41:29 -08:00
var eventInfos [ ] event . Info
2020-07-20 08:00:02 -07:00
2021-06-30 00:43:11 +03:00
logger := log . WithValues ( "policy" , er . PolicyResponse . Policy . Name , "kind" , er . PolicyResponse . Resource . Kind , "namespace" , er . PolicyResponse . Resource . Namespace , "name" , er . PolicyResponse . Resource . Name )
logger . V ( 4 ) . Info ( "reporting fail results for policy" )
2020-07-20 08:00:02 -07:00
2019-11-12 14:41:29 -08:00
for _ , rule := range er . PolicyResponse . Rules {
if rule . Success {
continue
}
2019-08-13 13:15:04 -07:00
// generate event on resource for each failed rule
2020-03-17 16:25:34 -07:00
logger . V ( 4 ) . Info ( "generating event on resource" )
2019-08-26 13:34:42 -07:00
e := event . Info { }
2019-11-12 14:41:29 -08:00
e . Kind = er . PolicyResponse . Resource . Kind
e . Namespace = er . PolicyResponse . Resource . Namespace
e . Name = er . PolicyResponse . Resource . Name
2019-11-18 17:13:48 -08:00
e . Reason = event . PolicyViolation . String ( )
2019-12-26 11:50:41 -08:00
e . Source = event . PolicyController
2021-06-30 00:43:11 +03:00
e . Message = fmt . Sprintf ( "policy '%s' (%s) rule '%s' failed. %v" , er . PolicyResponse . Policy . Name , rule . Type , rule . Name , rule . Message )
eventInfos = append ( eventInfos , e )
}
if ! er . IsFailed ( ) {
// generate event on policy for success rules
logger . V ( 4 ) . Info ( "generating event on policy for success rules" )
e := event . Info { }
kind := "ClusterPolicy"
if er . PolicyResponse . Policy . Namespace != "" {
kind = "Policy"
}
e . Kind = kind
e . Namespace = er . PolicyResponse . Policy . Namespace
e . Name = er . PolicyResponse . Policy . Name
e . Reason = event . PolicyApplied . String ( )
e . Source = event . PolicyController
e . Message = fmt . Sprintf ( "rules '%v' successfully applied on resource '%s/%s/%s'" , er . GetSuccessRules ( ) , er . PolicyResponse . Resource . Kind , er . PolicyResponse . Resource . Namespace , er . PolicyResponse . Resource . Name )
2019-11-12 14:41:29 -08:00
eventInfos = append ( eventInfos , e )
}
2021-06-30 00:43:11 +03:00
if ! er . IsSuccessful ( ) {
// generate event on policy for failed rules
logger . V ( 4 ) . Info ( "generating event on policy" )
e := event . Info { }
kind := "ClusterPolicy"
if er . PolicyResponse . Policy . Namespace != "" {
kind = "Policy"
}
e . Kind = kind
e . Name = er . PolicyResponse . Policy . Name
e . Namespace = er . PolicyResponse . Policy . Namespace
e . Reason = event . PolicyViolation . String ( )
e . Source = event . PolicyController
e . Message = fmt . Sprintf ( "rules '%v' not satisfied on resource '%s/%s/%s'" , er . GetFailedRules ( ) , er . PolicyResponse . Resource . Kind , er . PolicyResponse . Resource . Namespace , er . PolicyResponse . Resource . Name )
eventInfos = append ( eventInfos , e )
}
2019-11-12 14:41:29 -08:00
return eventInfos
2019-08-13 13:15:04 -07:00
}
2020-12-21 11:04:19 -08:00
func mergePvInfos ( infos [ ] policyreport . Info ) policyreport . Info {
aggregatedInfo := policyreport . Info { }
if len ( infos ) == 0 {
return aggregatedInfo
}
var results [ ] policyreport . EngineResponseResult
for _ , info := range infos {
for _ , res := range info . Results {
results = append ( results , res )
}
}
aggregatedInfo . PolicyName = infos [ 0 ] . PolicyName
aggregatedInfo . Namespace = infos [ 0 ] . Namespace
aggregatedInfo . Results = results
return aggregatedInfo
}