Write custom rules using Rego

This document describes how to write custom rules using the Rego policy language. You can use these rules in Workload Manager to evaluate your workloads against the best practices defined for your organization.

Before you begin

Write custom rules using Rego

Google provides a sample GitHub repository with a set of predefined rules that you can use to evaluate your workloads. These samples cover multiple use cases. Select rules from the repository or create a rule (.rego) file that describes your evaluation requirements.

A custom rule has the following sections:

  • Metadata. The following fields define the rule metadata:

    • DETAILS: a short description for the rule.
    • SEVERITY: a user-defined value that defines the severity of violation of the rule. For example, HIGH, CRITICAL, MEDIUM, or LOW.
    • ASSET_TYPE: one of the supported assets. See Supported data sources.
    • TAGS: one or more tags for the rule. These tags help filter the rules.
  • Package declaration. For example, templates.google.compute.instance.label.

  • Import statements. For example, data.validator.google.lib as lib.

  • Rule definitions. a set of instructions that defines the rule.

Example rules

The following sample rules are available in the GoogleCloudPlatform/workload-manager GitHub repository. You can upload these rules as they are to your Cloud Storage bucket and use it to run your evaluations. Alternatively, modify the rules as per your organization policies and then upload the files to a Cloud Storage bucket.

  • Example 1: ensures that there is at least one label for your VMs.
  • Example 2: ensures that your workload does not use the Compute Engine default service account.
  • Example 3: ensures that VMs in your workload don't use an external IP address.

For a full list of sample rules that you can use in Workload Manager, see the GoogleCloudPlatform/workload-manager GitHub repository.

Example 1

Ensures that there is at least one label for the VMs in your workload.

# Copyright 2024 Google LLC
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     https://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

########################################################################
# DETAILS:  MUST have atleast one or more Label
# SEVERITY: Medium
# ASSET_TYPE: compute.googleapis.com/Instance
# TAGS: Labeling, Compliance, Security, Cost, Compute Engine
########################################################################

package templates.google.compute.instance.label

import data.validator.google.lib as lib
import data.validator.google.lib.parameters as gparam
import future.keywords

asset := input.asset

asset_type := "compute.googleapis.com/Instance"

params := lib.get_default(gparam.global_parameters, "compute", {})

deny [{"msg": message, "details": metadata}] {

	# Check if resource is in exempt list
	exempt_list := lib.get_default(params, "exemptions", [])
	exempt := {asset.name} & {ex | ex := exempt_list[_]}
	not count(exempt) != 0

	labels := lib.get_default(asset.resource.data, "labels", {})
	count(labels) == 0

	message:= "No Labels found"

	metadata:= {"name": asset.name}

}

Example 2

Ensures that your workload does not use the Compute Engine default service account

# Copyright 2024 Google LLC
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     https://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

########################################################################
# DETAILS:  MUST NOT use default service account
# SEVERITY: Medium
# ASSET_TYPE: compute.googleapis.com/Instance
# TAGS: Defaults, Management, Compute Engine
########################################################################

package templates.google.compute.defaultserviceAccount

import data.validator.google.lib as lib
import data.validator.google.lib.parameters as gparam
import future.keywords

asset_type = "compute.googleapis.com/Instance"

asset := input.asset

input_enriched := object.union({"resource": {"data": {"serviceAccounts": []}}}, asset)

params := lib.get_default(gparam.global_parameters, "compute", {})

deny[{
	"msg": "Disallowed default service account",
	"details": {"name": asset.name},
}] {
	not lib.asset_type_should_be_skipped(asset_type)

	account = input_enriched.resource.data.serviceAccounts[_]
	endswith(account.email, params.default_sa)
}

Example 3

Ensures that VMs in your workload don't use an external IP address.

# Copyright 2024 Google LLC
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#     https://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

########################################################################
# DETAILS:  Ensure VMs dont have External IP
# SEVERITY: High
# ASSET_TYPE: compute.googleapis.com/Instance
# TAGS: Security, Network, Compute Engine, External IP, VM, Virtual Machine
########################################################################

package templates.google.compute.instance.approved.external.ip

import data.validator.google.lib as lib
import data.validator.google.lib.parameters as gparam
import future.keywords

asset := input.asset

asset_type := "compute.googleapis.com/Instance"

params := lib.get_default(gparam.global_parameters, "compute", {})

deny [{"msg": message, "details": metadata}] {

	# Check if resource is in exempt list
	exempt_list := lib.get_default(params, "exemptions", [])
	exempt := {asset.name} & {ex | ex := exempt_list[_]}
	not count(exempt) != 0

	# Find network access config block w/ external IP
	instance := asset.resource.data
	access_config := instance.networkInterfaces[_].accessConfigs
	count(access_config) > 0

	message := sprintf("%v : VM Instance has external IP. current config: %v",[asset.name, access_config])
	metadata := {"name": asset.name}
}

Upload the rule to a Cloud Storage bucket

After you create the .rego file, upload it a Cloud Storage bucket. The top level of your Cloud Storage bucket must include the /lib and /rules folders:

  • lib
    • parameters.rego
    • utils.rego
  • /rules
    • rule_name1.rego
    • rule_name2.rego

What's next