This page explains how to code a custom module definition by using the Common Expression Language (CEL) and YAML.
Use the Google Cloud CLI to upload your custom module definitions to Security Health Analytics.
In the YAML file, a custom module definition consists of a structured set of properties that you use to define the following elements of a Security Health Analytics custom module:
- The resources to scan.
- The detection logic to use.
- The information to provide to your security teams so that they can quickly understand, triage, and resolve the detected issue.
The specific required and optional properties that make up a YAML definition are covered in Coding steps.
Avoid creating redundant detectors
To control finding volume, avoid creating and running modules that contain redundant functionality.
For example, if you create a custom module that checks for encryption keys that
are not rotated after 30 days, consider disabling the built-in
Security Health Analytics detector KMS_KEY_NOT_ROTATED
, because
its check, which uses a value of 90 days, would be superfluous.
For more information on disabling detectors, see Enable and disable detectors.
Coding steps
You code the definition of a custom module for Security Health Analytics as a series of YAML properties, some of which contain CEL expressions.
To code a custom definition module, follow these steps:
Create a text file with the
yaml
filename extension.In the text file, create a
resource_selector
property and specify one to five resource types for the custom module to scan. A resource type cannot be specified more than once in a custom module definition. For example:resource_selector: resource_types: ‐ cloudkms.googleapis.com/CryptoKey
The resource types that you specify must be supported by Security Command Center. For a list of supported resource types, see Supported resource types.
Create a
predicate
property and specify one or more CEL expressions that check properties of the resource types to be scanned. Any properties the you reference in the CEL expressions must exist in the Google Cloud API definition of each resource type that you specify underresource_selector
. To trigger a finding, the expression must resolve toTRUE
. For example, in the following expression, onlyrotationPeriod
values greater than2592000s
trigger a finding.predicate: expression: resource.rotationPeriod > duration("2592000s")
For help writing CEL expressions, see the following resources:
- Supported resource types. Click on each resource to see the properties that you can use in your CEL expressions.
- Writing CEL expressions.
- Referencing resource and policy properties in custom modules.
Create a
description
property that explains the vulnerability or misconfiguration that the custom module detects. This explanation appears in each finding instance to help investigators understand the detected issue. The text must be enclosed in quotation marks. For example:description: "The rotation period of the identified cryptokey resource exceeds 30 days, the maximum rotation period that our security guidelines allow."
Create a
recommendation
property that explains how to fix the detected issue. The gcloud CLI requires an escape character before certain characters, such as quotation marks. The following example shows the use of the backslash to escape each set of quotation marks:recommendation: "To fix this issue go to https://console.cloud.google.com/security/kms. Click the key-ring that contains the key. Click the key. Click \"Edit rotation period\". Then set the rotation period to at most 30 days."
If you create or update a custom module by using the Google Cloud console, escape characters are not required.
Create a
severity
property and specify the default severity for the findings that are created by this module. Commonly used values for theseverity
property areLOW
,MEDIUM
,HIGH
, andCRITICAL
. For example,severity: MEDIUM
Optionally, create a
custom_output
property and specify additional information to return with each finding. Specify the information to return as one or more name-value pairs. You can return either the value of a property of the scanned resource or a literal string. Properties must be specified asresource.PROPERTY_NAME
. Literal strings must be enclosed in quotation marks. The following example shows acustom_output
definition that returns both a property value, the value ofrotationPeriod
in the scannedCryptoKey
resource, and a text string,"Excessive rotation period for CryptoKey"
:custom_output: properties: - name: duration value_expression: expression: resource.rotationPeriod - name: note value_expression: expression: "Excessive rotation period for CryptoKey"
Save the file to a location that your gcloud CLI can access.
Upload the definition to Security Health Analytics with the following command:
gcloud scc custom-modules sha create \ --organization=organizations/ORGANIZATION_ID \ --display-name="MODULE_DISPLAY_NAME" \ --enablement-state="ENABLED" \ --custom-config-from-file=DEFINITION_FILE_NAME.yaml
Replace the following values:
ORGANIZATION_ID
with the ID of the parent organization of the custom module or replace the--organization
flag with either--folders
or--project
and specify the ID of the parent folder or project.MODULE_DISPLAY_NAME
with a name to display as the finding category when the custom module returns findings. The name must be between 1 and 128 characters, start with a lowercase letter, and contain alphanumeric characters or underscores only.DEFINITION_FILE_NAME
with the path and file name of the YAML file that contains the definition of the custom module.
For more information about working with Security Health Analytics custom modules, see Using custom modules for Security Health Analytics.
Scan latencies for new custom modules
Creating a custom module does not trigger a new scan.
Security Health Analytics doesn't start using a new custom module until either of the following:
- The first batch scan after you create the custom module. Depending on when you create a custom module in your batch-scan schedule, you might have to wait up to 24 hours before Security Health Analytics starts using the custom module.
- A change to a target resource triggers a real-time scan.
Example custom module definition
The following example shows a completed custom module definition that
triggers a finding if the value of the rotationPeriod
property of a
cloudkms.googleapis.com/CryptoKey
resource is greater than 2,592,000
seconds (30 days). The example returns two optional values in the
custom_output
section: the value of resource.rotationPeriod
and a note
as a text string.
In the example, note the following elements:
- The type of asset or resource to check is listed in the
resource_selector
section underresource_types
. - The check that the module performs on the resources, its detection logic,
is defined in the
predicate
section preceded byexpression
. - Two custom source properties,
duration
andviolation
, are defined in thecustom_output
section. - The explanation of the issue that was detected is specified in the
description
property. - The guidance for remediating the detected issue is
specified on the
recommendation
property. Because quotation marks appear in the guidance, a backslash escape character is required before each quotation mark.
severity: HIGH
description: "Regular key rotation helps provide protection against
compromised keys, and limits the number of encrypted messages available
to cryptanalysis for a specific key version."
recommendation: "To fix this issue go to
https://console.cloud.google.com/security/kms. Click the key-ring that
contains the key. Click the key. Click \"Edit rotation period\". Then
set the rotation period to at most 30 days."
resource_selector:
resource_types:
- cloudkms.googleapis.com/CryptoKey
predicate:
expression: resource.rotationPeriod > duration("2592000s")
custom_output:
properties:
- name: duration
value_expression:
expression: resource.rotationPeriod
- name: violation
value_expression:
expression:
"Excessive rotation period for CryptoKey"
Referencing resource and policy properties in custom modules
Regardless of which method you use to create a custom module—by using the Google Cloud console or by writing the definition yourself—you need to be able to look up the properties that you can evaluate in the custom module. You also need to know how to reference those properties in a custom module definition.
Find the properties of a resource or policy
The properties of a Google Cloud resource are defined in the API definition of the resource. You can find this definition by clicking the resource name on Supported resource types.
You can find the properties of a policy in the IAM API reference documentation. For properties of a policy, see Policy.
Reference a resource property in a custom module definition
When you create a custom module, all direct references to the property
of a scanned resource must begin with resource
, followed by any parent
properties and finally the target property. The properties are separated
by a period, using a JSON-style dot notation.
The following are examples of resource properties and how they can be retrieved:
resourceName
: stores the full name of a resource in Cloud Asset Inventory, for example,//cloudresourcemanager.googleapis.com/projects/296605646631
.resource.rotationPeriod
: whererotationPeriod
is a property ofresource
.resource.metadata.name
: wherename
is a sub-property ofmetadata
, which is a sub-property ofresource
.
Reference IAM policy properties
You can create CEL expressions that evaluate the IAM policy of a resource by referencing the resource's IAM policy properties. The only properties available are bindings and roles within bindings.
When referencing IAM policy properties, policy
is the
top-level property.
The following are examples of IAM policy properties and how they can be retrieved:
policy.bindings
, wherebindings
is a property ofpolicy
.policy.version
, whereversion
is a property ofpolicy
.
For more examples, see Example CEL expressions.
For information about the properties of a policy, see Policy.
Writing CEL expressions
When you create a custom module, you use CEL expressions to evaluate
the properties of the scanned resource. Optionally, you can also use
CEL expressions to define custom name
-value
pairs that return additional
information with your findings.
Whether you are creating a custom module in the Google Cloud console or writing your custom module definition yourself in a YAML file, the CEL expressions you define are the same.
CEL expressions for detection logic
You code the detection logic of a custom module by using CEL expressions with standard CEL operators to evaluate the properties of the scanned resources.
Your expressions can be simple checks of a single value or more complex
compound expressions that check multiple values or conditions. Either way,
the expression must resolve to a boolean true
to trigger a finding.
If you are creating a custom module in the Google Cloud console, you write these expressions in the Expression editor on the Configure module page.
If you are coding a custom module in a YAML file, you add these expressions
under the predicate
property.
Regardless of whether you are using the Google Cloud console or a YAML file, CEL expressions that evaluate resource properties, must conform to the following rules:
- The properties that you specify in a CEL expression must be properties of the scanned resource, as defined in the API definition of the resource type.
If a custom module evaluates multiple resource types, the properties that you specify in the CEL expressions must be common to each resource type the custom module evaluates.
For example, if you define a custom module called
invalid_cryptokey
that checks two resource types:cloudkms.googleapis.com/CryptoKey
andcloudkms.googleapis.com/CryptoKeyVersion
, you could write the following expression, because both theCryptoKey
andCryptoKeyVersion
resource types include thename
property:predicate: resource.name.matches("projects/project1/locations/us-central1/keyRings/keyring1/cryptoKeys/.*")
However, you can't specify the following expression in the
invalid_cryptokey
custom module because theimportTime
androtationPeriod
properties that the expression evaluates are not shared by both resource types:predicate: resource.importTime >= timestamp("2022-10-02T15:01:23Z") || resource.rotationPeriod > duration("2592000s")
All enums in a CEL expression must be represented as strings. For example, the following is a valid expression for the
cloudkms.googleapis.com/CryptoKeyVersion
resource type:resource.state = "PENDING_GENERATION"
The result of the CEL expressions that you define in the
predicate
property must be a Boolean. A finding is triggered only if the result istrue
.
For more information about CEL, see the following:
Example CEL expressions
The following table lists some CEL expressions that can be used to evaluate resource properties. You can use these both in the Google Cloud console and in YAML custom module definitions.
Resource type | Explanation | CEL expression |
---|---|---|
Any resource with an IAM policy | IAM policy check to identify members from outside domain | !policy.bindings.all(binding, binding.members.all(m ,!m.endsWith('@gmail.com'))) |
cloudkms.googleapis.com/CryptoKey |
Cloud KMS key rotation period check | has(resource.rotationPeriod) && resource.rotationPeriod > duration('60h') |
Multiple resource types with a single policy | Check if the resource name starts with dev or devAccess
based on resource type |
(resource.type == 'compute.googleapis.com/Disk' &&
resource.name.startsWith('projects/PROJECT_ID/regions/
REGION/disks/devAccess')) || (resource.type ==
'compute.googleapis.com/Instance ' &&
resource.name.startsWith('projects/PROJECT_ID/zones/REGION/instances/dev-')) |
compute.googleapis.com/Network |
Virtual Private Cloud peering rule to match network peers | resource.selfLink.matches('https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/networks/default') || resource.peerings.exists(p, p.network.matches('https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/networks/shared$')) |
cloudfunctions.googleapis.com/CloudFunction |
Only allow internal ingress traffic for Cloud Run function | has(resource.ingressSettings) && resource.ingressSettings.matches('ALLOW_INTERNAL_ONLY') |
compute.googleapis.com/Instance |
Resource name matches pattern | resource.name.matches('^gcp-vm-(linux|windows)-v\\d+$') |
serviceusage.googleapis.com/Service |
Only allow storage-related APIs to be enabled | resource.state == 'ENABLED' && !( resource.name.matches('storage-api.googleapis.com') || resource.name.matches('bigquery-json.googleapis.com') || resource.name.matches('bigquery.googleapis.com') || resource.name.matches('sql-component.googleapis.com') || resource.name.matches('spanner.googleapis.com'))
|
sqladmin.googleapis.com/Instance
|
Only allowlisted public IPs are allowed | (resource.instanceType == 'CLOUD_SQL_INSTANCE' && resource.backendType == 'SECOND_GEN' && resource.settings.ipConfiguration.ipv4Enabled ) && !(resource.ipAddresses.all(ip, ip.type != 'PRIMARY' || ip.ipAddress.matches('IP_ADDRESS'))))
|
dataproc.googleapis.com/Cluster |
Check if project IDs in a Dataproc cluster contain the substrings testing or development | has(resource.projectId) && resource.projectId.contains('testing') || resource.projectId.contains('development') |
CEL expressions for custom finding properties
Optionally, to return additional information with each finding that can be used in finding queries, you can define up to ten custom properties to return with the findings that are generated by your custom modules.
The custom properties appear in the source properties of the finding in the its JSON and under the Source properties tab of the finding details in Google Cloud console.
You define custom properties as name
-value
pairs.
If you are creating a custom module in the Google Cloud console,
you define the name
-value
pairs in the Custom finding properties
section on the Define finding details page.
If you are coding a custom module in a YAML file, you list the name
-value
pairs as properties
under the custom_output
property.
Regardless of whether you are using the Google Cloud console or a YAML file, the following rules apply:
- Specify
name
as a text string without quotation marks. Specify
value
as one of the following:To return the value of a property, specify the property in the following format:
RESOURCE_TYPE.PROPERTY.PROPERTY_TO_RETURN
In the example:
RESOURCE_TYPE
can be eitherresource
orpolicy
.PROPERTY
one or more parent properties of the property that contains the value to return.PROPERTY_TO_RETURN
is the property that contains the value to return.To return a text string, enclose the string in quotation marks.
The following example shows two name
-value
pairs properly defined under
custom_output
in a YAML file:
custom_output: properties: - name: duration value_expression: expression: resource.name - name: property_with_text value_expression: expression: "Note content"
What's next
To test, submit, view, and update custom modules, see the following pages:
- To test custom modules, see Test custom modules for Security Health Analytics.
- To upload, view, and update custom modules, see Creating and managing custom modules for Security Health Analytics.