mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-07 03:03:59 +00:00
Merge pull request #118540 from jiahuif-forks/feature/validating-admission-policy/authorizer-typechecking-support
add support for authorizer to type checking.
This commit is contained in:
commit
4954c7bac4
@ -35,6 +35,7 @@ import (
|
|||||||
apiservercel "k8s.io/apiserver/pkg/cel"
|
apiservercel "k8s.io/apiserver/pkg/cel"
|
||||||
"k8s.io/apiserver/pkg/cel/common"
|
"k8s.io/apiserver/pkg/cel/common"
|
||||||
"k8s.io/apiserver/pkg/cel/environment"
|
"k8s.io/apiserver/pkg/cel/environment"
|
||||||
|
"k8s.io/apiserver/pkg/cel/library"
|
||||||
"k8s.io/apiserver/pkg/cel/openapi"
|
"k8s.io/apiserver/pkg/cel/openapi"
|
||||||
"k8s.io/apiserver/pkg/cel/openapi/resolver"
|
"k8s.io/apiserver/pkg/cel/openapi/resolver"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
@ -72,7 +73,8 @@ func (c *TypeChecker) Check(policy *v1alpha1.ValidatingAdmissionPolicy) []v1alph
|
|||||||
for _, v := range policy.Spec.Validations {
|
for _, v := range policy.Spec.Validations {
|
||||||
exps = append(exps, v.Expression)
|
exps = append(exps, v.Expression)
|
||||||
}
|
}
|
||||||
msgs := c.CheckExpressions(exps, policy.Spec.ParamKind != nil, policy)
|
// TODO(jiahuif) hasAuthorizer always true for now, will change after expending type checking to all fields.
|
||||||
|
msgs := c.CheckExpressions(exps, policy.Spec.ParamKind != nil, true, policy)
|
||||||
var results []v1alpha1.ExpressionWarning // intentionally not setting capacity
|
var results []v1alpha1.ExpressionWarning // intentionally not setting capacity
|
||||||
for i, msg := range msgs {
|
for i, msg := range msgs {
|
||||||
if msg != "" {
|
if msg != "" {
|
||||||
@ -93,7 +95,7 @@ func (c *TypeChecker) Check(policy *v1alpha1.ValidatingAdmissionPolicy) []v1alph
|
|||||||
// TODO: It is much more useful to have machine-readable output and let the
|
// TODO: It is much more useful to have machine-readable output and let the
|
||||||
// client format it. That requires an update to the KEP, probably in coming
|
// client format it. That requires an update to the KEP, probably in coming
|
||||||
// releases.
|
// releases.
|
||||||
func (c *TypeChecker) CheckExpressions(expressions []string, hasParams bool, policy *v1alpha1.ValidatingAdmissionPolicy) []string {
|
func (c *TypeChecker) CheckExpressions(expressions []string, hasParams, hasAuthorizer bool, policy *v1alpha1.ValidatingAdmissionPolicy) []string {
|
||||||
var allWarnings []string
|
var allWarnings []string
|
||||||
allGvks := c.typesToCheck(policy)
|
allGvks := c.typesToCheck(policy)
|
||||||
gvks := make([]schema.GroupVersionKind, 0, len(allGvks))
|
gvks := make([]schema.GroupVersionKind, 0, len(allGvks))
|
||||||
@ -127,7 +129,7 @@ func (c *TypeChecker) CheckExpressions(expressions []string, hasParams bool, pol
|
|||||||
var results []typeCheckingResult
|
var results []typeCheckingResult
|
||||||
for i, gvk := range gvks {
|
for i, gvk := range gvks {
|
||||||
s := schemas[i]
|
s := schemas[i]
|
||||||
issues, err := c.checkExpression(exp, hasParams, typeOverwrite{
|
issues, err := c.checkExpression(exp, hasParams, hasAuthorizer, typeOverwrite{
|
||||||
object: common.SchemaDeclType(s, true).MaybeAssignTypeName(generateUniqueTypeName(gvk.Kind)),
|
object: common.SchemaDeclType(s, true).MaybeAssignTypeName(generateUniqueTypeName(gvk.Kind)),
|
||||||
params: paramsDeclType,
|
params: paramsDeclType,
|
||||||
})
|
})
|
||||||
@ -187,8 +189,8 @@ func (c *TypeChecker) paramsType(policy *v1alpha1.ValidatingAdmissionPolicy) sch
|
|||||||
return gv.WithKind(policy.Spec.ParamKind.Kind)
|
return gv.WithKind(policy.Spec.ParamKind.Kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TypeChecker) checkExpression(expression string, hasParams bool, types typeOverwrite) (*cel.Issues, error) {
|
func (c *TypeChecker) checkExpression(expression string, hasParams, hasAuthorizer bool, types typeOverwrite) (*cel.Issues, error) {
|
||||||
env, err := buildEnv(hasParams, types)
|
env, err := buildEnv(hasParams, hasAuthorizer, types)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -317,7 +319,7 @@ func sortGVKList(list []schema.GroupVersionKind) []schema.GroupVersionKind {
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildEnv(hasParams bool, types typeOverwrite) (*cel.Env, error) {
|
func buildEnv(hasParams bool, hasAuthorizer bool, types typeOverwrite) (*cel.Env, error) {
|
||||||
baseEnv := environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion())
|
baseEnv := environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion())
|
||||||
requestType := plugincel.BuildRequestType()
|
requestType := plugincel.BuildRequestType()
|
||||||
|
|
||||||
@ -337,6 +339,13 @@ func buildEnv(hasParams bool, types typeOverwrite) (*cel.Env, error) {
|
|||||||
declTypes = append(declTypes, types.params)
|
declTypes = append(declTypes, types.params)
|
||||||
varOpts = append(varOpts, createVariableOpts(types.params, plugincel.ParamsVarName)...)
|
varOpts = append(varOpts, createVariableOpts(types.params, plugincel.ParamsVarName)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// authorizer, implicitly available to all expressions of a policy
|
||||||
|
if hasAuthorizer {
|
||||||
|
// we only need its structure but not the variable itself
|
||||||
|
varOpts = append(varOpts, cel.Variable("authorizer", library.AuthorizerType))
|
||||||
|
}
|
||||||
|
|
||||||
env, err := baseEnv.Extend(
|
env, err := baseEnv.Extend(
|
||||||
environment.VersionedOptions{
|
environment.VersionedOptions{
|
||||||
// Feature epoch was actually 1.26, but we artificially set it to 1.0 because these
|
// Feature epoch was actually 1.26, but we artificially set it to 1.0 because these
|
||||||
|
@ -233,6 +233,42 @@ func TestTypeCheck(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
}}
|
}}
|
||||||
|
authorizerPolicy := &v1alpha1.ValidatingAdmissionPolicy{Spec: v1alpha1.ValidatingAdmissionPolicySpec{
|
||||||
|
Validations: []v1alpha1.Validation{
|
||||||
|
{
|
||||||
|
Expression: "authorizer.group('').resource('endpoints').check('create').allowed()",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MatchConstraints: &v1alpha1.MatchResources{ResourceRules: []v1alpha1.NamedRuleWithOperations{
|
||||||
|
{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Rule: v1alpha1.Rule{
|
||||||
|
APIGroups: []string{"apps"},
|
||||||
|
APIVersions: []string{"v1"},
|
||||||
|
Resources: []string{"deployments"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}}
|
||||||
|
authorizerInvalidPolicy := &v1alpha1.ValidatingAdmissionPolicy{Spec: v1alpha1.ValidatingAdmissionPolicySpec{
|
||||||
|
Validations: []v1alpha1.Validation{
|
||||||
|
{
|
||||||
|
Expression: "authorizer.allowed()",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MatchConstraints: &v1alpha1.MatchResources{ResourceRules: []v1alpha1.NamedRuleWithOperations{
|
||||||
|
{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Rule: v1alpha1.Rule{
|
||||||
|
APIGroups: []string{"apps"},
|
||||||
|
APIVersions: []string{"v1"},
|
||||||
|
Resources: []string{"deployments"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}}
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
schemaToReturn *spec.Schema
|
schemaToReturn *spec.Schema
|
||||||
@ -327,6 +363,36 @@ func TestTypeCheck(t *testing.T) {
|
|||||||
toHaveLengthOf(1),
|
toHaveLengthOf(1),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "authorizer",
|
||||||
|
policy: authorizerPolicy,
|
||||||
|
schemaToReturn: &spec.Schema{
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Type: []string{"object"},
|
||||||
|
Properties: map[string]spec.Schema{
|
||||||
|
"foo": *spec.StringProperty(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
assertions: []assertionFunc{toBeEmpty},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "authorizer invalid",
|
||||||
|
policy: authorizerInvalidPolicy,
|
||||||
|
schemaToReturn: &spec.Schema{
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Type: []string{"object"},
|
||||||
|
Properties: map[string]spec.Schema{
|
||||||
|
"foo": *spec.StringProperty(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
assertions: []assertionFunc{
|
||||||
|
toHaveFieldRef("spec.validations[0].expression"),
|
||||||
|
toHaveLengthOf(1),
|
||||||
|
toContain("found no matching overload for 'allowed' applied to 'kubernetes.authorization.Authorizer"),
|
||||||
|
},
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
typeChecker := buildTypeChecker(tc.schemaToReturn)
|
typeChecker := buildTypeChecker(tc.schemaToReturn)
|
||||||
|
Loading…
Reference in New Issue
Block a user