« Previous 1 2 3 Next »
Policy rulesets in cloud-native environments
Just Enough
Policy Example
The language constructs shown let you define policies to detect anomalies, incorrect configurations, or instances of poor practice. The code in Listing 2 shows an example of how to identify Amazon Web Service (AWS) users who do not use multifactor authentication (MFA) when logging in.
Listing 2
AWS Users Without MFA
01 [ 02 .... 03 { 04 "Path": "/", 05 "UserName": "liav", 06 "Arn": "arn:aws:iam::123456789:user/ferdinand", 07 "CreateDate": "2016-07-27 23:53:34+00:00", 08 "MFA": [ 09 { 10 "UserName": "ferdinand", 11 "SerialNumber": "arn:aws:iam::123456789:mfa/ferdinand", 12 "EnableDate": "2021-04-25 09:00:38+00:00" 13 } 14 ], 15 "Groups": [ 16 { 17 "GroupName": "RND-Admins" 18 } 19 ] 20 }, 21 { 22 "Path": "/", 23 "UserName": "guido", 24 "Arn": "arn:aws:iam::123456789:user/guido", 25 "CreateDate": "1979-10-07 19:53:34+00:00", 26 "MFA": [] 27 "Groups": [ 28 { 29 "GroupName": "DevOps-Admins" 30 } 31 ] 32 }, 33 .... 34 ]
After you have exported this input from AWS and saved it in the input.json
file, you can use it as input for a policy:
package match is_array(input.MFA) count(input.MFA) > 0 } match { not active_with_mfa }
This example looks to see whether users have stored MFA devices.
OPA and Kubernetes
One of the best known use cases for OPA is on Kubernetes. From a technical point of view, you install OPA as a pod in the cluster. The admission controller then forwards the user's request to the address stored in the validating or mutating webhook. The app responding on this address mutates the received manifest or sends back an allow or deny response. Both are enforced by the admission controller.
The controllers are – to put this briefly – responsible for checking incoming (and authenticated) API requests and either allowing or rejecting them depending on the result. Internally, the admission control process has two phases: a mutating phase, in which requests can be modified, and a validation phase for verification. An admission controller variant can intervene at either of these two points.
One example of such a controller is LimitRanger
, which provides pods with the default request resources while not allowing pods to grab more resources than intended by the limit. Of the more than 30 default admission controllers, two are worth special attention: ValidatingAdmissionWebhooks
and MutatingAdmissionWebhooks
. They do not implement any logic themselves but let you register REST endpoints of a service running in the cluster.
OPA is used as an admission controller, as well, and can thus configure a number of security features in the cluster by:
- defining that all containers must have sidecars (e.g., to perform logging or auditing tasks),
- providing all resources with annotations,
- modifying the container image definition so that images can only be loaded from a defined image registry, and
- setting node, pod affinity, and anti-affinity selectors for deployments.
Installing OPA in Kubernetes is quite easy [4]. The current Gatekeeper major release was published in 2019 in a collaboration between Google, Microsoft, Red Hat, and Styra (Figure 2).
To define rules, you need to configure a constraint template, with which you define both the Rego code used for checking resources and the elements to which the constraint is applied.
Listing 3 shows how to set up a template if you want to force namespace labeling. The API type here is templates.gatekeeper.sh/v1beta1
. The name
field in the metadata
section displays the name of the constraint. Within the spec
section, you need to create a custom resource definition (CRD) in Kubernetes named K8sRequiredLabels
and then configure the schema in the open-APIV3Schema
block. The object has an attribute named labels
and comprises an array of strings.
Listing 3
Adding Labels to Namespaces
01 apiVersion: templates.gatekeeper.sh/v1beta1 02 kind: ConstraintTemplate 03 metadata: 04 name: k8srequiredlabels 05 spec: 06 crd: 07 spec: 08 names: 09 kind: K8sRequiredLabels 10 validation: 11 # Schema for the parameter field 12 openAPIV3Schema: 13 properties: 14 labels: 15 type: array 16 items: string 17 targets: 18 - target: admission.k8s.gatekeeper.sh 19 rego: | 20 package k8srequiredlabels 21 deny[{"msg": msg, "details": {"missing_labels": missing}}] { 22 provided := {label | input.review.object.metadata.labels[label]} 23 required := {label | label := input.parameters.labels[_]} 24 missing := required - provided 25 count(missing) > 0 26 msg := sprintf("you must provide labels: %v", [missing]) 27 }
In the targets
section, you use a constraint to specify who is responsible for the evaluation, which is the installed admission controller in this case. In the rego
attribute, you then store the Rego code, which checks whether a namespace includes the desired labels.
To upload the constraint to the Kubernetes cluster, use the kubectl
command:
kubectl apply --f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/demo/basic/templates/k8srequiredlabels_template.yaml
Now you can use the constraint to define which labels must be present in the namespaces by creating a YAML file with the content shown in Listing 4.
Listing 4
Label Constraints
apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sRequiredLabels metadata: name: ns-must-have-gk spec: match: kinds: - apiGroups: [""] kinds: ["Namespace"] parameters: labels: ["gatekeeper"]
CRD is then installed with:
kubectl apply --f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/demo/basic/constraints/all_ns_must_have_gatekeeper.yaml
At this point you will want to test the OPA rules and create a namespace with missing labels. The error message shown in Listing 5 should appear.
Listing 5
Error Message
kubectl apply -f badns.yaml Error from server ([denied by ns-must-have-gk] you must provide labels: {"gatekeeper"}): error when creating "badns.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [denied by ns-must-have-gk] you must provide labels: {"gatekeeper"}
IaC and OPA
OPA is very flexible and thus is suited to a wide variety of security use cases, for example, reviewing Terraform code is a popular application. You can run OPA manually, or you can include it in an IaC pipeline to run automatically during deployment. For this to work, you need to save the Terraform execution plan and convert it to JSON format:
terraform init terraform plan --out tfplan.binary terraform show -json tfplan.binary > tfplan.json
Next, you need to get to work on implementing the policy. The example in Listing 6 shows how to ensure that all S3 buckets in AWS have a private access control list (ACL). To run this test, enter:
Listing 6
Checking S3 Buckets for ACLs
01 package terraform 02 deny[msg] { 03 changeset := input.resource_changes[_] 04 is_create_or_update(changeset.change.actions) 05 changeset.type == "aws_s3_bucket" 06 changeset.change.after.acl != "private" 07 msg := sprintf("S3 bucket %v has a non-private acl", [ 08 changeset.name 09 ]) 10 } 11 is_create_or_update(actions) { actions[_] == "create" } 12 is_create_or_update(actions) { actions[_] == "update" }
opa eval --fail-defined --format raw --input tfplan.json --data policy/ 'data.main.deny[x]'
« Previous 1 2 3 Next »
Buy this article as PDF
(incl. VAT)
Buy ADMIN Magazine
Subscribe to our ADMIN Newsletters
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Most Popular
Support Our Work
ADMIN content is made possible with support from readers like you. Please consider contributing when you've found an article to be beneficial.