Approver Policy Enterprise Rego plugin¶
The Approver Policy Enterprise Rego plugin introduces 3 new flags:
--rego-replicate
--rego-replicate-cluster
--rego-policy-directory
The optional --rego-replicate
and --rego-replicate-cluster
flags define the resource types watched by the plugin. These are then made available to the Rego rules during evaluation.
-
--rego-replicate
Usage:
--rego-replicate=<group/version/resource[/namespace]>
Use this flag if you want to watch namespaced resources. Optionally you can add a scope to watch a single namespace. Note that resources belonging to the core Kubernetes group (i.e.
services
,deployments
, ...) have an empty group string, and therefore are defined with a leading '/':--rego-replicate="networking.k8s.io/v1/ingresses" --rego-replicate="/v1/deployments" --rego-replicate="/v1/services/my-namespace'
-
--rego-replicate-cluster
Usage:
--rego-replicate-cluster=<group/version/resource>
Use this flag if you want to watch cluster scoped resources. Note that resources belonging to the core Kubernetes group (i.e.
namespaces
, ...) have an empty group string, and therefore are defined with a leading '/':--rego-replicate-cluster="trust.cert-manager.io/v1alpha1/bundles" --rego-replicate-cluster="/v1/namespaces"
-
--rego-policy-directory
Usage:
--rego-policy-directory
Use this flag to define the directory where your Rego policies are defined and loaded from. This directory, and all child directories, are loaded. Rules are mounted into the approver Pod via a Kubernetes Volume, such as a ConfigMap or Secret. The Approver Policy Enterprise Rego plugin updates the Rego modules it has loaded if and when they are modified in the mounted volume for the directory.
Writing Rego rules¶
The Approver Policy Enterprise Rego plugin let's you write certificate request policy rules in Rego. The following sample rule ensures a certificate request contains the hostnames of an Ingress resource in the same namespace:
deny_dns_names_not_found_in_ingress_tls[message] {
csr := crypto.x509.parse_certificate_request(input.spec.request)
csr_hosts := {x|x := csr.DNSNames[_] }
matching_ingresses := {ingress|
ingress := data.resources.ingresses[input.metadata.namespace][_]
matching_tls := {tls|
tls := ingress.spec.tls[_]
hosts := {x| x := tls.hosts[_] }
hosts == csr_hosts
}
count(matching_tls) > 0
}
count(matching_ingresses) == 0
message := sprintf("no ingress was found with a tls hosts entry with hostnames: %v", [concat(",", csr_hosts)])
}
See this OPAplayground link for more information about the above policy. You can use it to inspect how it's working with an example certificate request.
Rego Rule Format¶
Rego rules used for policy evaluation in the Approver Policy Enterprise Rego plugin must:
- Be named
deny_...
(you can define other supporting rules without this prefix. This is only required if the rules is referenced in policies). - Return a string message when a request is denied and nothing when the request is accepted.
Valid Examples:
# GOOD: partial rule format used, string message returned
deny_all_csr[message] {
1 == 1
message := "this rule denies all certificate requests"
}
# GOOD: partial rule format used, string message returned, supporting rule
used
deny_all_csr[message] {
supporting_rule
message := "this rule denies all certificate requests"
}
# supporting_rule is only used in deny_all_csr, not the policy
supporting_rule {
1 == 1
}
Invalid Examples:
# BAD: non string returned
deny_all_csr[message] {
1 == 1
message := 1
}
# BAD: complete rule format used, no message returned
deny_all_csr {
1 == 1
}
Using the approver¶
The plugin parses and loads any Rego rules that are under the configured directory (--rego-policy-directory
). The following command creates a policy in a ConfigMap to load into the approver:
$ cat << EOF | > ingress.rego
package ingress
deny_dns_names_not_found_in_ingress_tls[message] {
csr := crypto.x509.parse_certificate_request(input.spec.request)
csr_hosts := {x|x := csr.DNSNames[_] }
matching_ingresses := {ingress|
ingress := data.resources.ingresses[input.metadata.namespace][_]
matching_tls := {tls|
tls := ingress.spec.tls[_]
hosts := {x| x := tls.hosts[_] }
hosts == csr_hosts
}
count(matching_tls) > 0
}
count(matching_ingresses) == 0
message := sprintf("no ingress was found with a tls hosts entry with hostnames: %v", [concat(",", csr_hosts)])
}
EOF
$ kubectl create configmap approver-policy-rego -n cert-manager --from-file=ingress.rego --dry-run=true -o yaml | kubectl apply -f -
Loaded rules are then available under the path data.<package name>.<rule name>
for reference in certificate request policy resources under the path: spec.plugins.rego.values.rules
.
Our example rule above in the ingress
package will be available at:data.ingress.deny_dns_names_not_found_in_ingress_tls
The following CertificateRequestPolicy
uses the rule as defined above:
apiVersion: policy.cert-manager.io/v1alpha1
kind: CertificateRequestPolicy
metadata:
name: rego-example
spec:
allowed:
dnsNames:
values: ["*.example.com"]
required: true
selector:
issuerRef:
group: cert-manager.io
kind: ClusterIssuer
name: letsencrypt-prod
plugins:
rego:
values:
rules: "data.ingress.deny_dns_names_not_found_in_ingress_tls"
Input & Data¶
The approver makes information about the context available for you to use in policy decisions.
The input
document¶
The certificate request you want to evaluate is available at the input
path within Rego rules.
Rego data
& data.resources
¶
The watched Kubernetes resources are made available to the Rego rules during evaluation. These can be accessed from resources
under data
, and have the following structure:
# Namespaced resource
data.resources.<resource name>.<namespace>.<name>.<Kubernetes object>
# Cluster Scoped resourece
data.resources.<resource name>.<name>.<Kubernetes object>
Alternatively, in JSON format:
{
"resources": {
"services": {
"default": {
"example-service": {...},
"another-service": {...},
...
},
...
},
"namespaces": {
"default": {...},
"kube-system": {...},
...
},
...
}
}
Related links¶
- To learn more about the Rego policy language, see Learning Rego.