mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
feat: new access checks for background policies (#6970)
* switch to use sar for access checks Signed-off-by: ShutingZhao <shuting@nirmata.com> * fix unit tests Signed-off-by: ShutingZhao <shuting@nirmata.com> * update helm config Signed-off-by: ShutingZhao <shuting@nirmata.com> * fix username Signed-off-by: ShutingZhao <shuting@nirmata.com> * update msg Signed-off-by: ShutingZhao <shuting@nirmata.com> * fix sa name Signed-off-by: ShutingZhao <shuting@nirmata.com> * update install.yaml Signed-off-by: ShutingZhao <shuting@nirmata.com> --------- Signed-off-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
parent
f9578ed582
commit
e14fe847bc
23 changed files with 122 additions and 90 deletions
|
@ -88,6 +88,12 @@ rules:
|
|||
- create
|
||||
- update
|
||||
- patch
|
||||
- apiGroups:
|
||||
- authorization.k8s.io
|
||||
resources:
|
||||
- subjectaccessreviews
|
||||
verbs:
|
||||
- create
|
||||
- apiGroups:
|
||||
- '*'
|
||||
resources:
|
||||
|
|
|
@ -120,7 +120,7 @@ spec:
|
|||
image: {{ include "kyverno.image" (dict "image" .Values.admissionController.container.image "defaultTag" .Chart.AppVersion) | quote }}
|
||||
imagePullPolicy: {{ .Values.admissionController.container.image.pullPolicy }}
|
||||
args:
|
||||
- --backgroundServiceAccountName='system:serviceaccount:{{ include "kyverno.namespace" . }}:{{ include "kyverno.background-controller.serviceAccountName" . }}'
|
||||
- --backgroundServiceAccountName=system:serviceaccount:{{ include "kyverno.namespace" . }}:{{ include "kyverno.background-controller.serviceAccountName" . }}
|
||||
- --servicePort={{ .Values.admissionController.service.port }}
|
||||
- --loggingFormat={{ .Values.admissionController.logging.format }}
|
||||
- --v={{ .Values.admissionController.logging.verbosity }}
|
||||
|
|
|
@ -14,8 +14,5 @@ subjects:
|
|||
- kind: ServiceAccount
|
||||
name: {{ template "kyverno.background-controller.serviceAccountName" . }}
|
||||
namespace: {{ template "kyverno.namespace" . }}
|
||||
- kind: ServiceAccount
|
||||
name: {{ template "kyverno.admission-controller.serviceAccountName" . }}
|
||||
namespace: {{ template "kyverno.namespace" . }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
|
|
@ -73,6 +73,12 @@ rules:
|
|||
- create
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- authorization.k8s.io
|
||||
resources:
|
||||
- subjectaccessreviews
|
||||
verbs:
|
||||
- create
|
||||
{{- with .Values.cleanupController.rbac.clusterRole.extraResources }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
|
|
|
@ -113,6 +113,8 @@ spec:
|
|||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: KYVERNO_SERVICEACCOUNT_NAME
|
||||
value: {{ template "kyverno.cleanup-controller.serviceAccountName" . }}
|
||||
- name: KYVERNO_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
|
|
|
@ -388,7 +388,7 @@ func (c *ApplyCommandConfig) applyCommandHelper() (rc *common.ResultCounts, reso
|
|||
skipInvalidPolicies.invalid = make([]string, 0)
|
||||
|
||||
for _, policy := range policies {
|
||||
_, err := policy2.Validate(policy, nil, nil, true, openApiManager)
|
||||
_, err := policy2.Validate(policy, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName()))
|
||||
if err != nil {
|
||||
log.Log.Error(err, "policy validation error")
|
||||
if strings.HasPrefix(err.Error(), "variable 'element.name'") {
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/google/go-containerregistry/pkg/v1/static"
|
||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/common"
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
"github.com/kyverno/kyverno/pkg/openapi"
|
||||
policyvalidation "github.com/kyverno/kyverno/pkg/policy"
|
||||
policyutils "github.com/kyverno/kyverno/pkg/utils/policy"
|
||||
|
@ -47,7 +48,7 @@ kyverno oci push -p policies. -i <imgref>`,
|
|||
return fmt.Errorf("creating openapi manager: %v", err)
|
||||
}
|
||||
for _, policy := range policies {
|
||||
if _, err := policyvalidation.Validate(policy, nil, nil, true, openApiManager); err != nil {
|
||||
if _, err := policyvalidation.Validate(policy, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName())); err != nil {
|
||||
return fmt.Errorf("validating policy %s: %v", policy.GetName(), err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/kyverno/kyverno/pkg/autogen"
|
||||
"github.com/kyverno/kyverno/pkg/background/generate"
|
||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
"github.com/kyverno/kyverno/pkg/openapi"
|
||||
policy2 "github.com/kyverno/kyverno/pkg/policy"
|
||||
|
@ -178,7 +179,7 @@ func applyPoliciesFromPath(
|
|||
}
|
||||
|
||||
for _, policy := range policies {
|
||||
_, err := policy2.Validate(policy, nil, nil, true, openApiManager)
|
||||
_, err := policy2.Validate(policy, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName()))
|
||||
if err != nil {
|
||||
log.Log.Error(err, "skipping invalid policy", "name", policy.GetName())
|
||||
continue
|
||||
|
|
|
@ -408,6 +408,7 @@ func main() {
|
|||
policyHandlers := webhookspolicy.NewHandlers(
|
||||
setup.KyvernoDynamicClient,
|
||||
openApiManager,
|
||||
backgroundServiceAccountName,
|
||||
)
|
||||
resourceHandlers := webhooksresource.NewHandlers(
|
||||
engine,
|
||||
|
|
|
@ -33962,6 +33962,12 @@ rules:
|
|||
- create
|
||||
- update
|
||||
- patch
|
||||
- apiGroups:
|
||||
- authorization.k8s.io
|
||||
resources:
|
||||
- subjectaccessreviews
|
||||
verbs:
|
||||
- create
|
||||
- apiGroups:
|
||||
- '*'
|
||||
resources:
|
||||
|
@ -34143,6 +34149,12 @@ rules:
|
|||
- create
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- authorization.k8s.io
|
||||
resources:
|
||||
- subjectaccessreviews
|
||||
verbs:
|
||||
- create
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
|
@ -34444,9 +34456,6 @@ subjects:
|
|||
- kind: ServiceAccount
|
||||
name: kyverno-background-controller
|
||||
namespace: kyverno
|
||||
- kind: ServiceAccount
|
||||
name: kyverno-admission-controller
|
||||
namespace: kyverno
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
|
@ -34937,7 +34946,7 @@ spec:
|
|||
image: "ghcr.io/kyverno/kyverno:latest"
|
||||
imagePullPolicy: IfNotPresent
|
||||
args:
|
||||
- --backgroundServiceAccountName='system:serviceaccount:kyverno:kyverno-background-controller'
|
||||
- --backgroundServiceAccountName=system:serviceaccount:kyverno:kyverno-background-controller
|
||||
- --servicePort=443
|
||||
- --loggingFormat=text
|
||||
- --v=2
|
||||
|
@ -35179,6 +35188,8 @@ spec:
|
|||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: KYVERNO_SERVICEACCOUNT_NAME
|
||||
value: kyverno-cleanup-controller
|
||||
- name: KYVERNO_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
|
|
|
@ -23,7 +23,7 @@ type CanIOptions interface {
|
|||
// - can only evaluate a single verb
|
||||
// - group version resource is determined from the kind using the discovery client REST mapper
|
||||
// - If disallowed, the reason and evaluationError is available in the logs
|
||||
// - each can generates a SelfSubjectAccessReview resource and response is evaluated for permissions
|
||||
// - each can generates a SubjectAccessReview resource and response is evaluated for permissions
|
||||
RunAccessCheck(context.Context) (bool, error)
|
||||
}
|
||||
|
||||
|
@ -32,19 +32,21 @@ type canIOptions struct {
|
|||
verb string
|
||||
kind string
|
||||
subresource string
|
||||
user string
|
||||
discovery Discovery
|
||||
ssarClient authorizationv1client.SelfSubjectAccessReviewInterface
|
||||
sarClient authorizationv1client.SubjectAccessReviewInterface
|
||||
}
|
||||
|
||||
// NewCanI returns a new instance of operation access controller evaluator
|
||||
func NewCanI(discovery Discovery, ssarClient authorizationv1client.SelfSubjectAccessReviewInterface, kind, namespace, verb, subresource string) CanIOptions {
|
||||
func NewCanI(discovery Discovery, sarClient authorizationv1client.SubjectAccessReviewInterface, kind, namespace, verb, subresource string, user string) CanIOptions {
|
||||
return &canIOptions{
|
||||
namespace: namespace,
|
||||
verb: verb,
|
||||
kind: kind,
|
||||
subresource: subresource,
|
||||
user: user,
|
||||
discovery: discovery,
|
||||
ssarClient: ssarClient,
|
||||
sarClient: sarClient,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,8 +74,8 @@ func (o *canIOptions) RunAccessCheck(ctx context.Context) (bool, error) {
|
|||
return false, fmt.Errorf("failed to get the Group Version Resource for kind %s", o.kind)
|
||||
}
|
||||
|
||||
sar := &authorizationv1.SelfSubjectAccessReview{
|
||||
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
|
||||
sar := &authorizationv1.SubjectAccessReview{
|
||||
Spec: authorizationv1.SubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationv1.ResourceAttributes{
|
||||
Namespace: o.namespace,
|
||||
Verb: o.verb,
|
||||
|
@ -81,17 +83,12 @@ func (o *canIOptions) RunAccessCheck(ctx context.Context) (bool, error) {
|
|||
Resource: gvr.Resource,
|
||||
Subresource: o.subresource,
|
||||
},
|
||||
User: o.user,
|
||||
},
|
||||
}
|
||||
// Set self subject access review
|
||||
// - namespace
|
||||
// - verb
|
||||
// - resource
|
||||
// - subresource
|
||||
logger := logger.WithValues("kind", sar.Kind, "namespace", sar.Namespace, "name", sar.Name)
|
||||
|
||||
// Create the Resource
|
||||
resp, err := o.ssarClient.Create(ctx, sar, metav1.CreateOptions{})
|
||||
logger := logger.WithValues("kind", sar.Kind, "namespace", sar.Namespace, "name", sar.Name, "gvr", gvr.String())
|
||||
resp, err := o.sarClient.Create(ctx, sar, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to create resource")
|
||||
return false, err
|
||||
|
@ -100,7 +97,6 @@ func (o *canIOptions) RunAccessCheck(ctx context.Context) (bool, error) {
|
|||
if !resp.Status.Allowed {
|
||||
reason := resp.Status.Reason
|
||||
evaluationError := resp.Status.EvaluationError
|
||||
// Reporting ? (just logs)
|
||||
logger.Info("disallowed operation", "reason", reason, "evaluationError", evaluationError)
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ func TestNewCanI(t *testing.T) {
|
|||
}}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := NewCanI(tt.args.client.Discovery(), tt.args.client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), tt.args.kind, tt.args.namespace, tt.args.verb, "")
|
||||
got := NewCanI(tt.args.client.Discovery(), tt.args.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), tt.args.kind, tt.args.namespace, tt.args.verb, "", "admin")
|
||||
assert.NotNil(t, got)
|
||||
})
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ func TestCanIOptions_DiscoveryError(t *testing.T) {
|
|||
}}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
o := NewCanI(tt.fields.discovery, nil, tt.fields.kind, tt.fields.namespace, tt.fields.verb, "")
|
||||
o := NewCanI(tt.fields.discovery, nil, tt.fields.kind, tt.fields.namespace, tt.fields.verb, "", "admin")
|
||||
got, err := o.RunAccessCheck(context.TODO())
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
|
@ -83,19 +83,19 @@ func TestCanIOptions_DiscoveryError(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
type ssar struct{}
|
||||
type sar struct{}
|
||||
|
||||
func (d *ssar) Create(_ context.Context, _ *v1.SelfSubjectAccessReview, _ metav1.CreateOptions) (*v1.SelfSubjectAccessReview, error) {
|
||||
func (d *sar) Create(_ context.Context, _ *v1.SubjectAccessReview, _ metav1.CreateOptions) (*v1.SubjectAccessReview, error) {
|
||||
return nil, errors.New("dummy")
|
||||
}
|
||||
|
||||
func TestCanIOptions_SsarError(t *testing.T) {
|
||||
type fields struct {
|
||||
namespace string
|
||||
verb string
|
||||
kind string
|
||||
discovery Discovery
|
||||
ssarClient authorizationv1client.SelfSubjectAccessReviewInterface
|
||||
namespace string
|
||||
verb string
|
||||
kind string
|
||||
discovery Discovery
|
||||
sarClient authorizationv1client.SubjectAccessReviewInterface
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -105,18 +105,18 @@ func TestCanIOptions_SsarError(t *testing.T) {
|
|||
}{{
|
||||
name: "deployments",
|
||||
fields: fields{
|
||||
discovery: dclient.NewEmptyFakeClient().Discovery(),
|
||||
ssarClient: &ssar{},
|
||||
kind: "Deployment",
|
||||
namespace: "default",
|
||||
verb: "test",
|
||||
discovery: dclient.NewEmptyFakeClient().Discovery(),
|
||||
sarClient: &sar{},
|
||||
kind: "Deployment",
|
||||
namespace: "default",
|
||||
verb: "test",
|
||||
},
|
||||
want: false,
|
||||
wantErr: true,
|
||||
}}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
o := NewCanI(tt.fields.discovery, tt.fields.ssarClient, tt.fields.kind, tt.fields.namespace, tt.fields.verb, "")
|
||||
o := NewCanI(tt.fields.discovery, tt.fields.sarClient, tt.fields.kind, tt.fields.namespace, tt.fields.verb, "", "admin")
|
||||
got, err := o.RunAccessCheck(context.TODO())
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
|
@ -173,7 +173,7 @@ func TestCanIOptions_RunAccessCheck(t *testing.T) {
|
|||
}}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
o := NewCanI(tt.fields.client.Discovery(), tt.fields.client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), tt.fields.kind, tt.fields.namespace, tt.fields.verb, "")
|
||||
o := NewCanI(tt.fields.client.Discovery(), tt.fields.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), tt.fields.kind, tt.fields.namespace, tt.fields.verb, "", "admin")
|
||||
got, err := o.RunAccessCheck(context.TODO())
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
|
|
|
@ -2,6 +2,7 @@ package config
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
|
@ -135,6 +136,10 @@ func KyvernoMetricsConfigMapName() string {
|
|||
return kyvernoMetricsConfigMapName
|
||||
}
|
||||
|
||||
func KyvernoUserName(serviceaccount string) string {
|
||||
return fmt.Sprintf("system:serviceaccount:%s:%s", kyvernoNamespace, serviceaccount)
|
||||
}
|
||||
|
||||
// Configuration to be used by consumer to check filters
|
||||
type Configuration interface {
|
||||
// GetDefaultRegistry return default image registry
|
||||
|
|
|
@ -173,7 +173,7 @@ func (h validateManifestHandler) verifyManifest(
|
|||
}
|
||||
|
||||
func (h validateManifestHandler) checkDryRunPermission(ctx context.Context, kind, namespace string) (bool, error) {
|
||||
canI := auth.NewCanI(h.client.Discovery(), h.client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), kind, namespace, "create", "")
|
||||
canI := auth.NewCanI(h.client.Discovery(), h.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "create", "", config.KyvernoServiceAccountName())
|
||||
ok, err := canI.RunAccessCheck(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
|
@ -22,7 +22,7 @@ type Validation interface {
|
|||
// - Mutate
|
||||
// - Validation
|
||||
// - Generate
|
||||
func validateActions(idx int, rule *kyvernov1.Rule, client dclient.Interface, mock bool) error {
|
||||
func validateActions(idx int, rule *kyvernov1.Rule, client dclient.Interface, mock bool, username string) error {
|
||||
if rule == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ func validateActions(idx int, rule *kyvernov1.Rule, client dclient.Interface, mo
|
|||
var checker Validation
|
||||
// Mutate
|
||||
if rule.HasMutate() {
|
||||
checker = mutate.NewMutateFactory(rule.Mutation, client)
|
||||
checker = mutate.NewMutateFactory(rule.Mutation, client, username)
|
||||
if path, err := checker.Validate(context.TODO()); err != nil {
|
||||
return fmt.Errorf("path: spec.rules[%d].mutate.%s.: %v", idx, path, err)
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ func validateActions(idx int, rule *kyvernov1.Rule, client dclient.Interface, mo
|
|||
return fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", idx, path, err)
|
||||
}
|
||||
} else {
|
||||
checker = generate.NewGenerateFactory(client, rule.Generation, logging.GlobalLogger())
|
||||
checker = generate.NewGenerateFactory(client, rule.Generation, username, logging.GlobalLogger())
|
||||
if path, err := checker.Validate(context.TODO()); err != nil {
|
||||
return fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", idx, path, err)
|
||||
}
|
||||
|
|
|
@ -23,13 +23,15 @@ type Operations interface {
|
|||
// Auth provides implementation to check if caller/self/kyverno has access to perofrm operations
|
||||
type Auth struct {
|
||||
client dclient.Interface
|
||||
user string
|
||||
log logr.Logger
|
||||
}
|
||||
|
||||
// NewAuth returns a new instance of Auth for operations
|
||||
func NewAuth(client dclient.Interface, log logr.Logger) *Auth {
|
||||
func NewAuth(client dclient.Interface, user string, log logr.Logger) *Auth {
|
||||
a := Auth{
|
||||
client: client,
|
||||
user: user,
|
||||
log: log,
|
||||
}
|
||||
return &a
|
||||
|
@ -37,7 +39,7 @@ func NewAuth(client dclient.Interface, 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().SelfSubjectAccessReviews(), kind, namespace, "create", "")
|
||||
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "create", "", a.user)
|
||||
ok, err := canI.RunAccessCheck(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -47,7 +49,7 @@ 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().SelfSubjectAccessReviews(), kind, namespace, "update", "")
|
||||
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "update", "", a.user)
|
||||
ok, err := canI.RunAccessCheck(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -57,7 +59,7 @@ 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().SelfSubjectAccessReviews(), kind, namespace, "delete", "")
|
||||
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "delete", "", a.user)
|
||||
ok, err := canI.RunAccessCheck(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -67,7 +69,7 @@ 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().SelfSubjectAccessReviews(), kind, namespace, "get", "")
|
||||
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "get", "", a.user)
|
||||
ok, err := canI.RunAccessCheck(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
|
@ -25,10 +25,10 @@ type Generate struct {
|
|||
}
|
||||
|
||||
// NewGenerateFactory returns a new instance of Generate validation checker
|
||||
func NewGenerateFactory(client dclient.Interface, rule kyvernov1.Generation, log logr.Logger) *Generate {
|
||||
func NewGenerateFactory(client dclient.Interface, rule kyvernov1.Generation, user string, log logr.Logger) *Generate {
|
||||
g := Generate{
|
||||
rule: rule,
|
||||
authCheck: NewAuth(client, log),
|
||||
authCheck: NewAuth(client, user, log),
|
||||
log: log,
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,7 @@ func (g *Generate) validateClone(c kyvernov1.CloneFrom, cl kyvernov1.CloneList,
|
|||
return "", err
|
||||
}
|
||||
if !ok {
|
||||
return "", fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Update permissions in ClusterRole 'kyverno:background-controller:additional'", kind, namespace)
|
||||
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.")
|
||||
|
@ -145,7 +145,7 @@ func (g *Generate) canIGenerate(ctx context.Context, kind, namespace string) err
|
|||
return err
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("kyverno does not have permissions to 'create' resource %s/%s. Update permissions in ClusterRole 'kyverno:background-controller:additional'", kind, namespace)
|
||||
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)
|
||||
|
@ -154,7 +154,7 @@ func (g *Generate) canIGenerate(ctx context.Context, kind, namespace string) err
|
|||
return err
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("kyverno does not have permissions to 'update' resource %s/%s. Update permissions in ClusterRole 'kyverno:background-controller:additional'", kind, namespace)
|
||||
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)
|
||||
|
@ -163,7 +163,7 @@ func (g *Generate) canIGenerate(ctx context.Context, kind, namespace string) err
|
|||
return err
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Update permissions in ClusterRole 'kyverno:background-controller:additional'", kind, namespace)
|
||||
return fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Grant proper permissions to the background controller", kind, namespace)
|
||||
}
|
||||
|
||||
// DELETE
|
||||
|
@ -173,7 +173,7 @@ func (g *Generate) canIGenerate(ctx context.Context, kind, namespace string) err
|
|||
return err
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("kyverno does not have permissions to 'delete' resource %s/%s. Update permissions in ClusterRole 'kyverno:background-controller:additional'", kind, namespace)
|
||||
return fmt.Errorf("kyverno does not have permissions to 'delete' 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.")
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
type authChecker struct {
|
||||
client dclient.Interface
|
||||
user string
|
||||
}
|
||||
|
||||
type AuthChecker interface {
|
||||
|
@ -17,21 +18,21 @@ type AuthChecker interface {
|
|||
CanIGet(ctx context.Context, kind, namespace, subresource string) (bool, error)
|
||||
}
|
||||
|
||||
func newAuthChecker(client dclient.Interface) AuthChecker {
|
||||
return &authChecker{client: client}
|
||||
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().SelfSubjectAccessReviews(), kind, namespace, "create", subresource)
|
||||
checker := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "create", 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().SelfSubjectAccessReviews(), kind, namespace, "update", subresource)
|
||||
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().SelfSubjectAccessReviews(), kind, namespace, "get", subresource)
|
||||
checker := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "get", subresource, a.user)
|
||||
return checker.RunAccessCheck(ctx)
|
||||
}
|
||||
|
|
|
@ -20,10 +20,10 @@ type Mutate struct {
|
|||
}
|
||||
|
||||
// NewMutateFactory returns a new instance of Mutate validation checker
|
||||
func NewMutateFactory(m kyvernov1.Mutation, client dclient.Interface) *Mutate {
|
||||
func NewMutateFactory(m kyvernov1.Mutation, client dclient.Interface, user string) *Mutate {
|
||||
return &Mutate{
|
||||
mutation: m,
|
||||
authChecker: newAuthChecker(client),
|
||||
authChecker: newAuthChecker(client, user),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -124,7 +124,7 @@ func checkValidationFailureAction(spec *kyvernov1.Spec) []string {
|
|||
}
|
||||
|
||||
// Validate checks the policy and rules declarations for required configurations
|
||||
func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interface, mock bool, openApiManager openapi.Manager) ([]string, error) {
|
||||
func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interface, mock bool, openApiManager openapi.Manager, username string) ([]string, error) {
|
||||
var warnings []string
|
||||
namespaced := policy.IsNamespaced()
|
||||
spec := policy.GetSpec()
|
||||
|
@ -328,7 +328,7 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
|
|||
}
|
||||
}
|
||||
|
||||
if err := validateActions(i, &rules[i], client, mock); err != nil {
|
||||
if err := validateActions(i, &rules[i], client, mock, username); err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
|
|
|
@ -352,7 +352,7 @@ func Test_Validate_Policy(t *testing.T) {
|
|||
err := json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager)
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
|
@ -499,7 +499,7 @@ func Test_Validate_ErrorFormat(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
|
||||
openApiManager, _ := openapi.NewManager(logr.Discard())
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager)
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
||||
|
@ -901,7 +901,7 @@ func Test_Validate_Kind(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
|
||||
openApiManager, _ := openapi.NewManager(logr.Discard())
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager)
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
||||
|
@ -950,7 +950,7 @@ func Test_Validate_Any_Kind(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
|
||||
openApiManager, _ := openapi.NewManager(logr.Discard())
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager)
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
||||
|
@ -1040,7 +1040,7 @@ func Test_Wildcards_Kind(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
|
||||
openApiManager, _ := openapi.NewManager(logr.Discard())
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager)
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
||||
|
@ -1090,7 +1090,7 @@ func Test_Namespced_Policy(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
|
||||
openApiManager, _ := openapi.NewManager(logr.Discard())
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager)
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
||||
|
@ -1268,7 +1268,7 @@ func Test_patchesJson6902_Policy(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
|
||||
openApiManager, _ := openapi.NewManager(logr.Discard())
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager)
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
|
@ -1316,7 +1316,7 @@ func Test_deny_exec(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
|
||||
openApiManager, _ := openapi.NewManager(logr.Discard())
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager)
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
|
@ -1464,7 +1464,7 @@ func Test_SignatureAlgorithm(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
|
||||
openApiManager, _ := openapi.NewManager(logr.Discard())
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager)
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
|
||||
if testcase.expectedOutput {
|
||||
assert.NilError(t, err)
|
||||
} else {
|
||||
|
@ -1514,7 +1514,7 @@ func Test_existing_resource_policy(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
|
||||
openApiManager, _ := openapi.NewManager(logr.Discard())
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager)
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
|
@ -1570,7 +1570,7 @@ func Test_PodControllerAutoGenExclusion_All_Controllers_Policy(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
|
||||
openApiManager, _ := openapi.NewManager(logr.Discard())
|
||||
res, err := Validate(policy, nil, nil, true, openApiManager)
|
||||
res, err := Validate(policy, nil, nil, true, openApiManager, "admin")
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, res == nil)
|
||||
}
|
||||
|
@ -1627,7 +1627,7 @@ func Test_PodControllerAutoGenExclusion_Not_All_Controllers_Policy(t *testing.T)
|
|||
assert.NilError(t, err)
|
||||
|
||||
openApiManager, _ := openapi.NewManager(logr.Discard())
|
||||
warnings, err := Validate(policy, nil, nil, true, openApiManager)
|
||||
warnings, err := Validate(policy, nil, nil, true, openApiManager, "admin")
|
||||
assert.Assert(t, warnings != nil)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
@ -1684,7 +1684,7 @@ func Test_PodControllerAutoGenExclusion_None_Policy(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
|
||||
openApiManager, _ := openapi.NewManager(logr.Discard())
|
||||
warnings, err := Validate(policy, nil, nil, true, openApiManager)
|
||||
warnings, err := Validate(policy, nil, nil, true, openApiManager, "admin")
|
||||
assert.Assert(t, warnings == nil)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
@ -2226,7 +2226,7 @@ func Test_Any_wildcard_policy(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
|
||||
openApiManager, _ := openapi.NewManager(logr.Discard())
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager)
|
||||
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
||||
|
@ -2284,7 +2284,7 @@ func Test_Validate_RuleImageExtractorsJMESPath(t *testing.T) {
|
|||
expectedErr := fmt.Errorf("path: spec.rules[0]: jmespath may not be used in an image extractor when mutating digests with verify images")
|
||||
|
||||
openApiManager, _ := openapi.NewManager(logr.Discard())
|
||||
_, actualErr := Validate(policy, nil, nil, true, openApiManager)
|
||||
_, actualErr := Validate(policy, nil, nil, true, openApiManager, "admin")
|
||||
assert.Equal(t, expectedErr.Error(), actualErr.Error())
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1"
|
||||
"github.com/kyverno/kyverno/pkg/auth"
|
||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
|
||||
"github.com/kyverno/kyverno/pkg/engine/variables"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
|
@ -70,7 +71,7 @@ func validateAuth(ctx context.Context, client dclient.Interface, policy kyvernov
|
|||
spec := policy.GetSpec()
|
||||
kinds := sets.New(spec.MatchResources.GetKinds()...)
|
||||
for kind := range kinds {
|
||||
checker := auth.NewCanI(client.Discovery(), client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), kind, namespace, "delete", "")
|
||||
checker := auth.NewCanI(client.Discovery(), client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "delete", "", config.KyvernoUserName(config.KyvernoServiceAccountName()))
|
||||
allowedDeletion, err := checker.RunAccessCheck(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -79,7 +80,7 @@ func validateAuth(ctx context.Context, client dclient.Interface, policy kyvernov
|
|||
return fmt.Errorf("cleanup controller has no permission to delete kind %s", kind)
|
||||
}
|
||||
|
||||
checker = auth.NewCanI(client.Discovery(), client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), kind, namespace, "list", "")
|
||||
checker = auth.NewCanI(client.Discovery(), client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "list", "", config.KyvernoUserName(config.KyvernoServiceAccountName()))
|
||||
allowedList, err := checker.RunAccessCheck(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -14,14 +14,16 @@ import (
|
|||
)
|
||||
|
||||
type policyHandlers struct {
|
||||
client dclient.Interface
|
||||
openApiManager openapi.Manager
|
||||
client dclient.Interface
|
||||
openApiManager openapi.Manager
|
||||
backgroungServiceAccountName string
|
||||
}
|
||||
|
||||
func NewHandlers(client dclient.Interface, openApiManager openapi.Manager) webhooks.PolicyHandlers {
|
||||
func NewHandlers(client dclient.Interface, openApiManager openapi.Manager, serviceaccount string) webhooks.PolicyHandlers {
|
||||
return &policyHandlers{
|
||||
client: client,
|
||||
openApiManager: openApiManager,
|
||||
client: client,
|
||||
openApiManager: openApiManager,
|
||||
backgroungServiceAccountName: serviceaccount,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +33,7 @@ func (h *policyHandlers) Validate(ctx context.Context, logger logr.Logger, reque
|
|||
logger.Error(err, "failed to unmarshal policies from admission request")
|
||||
return admissionutils.Response(request.UID, err)
|
||||
}
|
||||
warnings, err := policyvalidate.Validate(policy, oldPolicy, h.client, false, h.openApiManager)
|
||||
warnings, err := policyvalidate.Validate(policy, oldPolicy, h.client, false, h.openApiManager, h.backgroungServiceAccountName)
|
||||
if err != nil {
|
||||
logger.Error(err, "policy validation errors")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue