mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-28 02:18:15 +00:00
fix: auth checks with the APIVersion and the subresource (#7628)
* fix auth checks with apiVersion and subresource Signed-off-by: ShutingZhao <shuting@nirmata.com> * add kuttl tests Signed-off-by: ShutingZhao <shuting@nirmata.com> * remove duplicate code Signed-off-by: ShutingZhao <shuting@nirmata.com> * update permissions Signed-off-by: ShutingZhao <shuting@nirmata.com> --------- Signed-off-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
parent
c6e97c0ecc
commit
955570b0c5
26 changed files with 595 additions and 109 deletions
|
@ -29,7 +29,7 @@ type CanIOptions interface {
|
|||
type canIOptions struct {
|
||||
namespace string
|
||||
verb string
|
||||
kind string
|
||||
gvk string
|
||||
subresource string
|
||||
user string
|
||||
discovery Discovery
|
||||
|
@ -37,11 +37,11 @@ type canIOptions struct {
|
|||
}
|
||||
|
||||
// NewCanI returns a new instance of operation access controller evaluator
|
||||
func NewCanI(discovery Discovery, sarClient authorizationv1client.SubjectAccessReviewInterface, kind, namespace, verb, subresource string, user string) CanIOptions {
|
||||
func NewCanI(discovery Discovery, sarClient authorizationv1client.SubjectAccessReviewInterface, gvk, namespace, verb, subresource string, user string) CanIOptions {
|
||||
return &canIOptions{
|
||||
namespace: namespace,
|
||||
verb: verb,
|
||||
kind: kind,
|
||||
gvk: gvk,
|
||||
subresource: subresource,
|
||||
user: user,
|
||||
discovery: discovery,
|
||||
|
@ -58,18 +58,18 @@ func NewCanI(discovery Discovery, sarClient authorizationv1client.SubjectAccessR
|
|||
func (o *canIOptions) RunAccessCheck(ctx context.Context) (bool, error) {
|
||||
// get GroupVersionResource from RESTMapper
|
||||
// get GVR from kind
|
||||
apiVersion, kind := kubeutils.GetKindFromGVK(o.kind)
|
||||
apiVersion, kind := kubeutils.GetKindFromGVK(o.gvk)
|
||||
gv, err := schema.ParseGroupVersion(apiVersion)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to parse group/version %s", apiVersion)
|
||||
}
|
||||
gvr, err := o.discovery.GetGVRFromGVK(gv.WithKind(kind))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get GVR for kind %s", o.kind)
|
||||
return false, fmt.Errorf("failed to get GVR for kind %s", o.gvk)
|
||||
}
|
||||
if gvr.Empty() {
|
||||
// cannot find GVR
|
||||
return false, fmt.Errorf("failed to get the Group Version Resource for kind %s", o.kind)
|
||||
return false, fmt.Errorf("failed to get the Group Version Resource for kind %s", o.gvk)
|
||||
}
|
||||
logger := logger.WithValues("kind", kind, "namespace", o.namespace, "gvr", gvr.String(), "verb", o.verb)
|
||||
result, err := o.checker.Check(ctx, gvr.Group, gvr.Version, gvr.Resource, o.subresource, o.namespace, o.verb)
|
||||
|
|
|
@ -11,13 +11,13 @@ import (
|
|||
// Operations provides methods to performing operations on resource
|
||||
type Operations interface {
|
||||
// CanICreate returns 'true' if self can 'create' resource
|
||||
CanICreate(ctx context.Context, kind, namespace string) (bool, error)
|
||||
CanICreate(ctx context.Context, gvk, namespace, subresource string) (bool, error)
|
||||
// CanIUpdate returns 'true' if self can 'update' resource
|
||||
CanIUpdate(ctx context.Context, kind, namespace string) (bool, error)
|
||||
CanIUpdate(ctx context.Context, gvk, namespace, subresource string) (bool, error)
|
||||
// CanIDelete returns 'true' if self can 'delete' resource
|
||||
CanIDelete(ctx context.Context, kind, namespace string) (bool, error)
|
||||
CanIDelete(ctx context.Context, gvk, namespace, subresource string) (bool, error)
|
||||
// CanIGet returns 'true' if self can 'get' resource
|
||||
CanIGet(ctx context.Context, kind, namespace string) (bool, error)
|
||||
CanIGet(ctx context.Context, gvk, namespace, subresource string) (bool, error)
|
||||
}
|
||||
|
||||
// Auth provides implementation to check if caller/self/kyverno has access to perofrm operations
|
||||
|
@ -38,8 +38,8 @@ func NewAuth(client dclient.Interface, user string, log logr.Logger) *Auth {
|
|||
}
|
||||
|
||||
// CanICreate returns 'true' if self can 'create' resource
|
||||
func (a *Auth) CanICreate(ctx context.Context, kind, namespace string) (bool, error) {
|
||||
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "create", "", a.user)
|
||||
func (a *Auth) CanICreate(ctx context.Context, gvk, namespace, subresource string) (bool, error) {
|
||||
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), gvk, namespace, "create", "", a.user)
|
||||
ok, err := canI.RunAccessCheck(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -48,8 +48,8 @@ func (a *Auth) CanICreate(ctx context.Context, kind, namespace string) (bool, er
|
|||
}
|
||||
|
||||
// CanIUpdate returns 'true' if self can 'update' resource
|
||||
func (a *Auth) CanIUpdate(ctx context.Context, kind, namespace string) (bool, error) {
|
||||
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "update", "", a.user)
|
||||
func (a *Auth) CanIUpdate(ctx context.Context, gvk, namespace, subresource string) (bool, error) {
|
||||
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), gvk, namespace, "update", "", a.user)
|
||||
ok, err := canI.RunAccessCheck(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -58,8 +58,8 @@ func (a *Auth) CanIUpdate(ctx context.Context, kind, namespace string) (bool, er
|
|||
}
|
||||
|
||||
// CanIDelete returns 'true' if self can 'delete' resource
|
||||
func (a *Auth) CanIDelete(ctx context.Context, kind, namespace string) (bool, error) {
|
||||
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "delete", "", a.user)
|
||||
func (a *Auth) CanIDelete(ctx context.Context, gvk, namespace, subresource string) (bool, error) {
|
||||
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), gvk, namespace, "delete", "", a.user)
|
||||
ok, err := canI.RunAccessCheck(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -68,8 +68,8 @@ func (a *Auth) CanIDelete(ctx context.Context, kind, namespace string) (bool, er
|
|||
}
|
||||
|
||||
// CanIGet returns 'true' if self can 'get' resource
|
||||
func (a *Auth) CanIGet(ctx context.Context, kind, namespace string) (bool, error) {
|
||||
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "get", "", a.user)
|
||||
func (a *Auth) CanIGet(ctx context.Context, gvk, namespace, subresource string) (bool, error) {
|
||||
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), gvk, namespace, "get", "", a.user)
|
||||
ok, err := canI.RunAccessCheck(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
|
@ -12,21 +12,21 @@ func NewFakeAuth() *FakeAuth {
|
|||
}
|
||||
|
||||
// CanICreate returns 'true'
|
||||
func (a *FakeAuth) CanICreate(_ context.Context, kind, namespace string) (bool, error) {
|
||||
func (a *FakeAuth) CanICreate(_ context.Context, kind, namespace, sub string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CanIUpdate returns 'true'
|
||||
func (a *FakeAuth) CanIUpdate(_ context.Context, kind, namespace string) (bool, error) {
|
||||
func (a *FakeAuth) CanIUpdate(_ context.Context, kind, namespace, sub string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CanIDelete returns 'true'
|
||||
func (a *FakeAuth) CanIDelete(_ context.Context, kind, namespace string) (bool, error) {
|
||||
func (a *FakeAuth) CanIDelete(_ context.Context, kind, namespace, sub string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CanIGet returns 'true'
|
||||
func (a *FakeAuth) CanIGet(_ context.Context, kind, namespace string) (bool, error) {
|
||||
func (a *FakeAuth) CanIGet(_ context.Context, kind, namespace, sub string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
@ -3,13 +3,13 @@ package generate
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
"github.com/kyverno/kyverno/pkg/engine/variables/regex"
|
||||
"github.com/kyverno/kyverno/pkg/policy/common"
|
||||
datautils "github.com/kyverno/kyverno/pkg/utils/data"
|
||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||
"github.com/kyverno/kyverno/pkg/utils/wildcard"
|
||||
)
|
||||
|
@ -73,11 +73,6 @@ func (g *Generate) Validate(ctx context.Context) (string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if !datautils.DeepEqual(rule.Clone, kyvernov1.CloneFrom{}) {
|
||||
if path, err := g.validateClone(rule.Clone, rule.CloneList, kind); err != nil {
|
||||
return fmt.Sprintf("clone.%s", path), err
|
||||
}
|
||||
}
|
||||
if target := rule.GetData(); target != nil {
|
||||
// TODO: is this required ?? as anchors can only be on pattern and not resource
|
||||
// we can add this check by not sure if its needed here
|
||||
|
@ -93,90 +88,55 @@ func (g *Generate) Validate(ctx context.Context) (string, error) {
|
|||
// If kind and namespace contain variables, then we cannot resolve then so we skip the processing
|
||||
if len(rule.CloneList.Kinds) != 0 {
|
||||
for _, kind = range rule.CloneList.Kinds {
|
||||
_, kind = kubeutils.GetKindFromGVK(kind)
|
||||
if err := g.canIGenerate(ctx, kind, namespace); err != nil {
|
||||
gvk, sub := parseCloneKind(kind)
|
||||
if err := g.canIGenerate(ctx, gvk, namespace, sub); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := g.canIGenerate(ctx, kind, namespace); err != nil {
|
||||
k, sub := kubeutils.SplitSubresource(kind)
|
||||
if err := g.canIGenerate(ctx, strings.Join([]string{apiVersion, k}, "/"), namespace, sub); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (g *Generate) validateClone(c kyvernov1.CloneFrom, cl kyvernov1.CloneList, kind string) (string, error) {
|
||||
if len(cl.Kinds) == 0 {
|
||||
if c.Name == "" {
|
||||
return "name", fmt.Errorf("name cannot be empty")
|
||||
}
|
||||
}
|
||||
|
||||
namespace := c.Namespace
|
||||
if !regex.IsVariable(namespace) {
|
||||
namespace = ""
|
||||
}
|
||||
// Skip if there is variable defined
|
||||
if !regex.IsVariable(kind) {
|
||||
// GET
|
||||
ok, err := g.authCheck.CanIGet(context.TODO(), kind, namespace)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !ok {
|
||||
return "", fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Grant proper permissions to the background controller", kind, namespace)
|
||||
}
|
||||
} else {
|
||||
g.log.V(2).Info("resource Kind uses variables, so cannot be resolved. Skipping Auth Checks.")
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// canIGenerate returns a error if kyverno cannot perform operations
|
||||
func (g *Generate) canIGenerate(ctx context.Context, kind, namespace string) error {
|
||||
func (g *Generate) canIGenerate(ctx context.Context, gvk, namespace, subresource string) error {
|
||||
// Skip if there is variable defined
|
||||
authCheck := g.authCheck
|
||||
if !regex.IsVariable(namespace) {
|
||||
namespace = ""
|
||||
}
|
||||
if !regex.IsVariable(kind) {
|
||||
// CREATE
|
||||
ok, err := authCheck.CanICreate(ctx, kind, namespace)
|
||||
if !regex.IsVariable(gvk) {
|
||||
ok, err := authCheck.CanICreate(ctx, gvk, namespace, subresource)
|
||||
if err != nil {
|
||||
// machinery error
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("kyverno does not have permissions to 'create' resource %s/%s. Grant proper permissions to the background controller", kind, namespace)
|
||||
}
|
||||
// UPDATE
|
||||
ok, err = authCheck.CanIUpdate(ctx, kind, namespace)
|
||||
if err != nil {
|
||||
// machinery error
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("kyverno does not have permissions to 'update' resource %s/%s. Grant proper permissions to the background controller", kind, namespace)
|
||||
}
|
||||
// GET
|
||||
ok, err = authCheck.CanIGet(ctx, kind, namespace)
|
||||
if err != nil {
|
||||
// machinery error
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Grant proper permissions to the background controller", kind, namespace)
|
||||
return fmt.Errorf("kyverno does not have permissions to 'create' resource %s/%s/%s. Grant proper permissions to the background controller", gvk, subresource, namespace)
|
||||
}
|
||||
|
||||
// DELETE
|
||||
ok, err = authCheck.CanIDelete(ctx, kind, namespace)
|
||||
ok, err = authCheck.CanIUpdate(ctx, gvk, namespace, subresource)
|
||||
if err != nil {
|
||||
// machinery error
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("kyverno does not have permissions to 'delete' resource %s/%s. Grant proper permissions to the background controller", kind, namespace)
|
||||
return fmt.Errorf("kyverno does not have permissions to 'update' resource %s/%s/%s. Grant proper permissions to the background controller", gvk, subresource, namespace)
|
||||
}
|
||||
|
||||
ok, err = authCheck.CanIGet(ctx, gvk, namespace, subresource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s/%s. Grant proper permissions to the background controller", gvk, subresource, namespace)
|
||||
}
|
||||
|
||||
ok, err = authCheck.CanIDelete(ctx, gvk, namespace, subresource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("kyverno does not have permissions to 'delete' resource %s/%s/%s. Grant proper permissions to the background controller", gvk, subresource, namespace)
|
||||
}
|
||||
} else {
|
||||
g.log.V(2).Info("resource Kind uses variables, so cannot be resolved. Skipping Auth Checks.")
|
||||
|
@ -184,3 +144,12 @@ func (g *Generate) canIGenerate(ctx context.Context, kind, namespace string) err
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseCloneKind(gvks string) (gvk, sub string) {
|
||||
gv, ks := kubeutils.GetKindFromGVK(gvks)
|
||||
k, sub := kubeutils.SplitSubresource(ks)
|
||||
if !strings.Contains(gv, "*") {
|
||||
k = strings.Join([]string{gv, k}, "/")
|
||||
}
|
||||
return k, sub
|
||||
}
|
||||
|
|
|
@ -13,26 +13,20 @@ type authChecker struct {
|
|||
}
|
||||
|
||||
type AuthChecker interface {
|
||||
CanICreate(ctx context.Context, kind, namespace, subresource string) (bool, error)
|
||||
CanIUpdate(ctx context.Context, kind, namespace, subresource string) (bool, error)
|
||||
CanIGet(ctx context.Context, kind, namespace, subresource string) (bool, error)
|
||||
CanIUpdate(ctx context.Context, gvks, namespace, subresource string) (bool, error)
|
||||
CanIGet(ctx context.Context, gvks, namespace, subresource string) (bool, error)
|
||||
}
|
||||
|
||||
func newAuthChecker(client dclient.Interface, user string) AuthChecker {
|
||||
return &authChecker{client: client, user: user}
|
||||
}
|
||||
|
||||
func (a *authChecker) CanICreate(ctx context.Context, kind, namespace, subresource string) (bool, error) {
|
||||
checker := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "create", subresource, a.user)
|
||||
func (a *authChecker) CanIUpdate(ctx context.Context, gvk, namespace, subresource string) (bool, error) {
|
||||
checker := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), gvk, namespace, "update", subresource, a.user)
|
||||
return checker.RunAccessCheck(ctx)
|
||||
}
|
||||
|
||||
func (a *authChecker) CanIUpdate(ctx context.Context, kind, namespace, subresource string) (bool, error) {
|
||||
checker := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "update", subresource, a.user)
|
||||
return checker.RunAccessCheck(ctx)
|
||||
}
|
||||
|
||||
func (a *authChecker) CanIGet(ctx context.Context, kind, namespace, subresource string) (bool, error) {
|
||||
checker := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "get", subresource, a.user)
|
||||
func (a *authChecker) CanIGet(ctx context.Context, gvk, namespace, subresource string) (bool, error) {
|
||||
checker := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), gvk, namespace, "get", subresource, a.user)
|
||||
return checker.RunAccessCheck(ctx)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package mutate
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
|
@ -102,16 +103,16 @@ func (m *Mutate) validateAuth(ctx context.Context, targets []kyvernov1.TargetRes
|
|||
srcKey = srcKey + "/" + sub
|
||||
}
|
||||
|
||||
if ok, err := m.authChecker.CanIUpdate(ctx, k, target.Namespace, sub); err != nil {
|
||||
if ok, err := m.authChecker.CanIUpdate(ctx, strings.Join([]string{target.APIVersion, k}, "/"), target.Namespace, sub); err != nil {
|
||||
errs = append(errs, err)
|
||||
} else if !ok {
|
||||
errs = append(errs, fmt.Errorf("cannot %s %s in namespace %s", "update", srcKey, target.Namespace))
|
||||
errs = append(errs, fmt.Errorf("cannot %s/%s/%s in namespace %s", "update", target.APIVersion, srcKey, target.Namespace))
|
||||
}
|
||||
|
||||
if ok, err := m.authChecker.CanIGet(ctx, k, target.Namespace, sub); err != nil {
|
||||
if ok, err := m.authChecker.CanIGet(ctx, strings.Join([]string{target.APIVersion, k}, "/"), target.Namespace, sub); err != nil {
|
||||
errs = append(errs, err)
|
||||
} else if !ok {
|
||||
errs = append(errs, fmt.Errorf("cannot %s %s in namespace %s", "get", srcKey, target.Namespace))
|
||||
errs = append(errs, fmt.Errorf("cannot %s/%s/%s in namespace %s", "get", target.APIVersion, srcKey, target.Namespace))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kyverno:background-controller:manage-policy
|
||||
labels:
|
||||
app.kubernetes.io/component: background-controller
|
||||
app.kubernetes.io/instance: kyverno
|
||||
app.kubernetes.io/part-of: kyverno
|
||||
rules:
|
||||
- apiGroups:
|
||||
- kyverno.io
|
||||
resources:
|
||||
- policies
|
||||
verbs:
|
||||
- create
|
||||
- update
|
||||
- delete
|
||||
- get
|
|
@ -0,0 +1,321 @@
|
|||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.4
|
||||
name: policies.k8s.nginx.org
|
||||
spec:
|
||||
conversion:
|
||||
strategy: None
|
||||
group: k8s.nginx.org
|
||||
names:
|
||||
kind: Policy
|
||||
listKind: PolicyList
|
||||
plural: policies
|
||||
shortNames:
|
||||
- pol
|
||||
singular: policy
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- description: Current state of the Policy. If the resource has a valid status,
|
||||
it means it has been validated and accepted by the Ingress Controller.
|
||||
jsonPath: .status.state
|
||||
name: State
|
||||
type: string
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
name: v1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Policy defines a Policy for VirtualServer and VirtualServerRoute
|
||||
resources.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: PolicySpec is the spec of the Policy resource. The spec includes
|
||||
multiple fields, where each field represents a different policy. Only
|
||||
one policy (field) is allowed.
|
||||
properties:
|
||||
accessControl:
|
||||
description: AccessControl defines an access policy based on the source
|
||||
IP of a request.
|
||||
properties:
|
||||
allow:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
deny:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
basicAuth:
|
||||
description: 'BasicAuth holds HTTP Basic authentication configuration
|
||||
policy status: preview'
|
||||
properties:
|
||||
realm:
|
||||
type: string
|
||||
secret:
|
||||
type: string
|
||||
type: object
|
||||
egressMTLS:
|
||||
description: EgressMTLS defines an Egress MTLS policy.
|
||||
properties:
|
||||
ciphers:
|
||||
type: string
|
||||
protocols:
|
||||
type: string
|
||||
serverName:
|
||||
type: boolean
|
||||
sessionReuse:
|
||||
type: boolean
|
||||
sslName:
|
||||
type: string
|
||||
tlsSecret:
|
||||
type: string
|
||||
trustedCertSecret:
|
||||
type: string
|
||||
verifyDepth:
|
||||
type: integer
|
||||
verifyServer:
|
||||
type: boolean
|
||||
type: object
|
||||
ingressClassName:
|
||||
type: string
|
||||
ingressMTLS:
|
||||
description: IngressMTLS defines an Ingress MTLS policy.
|
||||
properties:
|
||||
clientCertSecret:
|
||||
type: string
|
||||
crlFileName:
|
||||
type: string
|
||||
verifyClient:
|
||||
type: string
|
||||
verifyDepth:
|
||||
type: integer
|
||||
type: object
|
||||
jwt:
|
||||
description: JWTAuth holds JWT authentication configuration.
|
||||
properties:
|
||||
jwksURI:
|
||||
type: string
|
||||
keyCache:
|
||||
type: string
|
||||
realm:
|
||||
type: string
|
||||
secret:
|
||||
type: string
|
||||
token:
|
||||
type: string
|
||||
type: object
|
||||
oidc:
|
||||
description: OIDC defines an Open ID Connect policy.
|
||||
properties:
|
||||
accessTokenEnable:
|
||||
type: boolean
|
||||
authEndpoint:
|
||||
type: string
|
||||
authExtraArgs:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
clientID:
|
||||
type: string
|
||||
clientSecret:
|
||||
type: string
|
||||
jwksURI:
|
||||
type: string
|
||||
redirectURI:
|
||||
type: string
|
||||
scope:
|
||||
type: string
|
||||
tokenEndpoint:
|
||||
type: string
|
||||
zoneSyncLeeway:
|
||||
type: integer
|
||||
type: object
|
||||
rateLimit:
|
||||
description: RateLimit defines a rate limit policy.
|
||||
properties:
|
||||
burst:
|
||||
type: integer
|
||||
delay:
|
||||
type: integer
|
||||
dryRun:
|
||||
type: boolean
|
||||
key:
|
||||
type: string
|
||||
logLevel:
|
||||
type: string
|
||||
noDelay:
|
||||
type: boolean
|
||||
rate:
|
||||
type: string
|
||||
rejectCode:
|
||||
type: integer
|
||||
zoneSize:
|
||||
type: string
|
||||
type: object
|
||||
waf:
|
||||
description: WAF defines an WAF policy.
|
||||
properties:
|
||||
apBundle:
|
||||
type: string
|
||||
apPolicy:
|
||||
type: string
|
||||
enable:
|
||||
type: boolean
|
||||
securityLog:
|
||||
description: SecurityLog defines the security log of a WAF policy.
|
||||
properties:
|
||||
apLogConf:
|
||||
type: string
|
||||
enable:
|
||||
type: boolean
|
||||
logDest:
|
||||
type: string
|
||||
type: object
|
||||
securityLogs:
|
||||
items:
|
||||
description: SecurityLog defines the security log of a WAF policy.
|
||||
properties:
|
||||
apLogConf:
|
||||
type: string
|
||||
enable:
|
||||
type: boolean
|
||||
logDest:
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
status:
|
||||
description: PolicyStatus is the status of the policy resource
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
reason:
|
||||
type: string
|
||||
state:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Policy defines a Policy for VirtualServer and VirtualServerRoute
|
||||
resources.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: PolicySpec is the spec of the Policy resource. The spec includes
|
||||
multiple fields, where each field represents a different policy. Only
|
||||
one policy (field) is allowed.
|
||||
properties:
|
||||
accessControl:
|
||||
description: AccessControl defines an access policy based on the source
|
||||
IP of a request.
|
||||
properties:
|
||||
allow:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
deny:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
egressMTLS:
|
||||
description: EgressMTLS defines an Egress MTLS policy.
|
||||
properties:
|
||||
ciphers:
|
||||
type: string
|
||||
protocols:
|
||||
type: string
|
||||
serverName:
|
||||
type: boolean
|
||||
sessionReuse:
|
||||
type: boolean
|
||||
sslName:
|
||||
type: string
|
||||
tlsSecret:
|
||||
type: string
|
||||
trustedCertSecret:
|
||||
type: string
|
||||
verifyDepth:
|
||||
type: integer
|
||||
verifyServer:
|
||||
type: boolean
|
||||
type: object
|
||||
ingressMTLS:
|
||||
description: IngressMTLS defines an Ingress MTLS policy.
|
||||
properties:
|
||||
clientCertSecret:
|
||||
type: string
|
||||
verifyClient:
|
||||
type: string
|
||||
verifyDepth:
|
||||
type: integer
|
||||
type: object
|
||||
jwt:
|
||||
description: JWTAuth holds JWT authentication configuration.
|
||||
properties:
|
||||
realm:
|
||||
type: string
|
||||
secret:
|
||||
type: string
|
||||
token:
|
||||
type: string
|
||||
type: object
|
||||
rateLimit:
|
||||
description: RateLimit defines a rate limit policy.
|
||||
properties:
|
||||
burst:
|
||||
type: integer
|
||||
delay:
|
||||
type: integer
|
||||
dryRun:
|
||||
type: boolean
|
||||
key:
|
||||
type: string
|
||||
logLevel:
|
||||
type: string
|
||||
noDelay:
|
||||
type: boolean
|
||||
rate:
|
||||
type: string
|
||||
rejectCode:
|
||||
type: integer
|
||||
zoneSize:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: false
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: policies.k8s.nginx.org
|
||||
spec: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: Policy
|
||||
listKind: PolicyList
|
||||
plural: policies
|
||||
singular: policy
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- file: policy-1.yaml
|
||||
shouldFail: false
|
||||
- file: policy-1-subresource.yaml
|
||||
shouldFail: false
|
||||
- file: policy-2.yaml
|
||||
shouldFail: true
|
||||
- file: policy-2-subresource.yaml
|
||||
shouldFail: true
|
|
@ -0,0 +1,12 @@
|
|||
## Description
|
||||
|
||||
This test ensures that the auth checks for generate policy is performed against given the APIVersion and the subresource.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
The test fails if the policy that generates `k8s.nginx.org/v1/policy` and its subresource can be created, otherwise passes.
|
||||
|
||||
|
||||
## Reference Issue(s)
|
||||
|
||||
https://github.com/kyverno/kyverno/issues/7618
|
|
@ -0,0 +1,34 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: generate-same-kind-pol-1
|
||||
spec:
|
||||
rules:
|
||||
- name: generate-add-labels-policy
|
||||
match:
|
||||
all:
|
||||
- resources:
|
||||
kinds:
|
||||
- Namespace
|
||||
generate:
|
||||
synchronize: true
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: Policy/status
|
||||
name: add-labels-policy
|
||||
namespace: '{{request.object.metadata.name}}'
|
||||
data:
|
||||
spec:
|
||||
rules:
|
||||
- name: add-labels
|
||||
match:
|
||||
all:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
- Service
|
||||
- PersistentVolumeClaim
|
||||
mutate:
|
||||
patchStrategicMerge:
|
||||
metadata:
|
||||
labels:
|
||||
AppID: '{{request.object.metadata.labels.AppID}}'
|
|
@ -0,0 +1,34 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: generate-same-kind-pol-2
|
||||
spec:
|
||||
rules:
|
||||
- name: generate-add-labels-policy
|
||||
match:
|
||||
all:
|
||||
- resources:
|
||||
kinds:
|
||||
- Namespace
|
||||
generate:
|
||||
synchronize: true
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: Policy
|
||||
name: add-labels-policy
|
||||
namespace: '{{request.object.metadata.name}}'
|
||||
data:
|
||||
spec:
|
||||
rules:
|
||||
- name: add-labels
|
||||
match:
|
||||
all:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
- Service
|
||||
- PersistentVolumeClaim
|
||||
mutate:
|
||||
patchStrategicMerge:
|
||||
metadata:
|
||||
labels:
|
||||
AppID: '{{request.object.metadata.labels.AppID}}'
|
|
@ -0,0 +1,34 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: generate-same-kind-pol-3
|
||||
spec:
|
||||
rules:
|
||||
- name: generate-add-labels-policy
|
||||
match:
|
||||
all:
|
||||
- resources:
|
||||
kinds:
|
||||
- Namespace
|
||||
generate:
|
||||
synchronize: true
|
||||
apiVersion: k8s.nginx.org/v1
|
||||
kind: Policy/status
|
||||
name: add-labels-policy
|
||||
namespace: '{{request.object.metadata.name}}'
|
||||
data:
|
||||
spec:
|
||||
rules:
|
||||
- name: add-labels
|
||||
match:
|
||||
all:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
- Service
|
||||
- PersistentVolumeClaim
|
||||
mutate:
|
||||
patchStrategicMerge:
|
||||
metadata:
|
||||
labels:
|
||||
AppID: '{{request.object.metadata.labels.AppID}}'
|
|
@ -0,0 +1,34 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: generate-same-kind-pol-4
|
||||
spec:
|
||||
rules:
|
||||
- name: generate-add-labels-policy
|
||||
match:
|
||||
all:
|
||||
- resources:
|
||||
kinds:
|
||||
- Namespace
|
||||
generate:
|
||||
synchronize: true
|
||||
apiVersion: k8s.nginx.org/v1
|
||||
kind: Policy
|
||||
name: add-labels-policy
|
||||
namespace: '{{request.object.metadata.name}}'
|
||||
data:
|
||||
spec:
|
||||
rules:
|
||||
- name: add-labels
|
||||
match:
|
||||
all:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
- Service
|
||||
- PersistentVolumeClaim
|
||||
mutate:
|
||||
patchStrategicMerge:
|
||||
metadata:
|
||||
labels:
|
||||
AppID: '{{request.object.metadata.labels.AppID}}'
|
|
@ -1,20 +1,33 @@
|
|||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kyverno:background-controller:additional
|
||||
name: kyverno:background-controller:manage-ns-crossplane-role
|
||||
labels:
|
||||
app.kubernetes.io/component: background-controller
|
||||
app.kubernetes.io/instance: kyverno
|
||||
app.kubernetes.io/part-of: kyverno
|
||||
rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
- ""
|
||||
- "iam.aws.crossplane.io"
|
||||
resources:
|
||||
- namespaces
|
||||
- roles
|
||||
verbs:
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: kyverno:manage-ns-crossplane-role
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: kyverno:background-controller:manage-ns-crossplane-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kyverno-background-controller
|
||||
namespace: kyverno
|
Loading…
Add table
Reference in a new issue