1
0
Fork 0
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:
shuting 2023-06-22 22:14:06 +08:00 committed by GitHub
parent c6e97c0ecc
commit 955570b0c5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 595 additions and 109 deletions

View file

@ -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)

View file

@ -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

View file

@ -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
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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))
}
}
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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}}'

View file

@ -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}}'

View file

@ -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}}'

View file

@ -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}}'

View file

@ -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