github.com/castai/kvisor@v1.7.1-0.20240516114728-b3572a2607b5/cmd/controller/state/kubelinter/kubelinter.go (about)

     1  package kubelinter
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/castai/kvisor/cmd/controller/state/kubelinter/customchecks/additionalcapabilities"
     7  	"github.com/castai/kvisor/cmd/controller/state/kubelinter/customchecks/automount"
     8  	"github.com/castai/kvisor/cmd/controller/state/kubelinter/customchecks/containerdsock"
     9  	"github.com/castai/kvisor/cmd/controller/state/kubelinter/customchecks/networkpolicypernamespace"
    10  	"github.com/castai/kvisor/cmd/controller/state/kubelinter/customchecks/securitycontext"
    11  	"github.com/castai/kvisor/cmd/controller/state/kubelinter/customobjectkinds"
    12  	"github.com/samber/lo"
    13  	"golang.stackrox.io/kube-linter/pkg/builtinchecks"
    14  	"golang.stackrox.io/kube-linter/pkg/checkregistry"
    15  	"golang.stackrox.io/kube-linter/pkg/config"
    16  	kubelinterconfig "golang.stackrox.io/kube-linter/pkg/config"
    17  	"golang.stackrox.io/kube-linter/pkg/configresolver"
    18  	"golang.stackrox.io/kube-linter/pkg/diagnostic"
    19  	"golang.stackrox.io/kube-linter/pkg/ignore"
    20  	"golang.stackrox.io/kube-linter/pkg/instantiatedcheck"
    21  	"golang.stackrox.io/kube-linter/pkg/lintcontext"
    22  	"golang.stackrox.io/kube-linter/pkg/run"
    23  	_ "golang.stackrox.io/kube-linter/pkg/templates/accesstoresources" // Import check templates.
    24  	_ "golang.stackrox.io/kube-linter/pkg/templates/antiaffinity"
    25  	_ "golang.stackrox.io/kube-linter/pkg/templates/clusteradminrolebinding"
    26  	_ "golang.stackrox.io/kube-linter/pkg/templates/containercapabilities"
    27  	_ "golang.stackrox.io/kube-linter/pkg/templates/cpurequirements"
    28  	_ "golang.stackrox.io/kube-linter/pkg/templates/danglinghpa"
    29  	_ "golang.stackrox.io/kube-linter/pkg/templates/danglingingress"
    30  	_ "golang.stackrox.io/kube-linter/pkg/templates/danglingnetworkpolicy"
    31  	_ "golang.stackrox.io/kube-linter/pkg/templates/danglingnetworkpolicypeer"
    32  	_ "golang.stackrox.io/kube-linter/pkg/templates/danglingservice"
    33  	_ "golang.stackrox.io/kube-linter/pkg/templates/deprecatedserviceaccount"
    34  	_ "golang.stackrox.io/kube-linter/pkg/templates/disallowedgvk"
    35  	_ "golang.stackrox.io/kube-linter/pkg/templates/dnsconfigoptions"
    36  	_ "golang.stackrox.io/kube-linter/pkg/templates/envvar"
    37  	_ "golang.stackrox.io/kube-linter/pkg/templates/forbiddenannotation"
    38  	_ "golang.stackrox.io/kube-linter/pkg/templates/hostipc"
    39  	_ "golang.stackrox.io/kube-linter/pkg/templates/hostmounts"
    40  	_ "golang.stackrox.io/kube-linter/pkg/templates/hostnetwork"
    41  	_ "golang.stackrox.io/kube-linter/pkg/templates/hostpid"
    42  	_ "golang.stackrox.io/kube-linter/pkg/templates/hpareplicas"
    43  	_ "golang.stackrox.io/kube-linter/pkg/templates/imagepullpolicy"
    44  	_ "golang.stackrox.io/kube-linter/pkg/templates/latesttag"
    45  	_ "golang.stackrox.io/kube-linter/pkg/templates/livenessprobe"
    46  	_ "golang.stackrox.io/kube-linter/pkg/templates/memoryrequirements"
    47  	_ "golang.stackrox.io/kube-linter/pkg/templates/mismatchingselector"
    48  	_ "golang.stackrox.io/kube-linter/pkg/templates/namespace"
    49  	_ "golang.stackrox.io/kube-linter/pkg/templates/nodeaffinity"
    50  	_ "golang.stackrox.io/kube-linter/pkg/templates/nonexistentserviceaccount"
    51  	_ "golang.stackrox.io/kube-linter/pkg/templates/nonisolatedpod"
    52  	_ "golang.stackrox.io/kube-linter/pkg/templates/ports"
    53  	_ "golang.stackrox.io/kube-linter/pkg/templates/privileged"
    54  	_ "golang.stackrox.io/kube-linter/pkg/templates/privilegedports"
    55  	_ "golang.stackrox.io/kube-linter/pkg/templates/privilegeescalation"
    56  	_ "golang.stackrox.io/kube-linter/pkg/templates/readinessprobe"
    57  	_ "golang.stackrox.io/kube-linter/pkg/templates/readonlyrootfs"
    58  	_ "golang.stackrox.io/kube-linter/pkg/templates/readsecret"
    59  	_ "golang.stackrox.io/kube-linter/pkg/templates/replicas"
    60  	_ "golang.stackrox.io/kube-linter/pkg/templates/requiredannotation"
    61  	_ "golang.stackrox.io/kube-linter/pkg/templates/requiredlabel"
    62  	_ "golang.stackrox.io/kube-linter/pkg/templates/runasnonroot"
    63  	_ "golang.stackrox.io/kube-linter/pkg/templates/serviceaccount"
    64  	_ "golang.stackrox.io/kube-linter/pkg/templates/servicetype"
    65  	_ "golang.stackrox.io/kube-linter/pkg/templates/sysctl"
    66  	_ "golang.stackrox.io/kube-linter/pkg/templates/targetport"
    67  	_ "golang.stackrox.io/kube-linter/pkg/templates/unsafeprocmount"
    68  	_ "golang.stackrox.io/kube-linter/pkg/templates/updateconfig"
    69  	_ "golang.stackrox.io/kube-linter/pkg/templates/wildcardinrules"
    70  	_ "golang.stackrox.io/kube-linter/pkg/templates/writablehostmount"
    71  	"k8s.io/apimachinery/pkg/types"
    72  )
    73  
    74  func New(checks []string) (*Linter, error) {
    75  	registry := checkregistry.New()
    76  
    77  	if err := builtinchecks.LoadInto(registry); err != nil {
    78  		return nil, fmt.Errorf("load info from registry: %w", err)
    79  	}
    80  
    81  	registerCustomObjectKinds()
    82  
    83  	if err := registerCustomChecks(registry); err != nil {
    84  		return nil, fmt.Errorf("loading custom CAST check: %w", err)
    85  	}
    86  
    87  	cfg := kubelinterconfig.Config{
    88  		Checks: kubelinterconfig.ChecksConfig{
    89  			AddAllBuiltIn: true,
    90  		},
    91  	}
    92  	if err := configresolver.LoadCustomChecksInto(&cfg, registry); err != nil {
    93  		return nil, fmt.Errorf("loading custom checks info: %w", err)
    94  	}
    95  
    96  	rules := make(map[string]struct{})
    97  	instantiatedChecks := make([]*instantiatedcheck.InstantiatedCheck, 0, len(checks))
    98  	for _, checkName := range checks {
    99  		rules[checkName] = struct{}{}
   100  		instantiatedCheck := registry.Load(checkName)
   101  		if instantiatedCheck == nil {
   102  			return nil, fmt.Errorf("check %q not found", checkName)
   103  		}
   104  		if checkName == "namespace" {
   105  			hideKubernetesDefaultService(instantiatedCheck)
   106  		}
   107  		instantiatedChecks = append(instantiatedChecks, instantiatedCheck)
   108  	}
   109  
   110  	return &Linter{
   111  		rules:              rules,
   112  		registry:           registry,
   113  		instantiatedChecks: instantiatedChecks,
   114  	}, nil
   115  }
   116  
   117  func registerCustomChecks(registry checkregistry.CheckRegistry) error {
   118  	checks := []*config.Check{
   119  		automount.Check(),
   120  		containerdsock.Check(),
   121  		securitycontext.Check(),
   122  		networkpolicypernamespace.Check(),
   123  		additionalcapabilities.Check(),
   124  	}
   125  	for _, check := range checks {
   126  		if err := registry.Register(check); err != nil {
   127  			return err
   128  		}
   129  	}
   130  	return nil
   131  }
   132  
   133  func registerCustomObjectKinds() {
   134  	customobjectkinds.RegisterNamespaceKind()
   135  }
   136  
   137  type Linter struct {
   138  	rules              map[string]struct{}
   139  	registry           checkregistry.CheckRegistry
   140  	instantiatedChecks []*instantiatedcheck.InstantiatedCheck
   141  }
   142  
   143  func (l *Linter) RunWithRules(objects []lintcontext.Object, rules []string) ([]LinterCheck, error) {
   144  	return l.run(objects, lo.SliceToMap(rules, func(item string) (string, struct{}) {
   145  		return item, struct{}{}
   146  	}))
   147  }
   148  
   149  func (l *Linter) Run(objects []lintcontext.Object) ([]LinterCheck, error) {
   150  	return l.run(objects, l.rules)
   151  }
   152  
   153  func (l *Linter) run(objects []lintcontext.Object, rules map[string]struct{}) ([]LinterCheck, error) {
   154  	lintctx := &lintContext{
   155  		objects: objects,
   156  	}
   157  
   158  	res := l.runKubeLinter([]lintcontext.LintContext{lintctx}, rules)
   159  
   160  	resources := make(map[types.UID]LinterCheck)
   161  	for _, check := range res.Reports {
   162  		obj := check.Object.K8sObject
   163  
   164  		if _, ok := resources[obj.GetUID()]; !ok {
   165  			resources[obj.GetUID()] = LinterCheck{
   166  				ResourceID: string(obj.GetUID()),
   167  				Failed:     new(LinterRuleSet),
   168  				Passed:     new(LinterRuleSet),
   169  			}
   170  		}
   171  
   172  		if check.Diagnostic.Message != "" {
   173  			resources[obj.GetUID()].Failed.Add(LinterRuleMap[check.Check])
   174  		} else {
   175  			resources[obj.GetUID()].Passed.Add(LinterRuleMap[check.Check])
   176  		}
   177  
   178  	}
   179  
   180  	return lo.Values(resources), nil
   181  }
   182  
   183  func (l *Linter) runKubeLinter(lintCtxs []lintcontext.LintContext, rules map[string]struct{}) run.Result {
   184  	var result run.Result
   185  
   186  	for _, instantiatedCheck := range l.instantiatedChecks {
   187  		if _, ok := rules[instantiatedCheck.Spec.Name]; ok {
   188  			result.Checks = append(result.Checks, instantiatedCheck.Spec)
   189  		}
   190  	}
   191  
   192  	for _, lintCtx := range lintCtxs {
   193  		for _, obj := range lintCtx.Objects() {
   194  			for _, check := range l.instantiatedChecks {
   195  				if _, ok := rules[check.Spec.Name]; !ok {
   196  					continue
   197  				}
   198  				if !check.Matcher.Matches(obj.K8sObject.GetObjectKind().GroupVersionKind()) {
   199  					continue
   200  				}
   201  				if ignore.ObjectForCheck(obj.K8sObject.GetAnnotations(), check.Spec.Name) {
   202  					continue
   203  				}
   204  				diagnostics := check.Func(lintCtx, obj)
   205  				if len(diagnostics) > 0 {
   206  					for _, d := range diagnostics {
   207  						result.Reports = append(result.Reports, diagnostic.WithContext{
   208  							Diagnostic: d,
   209  							Check:      check.Spec.Name,
   210  							Object:     obj,
   211  						})
   212  					}
   213  				} else {
   214  					result.Reports = append(result.Reports, diagnostic.WithContext{
   215  						Diagnostic: diagnostic.Diagnostic{},
   216  						Check:      check.Spec.Name,
   217  						Object:     obj,
   218  					})
   219  				}
   220  			}
   221  		}
   222  	}
   223  
   224  	return result
   225  }
   226  
   227  type lintContext struct {
   228  	objects        []lintcontext.Object
   229  	invalidObjects []lintcontext.InvalidObject
   230  }
   231  
   232  // Objects returns the (valid) objects loaded from this LintContext.
   233  func (l *lintContext) Objects() []lintcontext.Object {
   234  	return l.objects
   235  }
   236  
   237  // InvalidObjects returns any objects that we attempted to load, but which were invalid.
   238  func (l *lintContext) InvalidObjects() []lintcontext.InvalidObject {
   239  	return l.invalidObjects
   240  }
   241  
   242  func hideKubernetesDefaultService(check *instantiatedcheck.InstantiatedCheck) {
   243  	check.Func = func(lintCtx lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic {
   244  		if object.K8sObject.GetNamespace() == "default" &&
   245  			object.K8sObject.GetObjectKind().GroupVersionKind().Kind == "Service" &&
   246  			object.K8sObject.GetName() == "kubernetes" {
   247  			return []diagnostic.Diagnostic{}
   248  		}
   249  		return check.Func(lintCtx, object)
   250  	}
   251  }