Skip to content

Commit

Permalink
Merge branch 'main' of github.com:afdesk/trivy-operator into fix/cust…
Browse files Browse the repository at this point in the history
…om-policies

# Conflicts:
#	pkg/policy/policy.go
#	pkg/policy/policy_test.go
  • Loading branch information
afdesk committed Feb 20, 2025
2 parents 6a5153d + 5a93cbe commit 5a82d13
Show file tree
Hide file tree
Showing 28 changed files with 372 additions and 164 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/chart-testing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ jobs:
kind load image-archive trivy-operator.tar
- name: Set up python
uses: actions/setup-python@v5.3.0
uses: actions/setup-python@v5.4.0
with:
python-version: '3.x'
check-latest: true
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
fetch-depth: 0
ref: ${{ github.event.inputs.ref }}
persist-credentials: true
- uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38
- uses: actions/setup-python@8039c45ed9a312fba91f3399cd0605ba2ebfe93c
with:
python-version: 3.x
- run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-helm-chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
version: v3.14.2

- name: Set up python
uses: actions/setup-python@v5.3.0
uses: actions/setup-python@v5.4.0
with:
python-version: '3.x'
check-latest: true
Expand Down
32 changes: 15 additions & 17 deletions docs/tutorials/writing-custom-configuration-audit-policies.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,33 +110,31 @@ metadata:
data:
policy.recommended_labels.kinds: "*"
policy.recommended_labels.rego: |
package trivyoperator.policy.k8s.custom
package trivyoperator.policy.k8s.custom
import data.lib.result
import future.keywords.in

__rego_metadata__ := {
__rego_metadata__ := {
"id": "recommended_labels",
"title": "Recommended labels",
"severity": "LOW",
"type": "Kubernetes Security Check",
"description": "A common set of labels allows tools to work interoperably, describing objects in a common manner that all tools can understand.",
"recommended_actions": "Take full advantage of using recommended labels and apply them on every resource object.",
"url": "https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/",
}
}
__rego_input__ := {
"combine": false,
"selector": [{"type": "kubernetes"}],
}
recommended_labels := [
"app.kubernetes.io/name",
"app.kubernetes.io/version",
]
deny[res] {
input.kind == "Pod"
some container in input.spec.containers
not startswith(container.image, "hooli.com")
msg := sprintf("Image '%v' comes from untrusted registry", [container.image])
res := result.new(msg, container)
}
deny[res] {
provided := {label | input.metadata.labels[label]}
required := {label | label := recommended_labels[_]}
missing := required - provided
count(missing) > 0
msg := sprintf("You must provide labels: %v", [missing])
res := {"msg": msg}
}
```
In this example, to add a new policy, you must define two data entries in the `trivy-operator-policies-config`
Expand Down
128 changes: 128 additions & 0 deletions pkg/configauditreport/controller/checks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package controller

import (
"context"
"fmt"
"sync"

"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/predicate"

"github.com/aquasecurity/trivy-operator/pkg/configauditreport"
"github.com/aquasecurity/trivy-operator/pkg/kube"
"github.com/aquasecurity/trivy-operator/pkg/operator/etc"
"github.com/aquasecurity/trivy-operator/pkg/policy"
"github.com/aquasecurity/trivy-operator/pkg/trivyoperator"
"github.com/aquasecurity/trivy/pkg/set"
)

type ChecksLoader struct {
mu sync.Mutex
cfg etc.Config
logger logr.Logger
cl client.Client
objectResolver kube.ObjectResolver
pluginContext trivyoperator.PluginContext
pluginConfig configauditreport.PluginInMemory
policyLoader policy.Loader
policies *policy.Policies
}

func NewChecksLoader(
cfg etc.Config,
logger logr.Logger,
cl client.Client,
objectResolver kube.ObjectResolver,
pluginContext trivyoperator.PluginContext,
pluginConfig configauditreport.PluginInMemory,
policyLoader policy.Loader,
) *ChecksLoader {
return &ChecksLoader{
cfg: cfg,
logger: logger,
cl: cl,
objectResolver: objectResolver,
pluginContext: pluginContext,
pluginConfig: pluginConfig,
policyLoader: policyLoader,
}
}

func (r *ChecksLoader) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
r.mu.Lock()
defer r.mu.Unlock()

log := r.logger.WithValues("configMap", req.NamespacedName)

var cm corev1.ConfigMap
if err := r.cl.Get(ctx, req.NamespacedName, &cm); err != nil {
if req.Name == trivyoperator.TrivyConfigMapName {
log.V(1).Info("Checks removed since trivy config is removed")
r.policies = nil
}
return ctrl.Result{}, client.IgnoreNotFound(err)
}

if err := r.loadChecks(ctx); err != nil {
return ctrl.Result{}, fmt.Errorf("load checks: %w", err)
}

return ctrl.Result{}, nil
}

func (r *ChecksLoader) loadChecks(ctx context.Context) error {
log := r.logger

log.V(1).Info("Load checks")
cac, err := r.pluginConfig.NewConfigForConfigAudit(r.pluginContext)
if err != nil {
return fmt.Errorf("new config for config audit: %w", err)
}
policies, err := ConfigurePolicies(
ctx, r.cfg, r.objectResolver, cac, r.logger, r.policyLoader,
)
if err != nil {
return fmt.Errorf("getting policies: %w", err)
}
r.policies = policies
log.V(1).Info("Checks loaded")

return nil
}

var allowedConfigMaps = set.New(
trivyoperator.TrivyConfigMapName,
trivyoperator.PoliciesConfigMapName,
)

var configPredicate = func(namespace string) predicate.Predicate {
return predicate.NewPredicateFuncs(func(obj client.Object) bool {
if allowedConfigMaps.Contains(obj.GetName()) {
return false
}
return obj.GetNamespace() == namespace
})
}

func (r *ChecksLoader) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&corev1.ConfigMap{}, builder.WithPredicates(configPredicate(r.cfg.Namespace))).
Complete(r)
}

func (r *ChecksLoader) GetPolicies(ctx context.Context) (*policy.Policies, error) {
r.mu.Lock()
defer r.mu.Unlock()

if r.policies == nil {
if err := r.loadChecks(ctx); err != nil {
return nil, fmt.Errorf("load checks: %w", err)
}
}

return r.policies, nil
}
15 changes: 15 additions & 0 deletions pkg/configauditreport/controller/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@ func Policies(ctx context.Context, config etc.Config, c client.Client, cac confi
return policy.NewPolicies(cm.Data, cac, log, pl, version), nil
}

func ConfigurePolicies(ctx context.Context, config etc.Config, c client.Client, cac configauditreport.ConfigAuditConfig, log logr.Logger, pl policy.Loader, clusterVersion ...string) (*policy.Policies, error) {
policies, err := Policies(ctx, config, c, cac, log, pl, clusterVersion...)
if err != nil {
return nil, err
}
if err := policies.Load(); err != nil {
return nil, fmt.Errorf("load policies: %w", err)
}

if err := policies.InitScanner(); err != nil {
return nil, fmt.Errorf("init scanner: %w", err)
}
return policies, nil
}

func evaluate(ctx context.Context, policies *policy.Policies, resource client.Object, bi trivyoperator.BuildInfo, cd trivyoperator.ConfigData, c etc.Config, inputs ...[]byte) (Misconfiguration, error) {
misconfiguration := Misconfiguration{}
results, err := policies.Eval(ctx, resource, inputs...)
Expand Down
16 changes: 7 additions & 9 deletions pkg/configauditreport/controller/nodecollector.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ type NodeCollectorJobController struct {
configauditreport.PluginInMemory
InfraReadWriter infraassessment.ReadWriter
trivyoperator.BuildInfo
ChecksLoader *ChecksLoader
}

// +kubebuilder:rbac:groups=batch,resources=jobs,verbs=get;list;watch;create;delete

func (r *NodeCollectorJobController) SetupWithManager(mgr ctrl.Manager) error {
var predicates []predicate.Predicate

predicates = append(predicates, ManagedByTrivyOperator, IsNodeInfoCollector, JobHasAnyCondition)
return ctrl.NewControllerManagedBy(mgr).
For(&batchv1.Job{}, builder.WithPredicates(predicates...)).
Expand Down Expand Up @@ -132,19 +132,17 @@ func (r *NodeCollectorJobController) processCompleteScanJob(ctx context.Context,
if err != nil {
return err
}
cac, err := r.NewConfigForConfigAudit(r.PluginContext)
if err != nil {
return err
}
policies, err := Policies(ctx, r.Config, r.Client, cac, r.Logger, r.PolicyLoader)
if err != nil {
return fmt.Errorf("getting policies: %w", err)
}

resourceHash, err := kube.ComputeSpecHash(node)
if err != nil {
return fmt.Errorf("computing spec hash: %w", err)
}

policies, err := r.ChecksLoader.GetPolicies(ctx)
if err != nil {
return fmt.Errorf("get policies: %w", err)
}

policiesHash, err := policies.Hash(string(kube.KindNode))
if err != nil {
return fmt.Errorf("computing policies hash: %w", err)
Expand Down
11 changes: 5 additions & 6 deletions pkg/configauditreport/controller/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type ResourceController struct {
trivyoperator.BuildInfo
ClusterVersion string
CacheSyncTimeout time.Duration
ChecksLoader *ChecksLoader
}

// +kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch
Expand Down Expand Up @@ -156,6 +157,7 @@ func (r *ResourceController) buildControlMgr(mgr ctrl.Manager, configResource ku
func (r *ResourceController) reconcileResource(resourceKind kube.Kind) reconcile.Func {
return func(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Logger.WithValues("kind", resourceKind, "name", req.NamespacedName)

resourceRef := kube.ObjectRefFromKindAndObjectKey(resourceKind, req.NamespacedName)
resource, err := r.ObjectFromObjectRef(ctx, resourceRef)
if err != nil {
Expand All @@ -170,13 +172,10 @@ func (r *ResourceController) reconcileResource(resourceKind kube.Kind) reconcile
r.Config.ConfigAuditScannerScanOnlyCurrentRevisions, log, r.ConfigData.GetSkipResourceByLabels()); skip {
return ctrl.Result{}, err
}
cac, err := r.NewConfigForConfigAudit(r.PluginContext)
if err != nil {
return ctrl.Result{}, err
}
policies, err := Policies(ctx, r.Config, r.Client, cac, r.Logger, r.PolicyLoader, r.ClusterVersion)

policies, err := r.ChecksLoader.GetPolicies(ctx)
if err != nil {
return ctrl.Result{}, fmt.Errorf("getting policies: %w", err)
return ctrl.Result{}, fmt.Errorf("get policies: %w", err)
}

// Skip processing if there are no policies applicable to the resource
Expand Down
21 changes: 17 additions & 4 deletions pkg/operator/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,6 @@ func Start(ctx context.Context, buildInfo trivyoperator.BuildInfo, operatorConfi
return err
}
objectResolver := kube.NewObjectResolver(mgr.GetClient(), compatibleObjectMapper)
if err != nil {
return err
}
limitChecker := jobs.NewLimitChecker(operatorConfig, mgr.GetClient(), trivyOperatorConfig)
logsReader := kube.NewLogsReader(clientSet)
secretsReader := kube.NewSecretsReader(mgr.GetClient())
Expand Down Expand Up @@ -238,6 +235,20 @@ func Start(ctx context.Context, buildInfo trivyoperator.BuildInfo, operatorConfi
}
}

checksLoader := controller.NewChecksLoader(
operatorConfig,
ctrl.Log.WithName("checks-loader"),
mgr.GetClient(),
objectResolver,
pluginContext,
pluginConfig,
policyLoader,
)

if err := checksLoader.SetupWithManager(mgr); err != nil {
return fmt.Errorf("setup MyReconciler: %w", err)
}

if operatorConfig.ScannerReportTTL != nil {
ttlReconciler := &TTLReportReconciler{
Logger: ctrl.Log.WithName("reconciler").WithName("ttlreport"),
Expand Down Expand Up @@ -298,6 +309,7 @@ func Start(ctx context.Context, buildInfo trivyoperator.BuildInfo, operatorConfi
BuildInfo: buildInfo,
ClusterVersion: gitVersion,
CacheSyncTimeout: *operatorConfig.ControllerCacheSyncTimeout,
ChecksLoader: checksLoader,
}).SetupWithManager(mgr); err != nil {
return fmt.Errorf("unable to setup resource controller: %w", err)
}
Expand All @@ -310,7 +322,7 @@ func Start(ctx context.Context, buildInfo trivyoperator.BuildInfo, operatorConfi
PluginInMemory: pluginConfig,
ClusterVersion: gitVersion,
}).SetupWithManager(mgr); err != nil {
return fmt.Errorf("unable to setup resource controller: %w", err)
return fmt.Errorf("unable to setup policy config controller: %w", err)
}
if operatorConfig.InfraAssessmentScannerEnabled {
limitChecker := jobs.NewLimitChecker(operatorConfig, mgr.GetClient(), trivyOperatorConfig)
Expand Down Expand Up @@ -340,6 +352,7 @@ func Start(ctx context.Context, buildInfo trivyoperator.BuildInfo, operatorConfi
PluginInMemory: pluginConfig,
InfraReadWriter: infraassessment.NewReadWriter(&objectResolver),
BuildInfo: buildInfo,
ChecksLoader: checksLoader,
}).SetupWithManager(mgr); err != nil {
return fmt.Errorf("unable to setup node collector controller: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/plugins/trivy/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ func TestPlugin_Init(t *testing.T) {
},
Data: map[string]string{
"trivy.repository": DefaultImageRepository,
"trivy.tag": "0.52.2",
"trivy.tag": "0.59.1",
"trivy.severity": DefaultSeverity,
"trivy.slow": "true",
"trivy.mode": string(Standalone),
Expand Down
Loading

0 comments on commit 5a82d13

Please sign in to comment.