Skip to main content

Create a Custom Policy Query

Before creating a policy in the Lacework Console or with the Lacework CLI or API, you need to create the Lacework Query Language query that the policy will use. This topic takes you through the steps to create a custom query, using an example that checks for unrestricted ingress to TCP port 445 in the Lacework Console.

The steps below describe how to discover datasources and the fields they contain. Before starting, we recommend looking for a policy that is similar to the one you want to create, and examine its LQL query. It's possible you may be able to copy the query and make a query that meets your specific requirements with just a few tweaks.

Lacework Academy Course Available

If you want to learn more about Custom Policies and LQL, take the Fine Tuning & Customization module (part of Technical Foundations: Deploy) from the Lacework Academy.

An LQL query checks that state of data in a particular datasource. A datasource contains a set of data that Lacework has collected from your cloud integration.

The easiest way to learn about LQL datasources is to discover the names of the datasources and then get details about the ones that you want to learn more about.

To get a list of datasources, run the command that corresponds to your cloud provider:

AWS
lacework query list-sources | grep AWS
Google Cloud (Google Cloud datasources are currently in beta)
lacework query list-sources | grep GCP
Azure (Azure datasources are currently in beta)
lacework query list-sources | grep AZURE

Discover Datasource Fields

The examples use the following datasources:

  • AWS datasource: LW_CFG_AWS_EC2_SECURITY_GROUPS
  • Google Cloud datasource (currently in beta): LW_CFG_GCP_COMPUTE_FIREWALL
  • Azure datasource (currently in beta): LW_CFG_AZURE_NETWORK_NETWORKSECURITYGROUPS

In order to learn which fields to use in your query, run the lacework query show-source command for a description of the fields. For some datasources, you can run the lacework query preview-source command (not available for all datasources).

The following command, for example, shows the details for the LW_CFG_AWS_EC2_SECURITY_GROUPS datasource.

lacework query show-source LW_CFG_AWS_EC2_SECURITY_GROUPS
Response
            DATASOURCE                      DESCRIPTION
---------------------------------+---------------------------------
LW_CFG_AWS_EC2_SECURITY_GROUPS Results from AWS EC2
'describe-security-groups'

FIELD NAME DATA TYPE DESCRIPTION
-------------------+-----------+---------------------------------
BATCH_START_TIME Timestamp Beginning of time interval
BATCH_END_TIME Timestamp End of time interval
QUERY_START_TIME Timestamp Start time of query for this
resource
QUERY_END_TIME Timestamp End time of query for this
resource
ARN String ARN for the resource
API_KEY String Key describing the API used to
fetch data for this resource
SERVICE String Service this resource belongs
to
ACCOUNT_ID String AWS Account ID
ACCOUNT_ALIAS String User friendly alias for AWS
Account
RESOURCE_TYPE String Type of this resource
RESOURCE_ID String Identifier for this resource
RESOURCE_REGION String Region this resource belongs
to
RESOURCE_CONFIG JSON JSON Definition of this
resource
RESOURCE_TAGS JSON Tags associated with this
resource

The RESOURCE_CONFIG field is frequently used in LQL. Because it is a JSON datasource, the LQL query must first convert the field using the array_to_rows() function. To know exactly which JSON fields you need, you can either read the cloud provider's API documentation, or write an LQL query to explore the full content before writing the actual policy.

Explore Datasources Using LQL

The following steps demonstrate how to explore the LW_CFG_AWS_EC2_SECURITY_GROUPS datasource. Replace the datasource with LW_CFG_GCP_COMPUTE_FIREWALL or LW_CFG_AZURE_NETWORK_NETWORKSECURITYGROUPS, respectively, if using GCP or Azure.

  1. Open your text editor, create a new file, and add the following content:

    ---
    queryId: Explore_AWS_EC2_SECURITY_GROUPS
    queryText: |-
    {
    source {
    LW_CFG_AWS_EC2_SECURITY_GROUPS
    }
    return {
    RESOURCE_CONFIG
    }
    }
  2. Save the file as YAML with the filename Explore_AWS_EC2_SECURITY_GROUPS.yaml. Note the file's location.

  3. In the Lacework CLI, run this command:

    lacework query run -f <path_to>/Explore_AWS_EC2_SECURITY_GROUPS.yaml
    Response
    {
    "RESOURCE_CONFIG": {
    "Description": "default VPC security group",
    "GroupId": "sg-000",
    "GroupName": "default",
    "IpPermissions": [
    {
    "IpProtocol": "-1",
    "IpRanges": [],
    "Ipv6Ranges": [],
    "PrefixListIds": [],
    "UserIdGroupPairs": [
    {
    "GroupId": "sg-000",
    "UserId": "111"
    }
    ]
    }
    ],
    "IpPermissionsEgress": [
    {
    "IpProtocol": "-1",
    "IpRanges": [
    {
    "CidrIp": "0.0.0.0/0"
    }
    ],
    "Ipv6Ranges": [],
    "PrefixListIds": [],
    "UserIdGroupPairs": []
    }
    ],
    "OwnerId": "111",
    "VpcId": "vpc-000"
    }
    }

Create a Query

As described in LQL Overview, an LQL is made up of three parts: a source, filter, and return statement. Considering the discoveries described above, the following examples show how a query can utilize the source.

AWS query
  {
source {
LW_CFG_AWS_EC2_SECURITY_GROUPS a,
array_to_rows(a.RESOURCE_CONFIG:IpPermissions) as (ip_permissions),
array_to_rows(ip_permissions:IpRanges) as (ip_ranges)
}
filter {
ip_permissions:IpProtocol = 'tcp'
and ip_permissions:FromPort = 445
and ip_permissions:ToPort = 445
and ip_ranges:CidrIp = '0.0.0.0/0'
}
return distinct {
ACCOUNT_ALIAS,
ACCOUNT_ID,
ARN as RESOURCE_KEY,
RESOURCE_REGION,
RESOURCE_TYPE,
SERVICE
}
}
GCP query (currently in beta)
  {
source {
LW_CFG_GCP_COMPUTE_FIREWALL firewall,
array_to_rows(firewall.RESOURCE_CONFIG:allowed) as (allowed),
array_to_rows(allowed:ports) as (ports),
array_to_rows(firewall.RESOURCE_CONFIG:sourceRanges) as (ranges)
}
filter {
RESOURCE_CONFIG:direction = 'INGRESS'
and allowed:IPProtocol = 'tcp'
and ports = '445'
and ranges = '0.0.0.0/0'
}
return distinct {
ORGANIZATION_ID,
PROJECT_NUMBER,
PROJECT_ID,
FOLDER_IDS,
URN as RESOURCE_KEY,
RESOURCE_REGION,
RESOURCE_TYPE,
SERVICE
}
}
Azure query (currently in beta)
  {
source {
LW_CFG_AZURE_NETWORK_NETWORKSECURITYGROUPS a,
array_to_rows(a.RESOURCE_CONFIG:securityRules) as (rules)
}
filter {
rules:"properties".access = 'Allow'
and rules:"properties".direction = 'Inbound'
and rules:"properties".protocol = 'Tcp'
and rules:"properties".destinationPortRange = '445'
and rules:"properties".sourceAddressPrefix = '*'
}
return distinct {
TENANT_ID,
TENANT_NAME,
SUBSCRIPTION_ID,
SUBSCRIPTION_NAME,
URN as RESOURCE_KEY,
RESOURCE_REGION,
RESOURCE_TYPE
}
}

Now follow the steps in Create Custom Policies to create a policy and use the query in a new policy.