Skip to content

Commit

Permalink
Merge pull request #107 from verdel/reinvocation-policy-feature
Browse files Browse the repository at this point in the history
Add reinvocationPolicy parameter to jspolicy custom resource
  • Loading branch information
FabianKramm authored Dec 12, 2023
2 parents 3b396e5 + c5a51a3 commit 054a622
Show file tree
Hide file tree
Showing 10 changed files with 48 additions and 2 deletions.
7 changes: 7 additions & 0 deletions chart/crds/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,13 @@ spec:
description: OperationType specifies an operation for a request.
type: string
type: array
reinvocationPolicy:
description: To allow mutating admission plugins to observe changes
made by other plugins, built-in mutating admission plugins are re-run
if a mutating webhook modifies an object, and mutating webhooks
can specify a reinvocationPolicy to control whether they are reinvoked
as well.
type: string
resources:
description: "Resources is a list of resources this rule applies to.
\n For example: 'pods' means pods. 'pods/log' means the log subresource
Expand Down
1 change: 1 addition & 0 deletions docs/pages/getting-started/work-with-policies.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ The following options may be configured to specify when a particular policy shou
Within the `spec` of a `JsPolicy` object, you can also define certain settings that are relevant during the execution of a policy:
- `violationPolicy`: `deny` (default) or `warn` (for testing) when the policy logic calls the `deny()` function
- `failurePolicy`: `Fail` (default) or `Ignore` when jsPolicy fails to execute the policy or it aborts with a runtime error
- `reinvocationPolicy`: [Reinvocation Policy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) defines whether JSPolicy is called again as part of the admission evaluation if the object being admitted is modified by other admission plugins after the initial webhook call (`IfNeeded`) or not (`Never`, *default*)
- `auditPolicy`: `Log` (default) or `Skip` logging any policy violations (requests that lead to `deny()`) in the status of this policy
- `auditLogSize`: Maximum number of violations that should be stored in the status of this policy (default: `10` violations)
- `timeoutSeconds`: Maximum number of seconds that the execution of the policy logic may take before jsPolicy aborts the policy execution (default: `10` seconds, maximum is `30`)
Expand Down
3 changes: 2 additions & 1 deletion docs/pages/reference/policy-crd.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ spec:
matchPolicy: "Equivalent"
violationPolicy: "deny"
failurePolicy: ""
reinvocationPolicy: "Never"
auditPolicy: ""
auditLogSize: 10
dependencies:
dependencies:
"@jspolicy/package-1": "^1.0.0"
"@jspolicy/package-2": "~2.0.0"
javascript: "/* JS CODE HERE */"
Expand Down
7 changes: 7 additions & 0 deletions docs/pages/using-policies/policy-types.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ Possible examples for mutating policies would be:

Since mutating policies change the request's object, Kubernetes executes them sequentially, so that they do not interfere with each other.

Policy can specify a [Reinvocation Policy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) to allow JSPolicy admission plugin to observe changes made by other admission plugins after the initial mutating webhook call.

`reinvocationPolicy` in JSPolicy specification may be set to `Never` or `IfNeeded`. It defaults to `Never`.

- `Never`: the policy webhook must not be called more than once in a single admission evaluation.
- `IfNeeded`: the policy webhook may be called again as part of the admission evaluation if the object being admitted is modified by other admission plugins after the initial webhook call.

## `Validating`
Validating policies are executed as part of `kubectl` requests after the execution of mutating policies. The objective of validating policies is to inspect the request and then to either deny or allow it.

Expand Down
4 changes: 3 additions & 1 deletion docs/pages/writing-policies/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ kind: JsPolicy
metadata:
name: "policy-name.company.tld"
spec:
type: Validating # other options: Mutating | Controller
type: Validating # other options: Mutating | Controller
```
### Policy Trigger
Expand Down Expand Up @@ -75,6 +75,7 @@ spec:
Within the `spec` of a `JsPolicy` object, you can also define certain settings that are relevant during the execution of a policy:
- `violationPolicy`: `deny` (default) or `warn` (for testing) when the policy logic calls the `deny()` function
- `failurePolicy`: `Fail` (default) or `Ignore` when jsPolicy fails to execute the policy or it aborts with a runtime error
- `reinvocationPolicy`: [Reinvocation Policy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) defines whether JSPolicy is called again as part of the admission evaluation if the object being admitted is modified by other admission plugins after the initial webhook call (`IfNeeded`) or not (`Never`, *default*)
- `auditPolicy`: `Log` (default) or `Skip` logging any policy violations (requests that lead to `deny()`) in the status of this policy
- `auditLogSize`: Maximum number of violations that should be stored in the status of this policy (default: `10` violations)
- `timeoutSeconds`: Maximum number of seconds that the execution of the policy logic may take before jsPolicy aborts the policy execution (default: `10` seconds, maximum is `30`)
Expand All @@ -89,6 +90,7 @@ spec:
resources: ["pods", "deployments"]
violationPolicy: warn
failurePolicy: Ignore
reinvocationPolicy: IfNeeded
auditPolicy: Skip
timeoutSeconds: 30
```
11 changes: 11 additions & 0 deletions pkg/apis/policy/v1beta1/jspolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ type JsPolicySpec struct {
// +optional
Scope *admissionregistrationv1.ScopeType `json:"scope,omitempty" protobuf:"bytes,4,rep,name=scope"`

// To allow mutating admission plugins to observe changes made by other
// plugins, built-in mutating admission plugins are re-run if a mutating webhook
// modifies an object, and mutating webhooks can specify a
// reinvocationPolicy to control whether they are reinvoked as well.
// Allowed values are IfNeeded or Never. Defaults to Never.
// See
// https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy
// for more detailed information.
// +optional
ReinvocationPolicy *admissionregistrationv1.ReinvocationPolicyType `json:"reinvocationPolicy,omitempty" protobuf:"bytes,4,opt,name=reinvocationPolicy,casttype=reinvocationPolicyType"`

// FailurePolicy defines how unrecognized errors from the admission endpoint are handled -
// allowed values are Ignore or Fail. Defaults to Fail.
// +optional
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/policy/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkg/controllers/jspolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,7 @@ func (r *JsPolicyReconciler) syncMutatingWebhookConfiguration(ctx context.Contex
if webhook.Webhooks[0].SideEffects == nil {
webhook.Webhooks[0].SideEffects = &none
}
webhook.Webhooks[0].ReinvocationPolicy = jsPolicy.Spec.ReinvocationPolicy
if webhook.Webhooks[0].ReinvocationPolicy == nil {
webhook.Webhooks[0].ReinvocationPolicy = &never
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/controllers/jspolicy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ var (
Spec: policyv1beta1.JsPolicyBundleSpec{},
}

ifNeeded = admissionregistrationv1.IfNeededReinvocationPolicy

mutatingTestPolicy = &policyv1beta1.JsPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "test.test.com",
Expand All @@ -51,6 +53,7 @@ var (
"test": "test",
},
},
ReinvocationPolicy: &ifNeeded,
},
}
)
Expand Down
8 changes: 8 additions & 0 deletions pkg/webhook/validation/generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ func validateValidatingWebhook(name string, hook *policyv1beta1.JsPolicySpec, ol
if hook.FailurePolicy != nil && !supportedFailurePolicies.Has(string(*hook.FailurePolicy)) {
allErrors = append(allErrors, field.NotSupported(fldPath.Child("failurePolicy"), *hook.FailurePolicy, supportedFailurePolicies.List()))
}
if hook.ReinvocationPolicy != nil && !supportedReinvocationPolicies.Has(string(*hook.ReinvocationPolicy)) {
allErrors = append(allErrors, field.NotSupported(fldPath.Child("reinvocationPolicy"), *hook.ReinvocationPolicy, supportedReinvocationPolicies.List()))
}
if hook.MatchPolicy != nil && !supportedMatchPolicies.Has(string(*hook.MatchPolicy)) {
allErrors = append(allErrors, field.NotSupported(fldPath.Child("matchPolicy"), *hook.MatchPolicy, supportedMatchPolicies.List()))
}
Expand Down Expand Up @@ -250,6 +253,11 @@ var supportedOperations = sets.NewString(
string(admissionregistrationv1.Connect),
)

var supportedReinvocationPolicies = sets.NewString(
string(admissionregistrationv1.NeverReinvocationPolicy),
string(admissionregistrationv1.IfNeededReinvocationPolicy),
)

func hasWildcardOperation(operations []admissionregistrationv1.OperationType) bool {
for _, o := range operations {
if o == admissionregistrationv1.OperationAll {
Expand Down

0 comments on commit 054a622

Please sign in to comment.