diff --git a/api/v1alpha1/policy_types.go b/api/v1alpha1/policy_types.go index ef64092..883bbff 100644 --- a/api/v1alpha1/policy_types.go +++ b/api/v1alpha1/policy_types.go @@ -20,8 +20,26 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. +const ( + StrictValidationMode = "strict" + PermissiveValidationMode = "permissive" +) + +// PolicyValidation defines the +type PolicyValidation struct { + // Enforced indicates if creation or updates to the policy require schema validation + //+required + //+kubebuilder:default:value=false + Enforced bool `json:"enforced"` + + // ValidationMode indicates which validation mode to use. + // A value of `strict` requires that only literals are passed to extension functions (IP, decimal, datetime), and not entity attributes. + // See https://docs.cedarpolicy.com/policies/validation.html#validation-benefits-of-schema for more details. + //+kubebuilder:validation:Enum=strict;permissive + //+default:value=strict + //+optional + ValidationMode string `json:"validationMode"` +} // PolicySpec defines the desired state of Policy type PolicySpec struct { @@ -31,6 +49,10 @@ type PolicySpec struct { // Content is a string representing the policy content //+required Content string `json:"content"` + + // Validation + //+required + Validation PolicyValidation `json:"validation"` } // PolicyStatus defines the observed state of Policy diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 133ee79..af9ed2d 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -183,6 +183,7 @@ func (in *PolicyList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PolicySpec) DeepCopyInto(out *PolicySpec) { *out = *in + out.Validation = in.Validation } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicySpec. @@ -210,6 +211,21 @@ func (in *PolicyStatus) DeepCopy() *PolicyStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PolicyValidation) DeepCopyInto(out *PolicyValidation) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyValidation. +func (in *PolicyValidation) DeepCopy() *PolicyValidation { + if in == nil { + return nil + } + out := new(PolicyValidation) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StoreConfig) DeepCopyInto(out *StoreConfig) { *out = *in diff --git a/cmd/converter/main.go b/cmd/converter/main.go index 4f96506..a171346 100644 --- a/cmd/converter/main.go +++ b/cmd/converter/main.go @@ -186,6 +186,10 @@ func CRDForCedarPolicy(name string, policies *cedar.PolicySet) *cedarv1alpha1.Po Name: strings.ReplaceAll(name, ":", "."), }, Spec: cedarv1alpha1.PolicySpec{ + Validation: cedarv1alpha1.PolicyValidation{ + Enforced: true, + ValidationMode: cedarv1alpha1.StrictValidationMode, + }, Content: string(marshalled), }, } diff --git a/config/crd/bases/cedar.k8s.aws_policies.yaml b/config/crd/bases/cedar.k8s.aws_policies.yaml index 1e96dc7..29725c5 100644 --- a/config/crd/bases/cedar.k8s.aws_policies.yaml +++ b/config/crd/bases/cedar.k8s.aws_policies.yaml @@ -42,8 +42,29 @@ spec: content: description: Content is a string representing the policy content type: string + validation: + description: Validation + properties: + enforced: + default: false + description: Enforced indicates if creation or updates to the + policy require schema validation + type: boolean + validationMode: + description: |- + ValidationMode indicates which validation mode to use. + A value of `strict` requires that only literals are passed to extension functions (IP, decimal, datetime), and not entity attributes. + See https://docs.cedarpolicy.com/policies/validation.html#validation-benefits-of-schema for more details. + enum: + - strict + - permissive + type: string + required: + - enforced + type: object required: - content + - validation type: object status: description: PolicyStatus defines the observed state of Policy diff --git a/demo/admission-policy.yaml b/demo/admission-policy.yaml index a3b90e4..7b7c75d 100644 --- a/demo/admission-policy.yaml +++ b/demo/admission-policy.yaml @@ -3,6 +3,8 @@ kind: Policy metadata: name: combined-policy spec: + validation: + enforced: false content: | // Authorization policy // test-user can do Action::"*" on configmaps in the default namespace @@ -34,6 +36,8 @@ kind: Policy metadata: name: self-node-policy spec: + validation: + enforced: false content: | // On Kubernetes versions 1.29+ with the `ServiceAccountTokenPodNodeInfo` flag enabled, // Kubernetes injects a node name into the Service Account token, which gets propagated @@ -80,6 +84,8 @@ kind: Policy metadata: name: label-enforcement-policy spec: + validation: + enforced: false content: | // authz policy allowing sample-user to do anything on configmaps in default namespace permit ( @@ -135,4 +141,4 @@ spec: resource.oldObject has metadata && resource.oldObject.metadata has labels && resource.oldObject.metadata.labels.contains({"key": "owner", "value": principal.name}) - }; \ No newline at end of file + }; diff --git a/demo/authorization-policy.yaml b/demo/authorization-policy.yaml index d66a1cc..cd8b157 100644 --- a/demo/authorization-policy.yaml +++ b/demo/authorization-policy.yaml @@ -3,6 +3,8 @@ kind: Policy metadata: name: first-policy spec: + validation: + enforced: false content: | // test-user can get/list/watch pods at cluster scope permit ( @@ -29,6 +31,8 @@ kind: Policy metadata: name: viewer-group spec: + validation: + enforced: false content: | // viewer group members can get/list/watch any Resource other than secrets permit ( @@ -47,6 +51,8 @@ metadata: annotations: cedar.k8s.aws/description: "Grants access to public information, equivalent to RBAC CR/CRB system:public-info-viewer" spec: + validation: + enforced: false content: | permit ( principal in k8s::Group::"system:authenticated", @@ -98,6 +104,8 @@ kind: Policy metadata: name: secrets-label-selector-example spec: + validation: + enforced: false content: | permit ( principal is k8s::User, @@ -133,6 +141,8 @@ kind: Policy metadata: name: service-account-impersonate-example spec: + validation: + enforced: false content: | // test-user can impersonate the service-manager service account in the default namespace permit ( @@ -156,4 +166,4 @@ spec: resource.resource == "services" && resource has namespace && resource.namespace == principal.namespace - }; \ No newline at end of file + }; diff --git a/go.mod b/go.mod index 4129289..c4e9e0e 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/aws/aws-sdk-go-v2 v1.32.5 github.com/aws/aws-sdk-go-v2/config v1.28.5 github.com/aws/aws-sdk-go-v2/service/verifiedpermissions v1.20.2 - github.com/cedar-policy/cedar-go v1.0.2 + github.com/cedar-policy/cedar-go v1.1.0 github.com/go-logr/logr v1.4.2 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index 9a4b9e8..3befe7c 100644 --- a/go.sum +++ b/go.sum @@ -38,8 +38,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/cedar-policy/cedar-go v1.0.2 h1:9FcaMvANsvakfTjyPYJ/YLIYO3B+iLBAwZ6boQN2i6E= -github.com/cedar-policy/cedar-go v1.0.2/go.mod h1:pEgiK479O5dJfzXnTguOMm+bCplzy5rEEFPGdZKPWz4= +github.com/cedar-policy/cedar-go v1.1.0 h1:qAAmtjIPY2WCR2aQEC7UShExzm117UFxVe4ulhm618Q= +github.com/cedar-policy/cedar-go v1.1.0/go.mod h1:pEgiK479O5dJfzXnTguOMm+bCplzy5rEEFPGdZKPWz4= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= diff --git a/scratch/Dockerfile b/scratch/Dockerfile index c52037b..714ed02 100644 --- a/scratch/Dockerfile +++ b/scratch/Dockerfile @@ -4,6 +4,7 @@ ARG BASE_IMAGE=kindest/node:v1.31.1 FROM ${BASE_IMAGE} AS builder COPY webhook.image.tar /kind/images/webhook.image.tar +# The image import works, but the command returns an error, so we OR a success message RUN containerd & ctr -n k8s.io i import /kind/images/webhook.image.tar || echo "success" FROM ${BASE_IMAGE}