-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
system:masters and bind,impersonate,escalate checks (#453)
* add 4 of checks from CIS
- Loading branch information
1 parent
7f2c8f3
commit dd41cba
Showing
6 changed files
with
317 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
146 changes: 146 additions & 0 deletions
146
cmd/controller/state/kubelinter/customchecks/bindings/bindings.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package bindings | ||
|
||
import ( | ||
"fmt" | ||
"slices" | ||
|
||
"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" | ||
"golang.stackrox.io/kube-linter/pkg/templates/util" | ||
rbacv1 "k8s.io/api/rbac/v1" | ||
) | ||
|
||
const ( | ||
templateName = "bindings" | ||
) | ||
|
||
var ( | ||
ParamDescs = []check.ParameterDesc{} | ||
) | ||
|
||
func Checks() []*config.Check { | ||
var checks []*config.Check | ||
for k, v := range rules { | ||
checks = append(checks, &config.Check{ | ||
Name: k, | ||
Template: templateName, | ||
Description: v.Name, | ||
Params: map[string]interface{}{ | ||
"Values": v.Values, | ||
"ExcludedValues": v.ExcludedValues, | ||
}, | ||
}) | ||
} | ||
|
||
return checks | ||
} | ||
|
||
func init() { | ||
templates.Register(roleBindingsTemplate()) | ||
} | ||
|
||
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() check.Template { | ||
template := check.Template{ | ||
HumanName: "Avoid bindings to certain groups", | ||
Key: templateName, | ||
SupportedObjectKinds: config.ObjectKindsDesc{ | ||
ObjectKinds: []string{ | ||
objectkinds.RoleBinding, | ||
objectkinds.ClusterRoleBinding, | ||
}, | ||
}, | ||
Parameters: ParamDescs, | ||
ParseAndValidateParams: ParseAndValidate, | ||
Instantiate: WrapInstantiateFunc(func(p Params) (check.Func, error) { | ||
return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic { | ||
var subjects []rbacv1.Subject | ||
var roleRef rbacv1.RoleRef | ||
|
||
rb, ok := object.K8sObject.(*rbacv1.RoleBinding) | ||
if !ok { | ||
crb, ok := object.K8sObject.(*rbacv1.ClusterRoleBinding) | ||
if !ok { | ||
return nil | ||
} | ||
|
||
subjects = crb.Subjects | ||
roleRef = crb.RoleRef | ||
} else { | ||
subjects = rb.Subjects | ||
roleRef = rb.RoleRef | ||
} | ||
|
||
for _, subject := range subjects { | ||
if subject.Kind == "Group" && slices.Contains(p.Values, subject.Name) { | ||
if p.ExcludedValues == nil || !slices.Contains(p.ExcludedValues, roleRef.Name) { | ||
return []diagnostic.Diagnostic{{Message: fmt.Sprintf("Binding to %s", subject.Name)}} | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
}, nil | ||
}), | ||
} | ||
|
||
return template | ||
} | ||
|
||
type Params struct { | ||
Values []string | ||
ExcludedValues []string | ||
} | ||
|
||
func (p *Params) Validate() error { | ||
return nil | ||
} | ||
|
||
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 | ||
} | ||
|
||
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)) | ||
} | ||
} |
99 changes: 99 additions & 0 deletions
99
cmd/controller/state/kubelinter/customchecks/privescverbs/privescverbs.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package privescverbs | ||
|
||
import ( | ||
"fmt" | ||
"slices" | ||
"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" | ||
"golang.stackrox.io/kube-linter/pkg/templates/util" | ||
rbacv1 "k8s.io/api/rbac/v1" | ||
) | ||
|
||
func Check() *config.Check { | ||
return &config.Check{ | ||
Name: "privesc-verbs", | ||
Description: "Use of Bind, Impersonate and Escalate permissions", | ||
Template: "privesc-verbs", | ||
Params: map[string]interface{}{}, | ||
} | ||
} | ||
|
||
var ( | ||
privescVerbs = []string{"bind", "escalate", "impersonate"} | ||
) | ||
|
||
func init() { | ||
templates.Register(check.Template{ | ||
HumanName: "Use of Bind, Impersonate and Escalate permissions", | ||
Key: "privesc-verbs", | ||
SupportedObjectKinds: config.ObjectKindsDesc{ | ||
ObjectKinds: []string{ | ||
objectkinds.Role, | ||
objectkinds.ClusterRole, | ||
}, | ||
}, | ||
Parameters: ParamDescs, | ||
ParseAndValidateParams: ParseAndValidate, | ||
Instantiate: WrapInstantiateFunc(func(_ Params) (check.Func, error) { | ||
return func(_ lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic { | ||
var policyRules []rbacv1.PolicyRule | ||
rb, ok := object.K8sObject.(*rbacv1.Role) | ||
if !ok { | ||
cr, ok := object.K8sObject.(*rbacv1.ClusterRole) | ||
if !ok { | ||
return nil | ||
} | ||
policyRules = cr.Rules | ||
} else { | ||
policyRules = rb.Rules | ||
} | ||
for _, rule := range policyRules { | ||
for _, verb := range rule.Verbs { | ||
if slices.Contains(privescVerbs, verb) { | ||
return []diagnostic.Diagnostic{{Message: "Usage of 'bind, impersonate, escalate'"}} | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
}, nil | ||
}), | ||
}) | ||
} | ||
|
||
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 | ||
} | ||
|
||
func ParseAndValidate(m map[string]interface{}) (interface{}, error) { | ||
return Params{}, 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)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters