Skip to content

Commit

Permalink
bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
matas-cast committed Jan 28, 2025
1 parent abcb008 commit 2977777
Showing 1 changed file with 136 additions and 0 deletions.
136 changes: 136 additions & 0 deletions cmd/controller/state/kubelinter/customchecks/bindings/bindings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package bindings

import (
"fmt"
"strings"

"golang.stackrox.io/kube-linter/pkg/check"
"golang.stackrox.io/kube-linter/pkg/config"
"golang.stackrox.io/kube-linter/pkg/diagnostic"
"golang.stackrox.io/kube-linter/pkg/lintcontext"
"golang.stackrox.io/kube-linter/pkg/objectkinds"
"golang.stackrox.io/kube-linter/pkg/templates/util"
rbacv1 "k8s.io/api/rbac/v1"
)

var (
rules = map[string]BindingsCheck{
"system-masters": {
Name: "Avoid use of system:masters group",
Values: []string{"system:masters"},
},
"system-anonymous": {
Name: "Avoid binding to system:anonymous",
Values: []string{"system:anonymous"},
},
"system-unauthenticated": {
Name: "Avoid non-default bindings to system:unauthenticated",
Values: []string{"system:unauthenticated"},
ExcludedValues: []string{"system:public-info-viewer"},
},
"system-authenticated": {
Name: "Avoid non-default bindings to system:authenticated",
Values: []string{"system:authenticated"},
ExcludedValues: []string{"system:public-info-viewer", "system:basic-user", "system:discovery"},
},
}
)

type BindingsCheck struct {
Name string
Values []string
ExcludedValues []string
}

func roleBindingsTemplate(key string, bindingCheck BindingsCheck) check.Template {
template := check.Template{
HumanName: bindingCheck.Name,
Key: key,
SupportedObjectKinds: config.ObjectKindsDesc{
ObjectKinds: []string{
objectkinds.RoleBinding,
objectkinds.ClusterRoleBinding,
},
},
Parameters: ParamDescs,
ParseAndValidateParams: ParseAndValidate,
Instantiate: WrapInstantiateFunc(func(_ Params) (check.Func, error) {
return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic {
var subjects []rbacv1.Subject
rb, ok := object.K8sObject.(*rbacv1.RoleBinding)
if !ok {
crb, ok := object.K8sObject.(*rbacv1.ClusterRoleBinding)
if !ok {
return nil
}

subjects = crb.Subjects
} else {
subjects = rb.Subjects
}

for _, subject := range subjects {
if subject.Kind == "Group" && listContains(subject.Name, bindingCheck.Values) {
if bindingCheck.ExcludedValues == nil || !listContains(subject.Name, bindingCheck.ExcludedValues) {
return []diagnostic.Diagnostic{{Message: "Binding to system:masters"}}
}
}
}

return nil
}, nil
}),
}

return template
}

func listContains(value string, list []string) bool {
for _, v := range list {
if v == value {
return true
}
}
return false
}

type Params struct {
}

var (
// Use some imports in case they don't get used otherwise.
_ = util.MustParseParameterDesc
_ = fmt.Sprintf

ParamDescs = []check.ParameterDesc{}
)

func (p *Params) Validate() error {
var validationErrors []string
if len(validationErrors) > 0 {
return fmt.Errorf("invalid parameters: %s", strings.Join(validationErrors, ", "))
}
return nil
}

// ParseAndValidate instantiates a Params object out of the passed map[string]interface{},
// validates it, and returns it.
// The return type is interface{} to satisfy the type in the Template struct.
func ParseAndValidate(m map[string]interface{}) (interface{}, error) {
var p Params
if err := util.DecodeMapStructure(m, &p); err != nil {
return nil, err
}
if err := p.Validate(); err != nil {
return nil, err
}
return p, nil
}

// WrapInstantiateFunc is a convenience wrapper that wraps an untyped instantiate function
// into a typed one.
func WrapInstantiateFunc(f func(p Params) (check.Func, error)) func(interface{}) (check.Func, error) {
return func(paramsInt interface{}) (check.Func, error) {
return f(paramsInt.(Params))
}
}

0 comments on commit 2977777

Please sign in to comment.