2019-11-09 02:57:27 +00:00
|
|
|
package engine
|
|
|
|
|
|
|
|
import (
|
2022-05-17 11:12:43 +00:00
|
|
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
|
|
|
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
|
2022-12-16 09:13:14 +00:00
|
|
|
kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1"
|
2022-08-31 06:03:47 +00:00
|
|
|
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
2022-12-02 08:14:23 +00:00
|
|
|
"github.com/kyverno/kyverno/pkg/config"
|
2023-01-31 07:46:38 +00:00
|
|
|
"github.com/kyverno/kyverno/pkg/engine/api"
|
2022-12-02 08:14:23 +00:00
|
|
|
enginectx "github.com/kyverno/kyverno/pkg/engine/context"
|
2023-01-03 09:33:09 +00:00
|
|
|
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
|
2022-12-02 08:14:23 +00:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
admissionv1 "k8s.io/api/admission/v1"
|
2022-12-09 16:45:23 +00:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
2019-11-09 02:57:27 +00:00
|
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
2022-12-16 09:13:14 +00:00
|
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
|
|
"k8s.io/client-go/tools/cache"
|
2019-11-09 02:57:27 +00:00
|
|
|
)
|
|
|
|
|
2022-12-02 08:14:23 +00:00
|
|
|
// ExcludeFunc is a function used to determine if a resource is excluded
|
|
|
|
type ExcludeFunc = func(kind, namespace, name string) bool
|
|
|
|
|
2023-01-19 03:56:22 +00:00
|
|
|
type PolicyExceptionLister interface {
|
|
|
|
// List lists all PolicyExceptions in the indexer.
|
|
|
|
// Objects returned here must be treated as read-only.
|
|
|
|
List(selector labels.Selector) (ret []*kyvernov2alpha1.PolicyException, err error)
|
|
|
|
}
|
|
|
|
|
2019-11-09 02:57:27 +00:00
|
|
|
// PolicyContext contains the contexts for engine to process
|
|
|
|
type PolicyContext struct {
|
2022-12-02 08:14:23 +00:00
|
|
|
// policy is the policy to be processed
|
|
|
|
policy kyvernov1.PolicyInterface
|
2020-12-16 20:29:16 +00:00
|
|
|
|
2022-12-02 08:14:23 +00:00
|
|
|
// newResource is the resource to be processed
|
|
|
|
newResource unstructured.Unstructured
|
2020-12-16 20:29:16 +00:00
|
|
|
|
2022-12-02 08:14:23 +00:00
|
|
|
// oldResource is the prior resource for an update, or nil
|
|
|
|
oldResource unstructured.Unstructured
|
2020-12-16 20:29:16 +00:00
|
|
|
|
2022-12-02 08:14:23 +00:00
|
|
|
// element is set when the context is used for processing a foreach loop
|
|
|
|
element unstructured.Unstructured
|
2021-10-02 23:53:02 +00:00
|
|
|
|
2022-12-02 08:14:23 +00:00
|
|
|
// admissionInfo contains the admission request information
|
|
|
|
admissionInfo kyvernov1beta1.RequestInfo
|
2020-12-16 20:29:16 +00:00
|
|
|
|
2022-12-09 16:45:23 +00:00
|
|
|
// requestResource is the fully-qualified resource of the original API request (for example, v1.pods).
|
|
|
|
// If this is specified and differs from the value in "resource", an equivalent match and conversion was performed.
|
|
|
|
//
|
|
|
|
// For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of
|
|
|
|
// `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`,
|
|
|
|
// an API request to apps/v1beta1 deployments would be converted and sent to the webhook
|
|
|
|
// with `resource: {group:"apps", version:"v1", resource:"deployments"}` (matching the resource the webhook registered for),
|
|
|
|
// and `requestResource: {group:"apps", version:"v1beta1", resource:"deployments"}` (indicating the resource of the original API request).
|
|
|
|
requestResource metav1.GroupVersionResource
|
|
|
|
|
2022-04-25 12:20:40 +00:00
|
|
|
// Dynamic client - used for api lookups
|
2022-12-02 08:14:23 +00:00
|
|
|
client dclient.Interface
|
2020-12-16 20:29:16 +00:00
|
|
|
|
2020-08-08 00:09:24 +00:00
|
|
|
// Config handler
|
2022-12-02 08:14:23 +00:00
|
|
|
excludeGroupRole []string
|
|
|
|
|
|
|
|
excludeResourceFunc ExcludeFunc
|
|
|
|
|
|
|
|
// jsonContext is the variable context
|
2022-12-12 15:20:20 +00:00
|
|
|
jsonContext enginectx.Interface
|
2022-12-02 08:14:23 +00:00
|
|
|
|
|
|
|
// namespaceLabels stores the label of namespace to be processed by namespace selector
|
|
|
|
namespaceLabels map[string]string
|
|
|
|
|
|
|
|
// admissionOperation represents if the caller is from the webhook server
|
|
|
|
admissionOperation bool
|
2022-12-02 13:59:51 +00:00
|
|
|
|
|
|
|
// informerCacheResolvers - used to get resources from informer cache
|
2023-01-31 07:46:38 +00:00
|
|
|
informerCacheResolvers api.ConfigmapResolver
|
2022-12-09 16:45:23 +00:00
|
|
|
|
|
|
|
// subresource is the subresource being requested, if any (for example, "status" or "scale")
|
|
|
|
subresource string
|
|
|
|
|
|
|
|
// subresourcesInPolicy represents the APIResources that are subresources along with their parent resource.
|
|
|
|
// This is used to determine if a resource is a subresource. It is only used when the policy context is populated
|
|
|
|
// by kyverno CLI. In all other cases when connected to a cluster, this is empty.
|
|
|
|
subresourcesInPolicy []struct {
|
|
|
|
APIResource metav1.APIResource
|
|
|
|
ParentResource metav1.APIResource
|
|
|
|
}
|
2022-12-16 09:13:14 +00:00
|
|
|
|
|
|
|
// peLister list all policy exceptions
|
2023-01-19 03:56:22 +00:00
|
|
|
peLister PolicyExceptionLister
|
2022-12-02 08:14:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Getters
|
|
|
|
|
|
|
|
func (c *PolicyContext) Policy() kyvernov1.PolicyInterface {
|
|
|
|
return c.policy
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *PolicyContext) NewResource() unstructured.Unstructured {
|
|
|
|
return c.newResource
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *PolicyContext) OldResource() unstructured.Unstructured {
|
|
|
|
return c.oldResource
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *PolicyContext) AdmissionInfo() kyvernov1beta1.RequestInfo {
|
|
|
|
return c.admissionInfo
|
|
|
|
}
|
|
|
|
|
2022-12-12 15:20:20 +00:00
|
|
|
func (c *PolicyContext) JSONContext() enginectx.Interface {
|
2022-12-02 08:14:23 +00:00
|
|
|
return c.jsonContext
|
|
|
|
}
|
|
|
|
|
2022-12-16 09:13:14 +00:00
|
|
|
func (c *PolicyContext) FindExceptions(rule string) ([]*kyvernov2alpha1.PolicyException, error) {
|
|
|
|
if c.peLister == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
polexs, err := c.peLister.List(labels.Everything())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var result []*kyvernov2alpha1.PolicyException
|
|
|
|
policyName, err := cache.MetaNamespaceKeyFunc(c.policy)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to compute policy key")
|
|
|
|
}
|
|
|
|
for _, polex := range polexs {
|
|
|
|
if polex.Contains(policyName, rule) {
|
|
|
|
result = append(result, polex)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2022-12-27 08:36:49 +00:00
|
|
|
func (c *PolicyContext) Client() dclient.Interface {
|
|
|
|
return c.client
|
|
|
|
}
|
|
|
|
|
2022-12-02 08:14:23 +00:00
|
|
|
// Mutators
|
|
|
|
|
|
|
|
func (c *PolicyContext) WithPolicy(policy kyvernov1.PolicyInterface) *PolicyContext {
|
|
|
|
copy := c.Copy()
|
|
|
|
copy.policy = policy
|
|
|
|
return copy
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *PolicyContext) WithNamespaceLabels(namespaceLabels map[string]string) *PolicyContext {
|
|
|
|
copy := c.Copy()
|
|
|
|
copy.namespaceLabels = namespaceLabels
|
|
|
|
return copy
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *PolicyContext) WithAdmissionInfo(admissionInfo kyvernov1beta1.RequestInfo) *PolicyContext {
|
|
|
|
copy := c.Copy()
|
|
|
|
copy.admissionInfo = admissionInfo
|
|
|
|
return copy
|
|
|
|
}
|
|
|
|
|
2022-12-09 16:45:23 +00:00
|
|
|
func (c *PolicyContext) WithRequestResource(requestResource metav1.GroupVersionResource) *PolicyContext {
|
|
|
|
copy := c.Copy()
|
|
|
|
copy.requestResource = requestResource
|
|
|
|
return copy
|
|
|
|
}
|
|
|
|
|
2022-12-02 08:14:23 +00:00
|
|
|
func (c *PolicyContext) WithNewResource(resource unstructured.Unstructured) *PolicyContext {
|
|
|
|
copy := c.Copy()
|
|
|
|
copy.newResource = resource
|
|
|
|
return copy
|
|
|
|
}
|
2020-09-22 21:11:49 +00:00
|
|
|
|
2022-12-02 08:14:23 +00:00
|
|
|
func (c *PolicyContext) WithOldResource(resource unstructured.Unstructured) *PolicyContext {
|
|
|
|
copy := c.Copy()
|
|
|
|
copy.oldResource = resource
|
|
|
|
return copy
|
|
|
|
}
|
2020-12-16 20:29:16 +00:00
|
|
|
|
2022-12-02 08:14:23 +00:00
|
|
|
func (c *PolicyContext) WithResources(newResource unstructured.Unstructured, oldResource unstructured.Unstructured) *PolicyContext {
|
|
|
|
return c.WithNewResource(newResource).WithOldResource(oldResource)
|
|
|
|
}
|
2021-02-03 21:09:42 +00:00
|
|
|
|
2022-12-02 08:14:23 +00:00
|
|
|
func (c *PolicyContext) WithClient(client dclient.Interface) *PolicyContext {
|
|
|
|
copy := c.Copy()
|
|
|
|
copy.client = client
|
|
|
|
return copy
|
|
|
|
}
|
2022-04-25 12:20:40 +00:00
|
|
|
|
2022-12-02 08:14:23 +00:00
|
|
|
func (c *PolicyContext) WithExcludeGroupRole(excludeGroupRole ...string) *PolicyContext {
|
|
|
|
copy := c.Copy()
|
|
|
|
copy.excludeGroupRole = excludeGroupRole
|
|
|
|
return copy
|
2019-11-09 02:57:27 +00:00
|
|
|
}
|
2021-09-27 21:28:55 +00:00
|
|
|
|
2022-12-02 08:14:23 +00:00
|
|
|
func (c *PolicyContext) WithExcludeResourceFunc(excludeResourceFunc ExcludeFunc) *PolicyContext {
|
|
|
|
copy := c.Copy()
|
|
|
|
copy.excludeResourceFunc = excludeResourceFunc
|
|
|
|
return copy
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *PolicyContext) WithConfiguration(configuration config.Configuration) *PolicyContext {
|
|
|
|
return c.WithExcludeResourceFunc(configuration.ToFilter).WithExcludeGroupRole(configuration.GetExcludeGroupRole()...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *PolicyContext) WithAdmissionOperation(admissionOperation bool) *PolicyContext {
|
|
|
|
copy := c.Copy()
|
|
|
|
copy.admissionOperation = admissionOperation
|
|
|
|
return copy
|
|
|
|
}
|
|
|
|
|
2023-01-31 07:46:38 +00:00
|
|
|
func (c *PolicyContext) WithInformerCacheResolver(informerCacheResolver api.ConfigmapResolver) *PolicyContext {
|
2022-12-02 13:59:51 +00:00
|
|
|
copy := c.Copy()
|
|
|
|
copy.informerCacheResolvers = informerCacheResolver
|
|
|
|
return copy
|
|
|
|
}
|
|
|
|
|
2022-12-09 16:45:23 +00:00
|
|
|
func (c *PolicyContext) WithSubresource(subresource string) *PolicyContext {
|
|
|
|
copy := c.Copy()
|
|
|
|
copy.subresource = subresource
|
|
|
|
return copy
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *PolicyContext) WithSubresourcesInPolicy(subresourcesInPolicy []struct {
|
|
|
|
APIResource metav1.APIResource
|
|
|
|
ParentResource metav1.APIResource
|
|
|
|
},
|
|
|
|
) *PolicyContext {
|
|
|
|
copy := c.Copy()
|
|
|
|
copy.subresourcesInPolicy = subresourcesInPolicy
|
|
|
|
return copy
|
|
|
|
}
|
|
|
|
|
2023-01-19 03:56:22 +00:00
|
|
|
func (c *PolicyContext) WithExceptions(peLister PolicyExceptionLister) *PolicyContext {
|
2022-12-16 09:13:14 +00:00
|
|
|
copy := c.Copy()
|
|
|
|
copy.peLister = peLister
|
|
|
|
return copy
|
|
|
|
}
|
2022-12-02 08:14:23 +00:00
|
|
|
|
2022-12-16 09:13:14 +00:00
|
|
|
// Constructors
|
2022-12-12 15:20:20 +00:00
|
|
|
func NewPolicyContextWithJsonContext(jsonContext enginectx.Interface) *PolicyContext {
|
2021-09-27 21:28:55 +00:00
|
|
|
return &PolicyContext{
|
2022-12-02 08:14:23 +00:00
|
|
|
jsonContext: jsonContext,
|
|
|
|
excludeGroupRole: []string{},
|
|
|
|
excludeResourceFunc: func(string, string, string) bool {
|
|
|
|
return false
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewPolicyContext() *PolicyContext {
|
2022-12-12 15:20:20 +00:00
|
|
|
return NewPolicyContextWithJsonContext(enginectx.NewContext())
|
2022-12-02 08:14:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewPolicyContextFromAdmissionRequest(
|
|
|
|
request *admissionv1.AdmissionRequest,
|
|
|
|
admissionInfo kyvernov1beta1.RequestInfo,
|
|
|
|
configuration config.Configuration,
|
|
|
|
client dclient.Interface,
|
2023-01-31 07:46:38 +00:00
|
|
|
informerCacheResolver api.ConfigmapResolver,
|
2023-01-19 03:56:22 +00:00
|
|
|
polexLister PolicyExceptionLister,
|
2022-12-02 08:14:23 +00:00
|
|
|
) (*PolicyContext, error) {
|
|
|
|
ctx, err := newVariablesContext(request, &admissionInfo)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to create policy rule context")
|
|
|
|
}
|
2023-01-03 09:33:09 +00:00
|
|
|
newResource, oldResource, err := admissionutils.ExtractResources(nil, request)
|
2022-12-02 08:14:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to parse resource")
|
|
|
|
}
|
2023-01-02 17:14:40 +00:00
|
|
|
if err := ctx.AddImageInfos(&newResource, configuration); err != nil {
|
2022-12-02 08:14:23 +00:00
|
|
|
return nil, errors.Wrap(err, "failed to add image information to the policy rule context")
|
|
|
|
}
|
2022-12-09 16:45:23 +00:00
|
|
|
requestResource := request.RequestResource.DeepCopy()
|
2022-12-02 08:14:23 +00:00
|
|
|
policyContext := NewPolicyContextWithJsonContext(ctx).
|
|
|
|
WithNewResource(newResource).
|
|
|
|
WithOldResource(oldResource).
|
|
|
|
WithAdmissionInfo(admissionInfo).
|
|
|
|
WithConfiguration(configuration).
|
|
|
|
WithClient(client).
|
2022-12-02 13:59:51 +00:00
|
|
|
WithAdmissionOperation(true).
|
2022-12-09 16:45:23 +00:00
|
|
|
WithInformerCacheResolver(informerCacheResolver).
|
|
|
|
WithRequestResource(*requestResource).
|
2022-12-16 09:13:14 +00:00
|
|
|
WithSubresource(request.SubResource).
|
2023-01-19 03:56:22 +00:00
|
|
|
WithExceptions(polexLister)
|
2022-12-02 08:14:23 +00:00
|
|
|
return policyContext, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c PolicyContext) Copy() *PolicyContext {
|
|
|
|
return &c
|
|
|
|
}
|
|
|
|
|
|
|
|
func newVariablesContext(request *admissionv1.AdmissionRequest, userRequestInfo *kyvernov1beta1.RequestInfo) (enginectx.Interface, error) {
|
|
|
|
ctx := enginectx.NewContext()
|
|
|
|
if err := ctx.AddRequest(request); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to load incoming request in context")
|
|
|
|
}
|
|
|
|
if err := ctx.AddUserInfo(*userRequestInfo); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to load userInfo in context")
|
|
|
|
}
|
|
|
|
if err := ctx.AddServiceAccount(userRequestInfo.AdmissionUserInfo.Username); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to load service account in context")
|
2021-09-27 21:28:55 +00:00
|
|
|
}
|
2022-12-02 08:14:23 +00:00
|
|
|
return ctx, nil
|
2021-09-28 06:40:05 +00:00
|
|
|
}
|