Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions config/v1alpha1/manifests/pki-certificate-override-validation.yaml
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taking a closer look at the validations you are trying to enforce here, I don't believe that a VAP is actually suitable for your use case.

I could be wrong but as far as I understand it, when using parameter references that reference multiple parameter resources the policy is evaluated one parameter resource at a time. I think this makes the validation you are trying to achieve (rejecting PKI resources with .spec.certificateManagement.custom.overrides that do not have corresponding PKICertificateDefinition resource entries) not feasible because you won't have a list of the dependent resources to build your validation from.

If you truly want to pursue this approach of having validations that are dependent on the presence of values in another API you'll either need a validating admission webhook or to update our KAS admission plugins to enforce this validation.

Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
# ValidatingAdmissionPolicy that dynamically validates PKI certificate override names
# against registered PKICertificateDefinition resources
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: validate-pki-certificate-overrides.config.openshift.io
spec:
# Only validate PKI resources during CREATE/UPDATE
matchConstraints:
resourceRules:
- apiGroups: ["config.openshift.io"]
apiVersions: ["v1alpha1"]
operations: ["CREATE", "UPDATE"]
resources: ["pkis"]

# Use PKICertificateDefinition resources as the parameter source
paramKind:
apiVersion: config.openshift.io/v1alpha1
kind: PKICertificateDefinition

# Validate each certificate override references a registered certificate name
validations:
# Skip validation if no overrides are present
- expression: "!has(object.spec.certificateManagement.custom.overrides) || size(object.spec.certificateManagement.custom.overrides) == 0"
reason: Skip
Comment on lines +24 to +26
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be under matchConditions

matchConditions:
- name: check-only-overrides
  expression: size(object.spec.?certificateManagement.custom.overrides.orValue('[]')) > 0


# Build list of all valid certificate names from all PKICertificateDefinition resources
- expression: |
has(params) && has(params.spec) && has(params.spec.certificates) ?
params.spec.certificates.map(cert, cert.name) : []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are you trying to achieve here? This appears to be trying to construct a list of names, did you want to create a variable?

Validation expressions must map evaluate to a boolean

I think you want a variable to use later?

variables:
- name: certificates
  expression: params.spec.?certificates.orValue('[]')

I'm not convinced I understand why you want to map here so I've left that out

messageExpression: "'No PKICertificateDefinition found for component ' + (has(params.spec) ? params.spec.component : 'unknown')"
reason: Invalid

# Validate each override.certificateName exists in a PKICertificateDefinition
- expression: |
!has(object.spec.certificateManagement.custom.overrides) ||
object.spec.certificateManagement.custom.overrides.all(override,
params.exists(p,
has(p.spec) && has(p.spec.certificates) &&
p.spec.certificates.exists(cert, cert.name == override.certificateName)
)
)
message: "certificateName in overrides must reference a certificate registered in a PKICertificateDefinition resource"
reason: Invalid
Comment on lines +36 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so lets make the overrides a variable too

variables:
- name: overrides
  expression: object.spec.?certificateManagement.custom.overrides.orValue('[]')

And then this expression can be

variables.overrides.all(override, variables.certificates.exists(certificate, certificate.name == override.certificateName)


# Validate that certificate names follow DNS subdomain rules
- expression: |
!has(object.spec.certificateManagement.custom.overrides) ||
object.spec.certificateManagement.custom.overrides.all(override,
override.certificateName.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')
)
message: "certificateName must be a valid DNS subdomain (lowercase alphanumeric with hyphens)"
reason: Invalid
Comment on lines +47 to +54
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be within the API itself, you don't need a VAP for this


---
# ValidatingAdmissionPolicyBinding that applies the validation policy
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: validate-pki-certificate-overrides.config.openshift.io
spec:
policyName: validate-pki-certificate-overrides.config.openshift.io
validationActions: ["Deny"]

# Bind to all PKICertificateDefinition resources in openshift-config namespace
paramRef:
name: "" # Empty means all resources of the paramKind
namespace: openshift-config
# If no PKICertificateDefinition resources exist, allow the PKI resource
# This prevents blocking PKI resource creation before any components have registered
parameterNotFoundAction: Allow

# Match all PKI resources
matchResources:
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values: [""] # Empty string matches cluster-scoped resources
Comment on lines +78 to +80
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure it's metadata.name and not metadata.namespace?

2 changes: 2 additions & 0 deletions config/v1alpha1/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&ImagePolicyList{},
&ClusterImagePolicy{},
&ClusterImagePolicyList{},
&PKI{},
&PKIList{},
)
metav1.AddToGroupVersion(scheme, GroupVersion)
return nil
Expand Down
Loading