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 }