Custom Rules

Note

Looking for some examples of custom rules? Check out our GitHub repo!

Fugue’s custom rules feature allows you to add user-defined compliance rules to your environment through the Custom Rules Page (UI) or API. Fugue evaluates your infrastructure against the rules and displays compliance results as it would for out-of-the-box standards such as SOC 2 or HIPAA. The visualizer also denotes noncompliance with custom rules.

A custom rule is a type of compliance control used to assess adherence to a user-defined resource configuration.

You can use custom rules to enforce enterprise policies.

Custom rules function at the organization level, so they are automatically enabled for all your environments.

What are Custom Rules?

Custom rules are written using Rego, an open source policy-as-code language for Open Policy Agent (OPA).

Rego is a query language. A Rego rule consists of one or more queries that return information from a Fugue scan. Fugue uses Rego rules to determine whether a resource is compliant with a given configuration.

There are two types of rules:

Here is an example of a simple rule, which we’ll explain in detail later in this guide:

allow {
  input.multi_az == true
}

Here’s an example of an advanced rule, which we’ll also discuss later:

# Return all security groups in an environment
security_groups = fugue.resources("AWS.EC2.SecurityGroup")

# Security groups that have port 9200 open to the internet are considered invalid
invalid(sg) {
  sg.ingress[i].from_port <= 9200
  sg.ingress[i].to_port >= 9200
  sg.ingress[i].cidr_blocks[_] == "0.0.0.0/0"
}

# Build policy document; invalid security groups fail, valid ones pass
policy[r] {
   security_group = security_groups[_]
   invalid(security_group)
   r = fugue.deny_resource(security_group)
} {
   security_group = security_groups[_]
   not invalid(security_group)
   r = fugue.allow_resource(security_group)
}

General Custom Rules Workflow

To create a custom rule, you first write Rego code defining a pass (allow) or fail (deny) condition for the attribute. Then, you validate and add the rule to Fugue from the Custom Rules page (UI) or the API.

To write a custom rule, you must:

  1. Determine the resource attribute(s) you want Fugue to check

  2. Define pass or fail conditions for the resource

Once you’ve successfully written a rule, you can:

  • Validate and add the rule to Fugue from the Custom Rules page (aka UI) or API

  • Modify the rule’s name, description, or Rego definition (UI | API)

  • Delete the rule (UI | API)

  • View compliance results by rule (UI | API), resource type (UI | API), or resource (UI only).

  • View compliance results in the visualizer

See the API Reference for Swagger documentation.

Writing Custom Rules

As stated earlier, you can write custom rules using Rego, an open source query language for Open Policy Agent (OPA).

We won’t detail how to write Rego here, but we encourage you to consult the Rego documentation for the basics.

We’ve added some Fugue-specific functionality to Rego. For details, jump ahead to the Custom Rules Cheat Sheet. Otherwise, continue reading to learn how to write custom rules.

Simple Custom Rules

First, we’ll dive into how to write a simple custom rule, which tests a single resource type.

(Can’t wait to write rules for multiple resources? Feel free to skip ahead to Advanced Custom Rules.)

Step 1: Determine resource attributes (simple)

Once you have a policy in mind that your resources should comply with, you can start writing a custom rule.

To translate policy to code, you’ll need to determine the resource type and attributes that are involved. There are a couple ways to do this:

You can reference the Terraform documentation to determine the resource types and syntax required for AWS and AWS GovCloud and Azure. You’ll find the information you need in the Resource page of each provider resource. For an example, see Resource: aws_security_group.

However, it may be easier to write rules by looking at a concrete example. If you have an environment (or create a new one) that contains the resource type you’re evaluating, you can determine which attributes you need to specify in the rule by using the API to retrieve the results of a recent scan (see Creating a Rule via API - Get Input for Test for details).

Once you have the JSON document containing the scan results, you’ll see the configuration for every resource in the scan. You can then find the attribute in question. See the API Reference for details.

Step 2: Define pass or fail conditions for the resource (simple)

Let’s say your organization requires AWS RDS instances to be deployed in multiple availability zones. If you look at the Terraform RDS docs, you’ll see the aws_db_instance resource lists a multi_az attribute. So, the custom rule should query each AWS.RDS.Instance in an environment and verify that the multi_az property is enabled (i.e., set to true). If so, the resource passes the compliance check. If not, the resource fails.

allow rules

In Fugue, you define a “pass” state by using the allow function. You can define a “fail” state by using the deny function.

For our RDS example, it makes sense to use the allow function, because anything with multi_az set to true should pass the compliance check.

Let’s look at the example again:

allow {
  input.multi_az == true
}

This simple rule contains the following elements:

allow { ... }

This function states that any resource matching the query in the brackets should be assigned a PASS compliance state.

input.multi_az

This element tells Fugue to look in the provided input for the multi_az attribute. When you specify the resource type to assess, Fugue automatically sets the input to that resource type.

== true

If the preceding attribute is true, execute the function. Here, that means Fugue should allow (“pass”) resources that have multi_az set to true.

deny rules

You can also write the rule with the deny function, which accomplishes the same thing by failing any resource where input.multi_az is set to false:

deny {
  input.multi_az == false
}

Another example of a deny function would be a policy that forbids security groups from having port 9200 (Elasticsearch) open to the internet. Any resources matching that query should fail the compliance check. We’ll discuss this use case in the section below.

Multiple queries

So far, all the example rules we’ve shown have single queries, but Rego rules can also consist of multiple queries. These queries can all be required, or some can be required – just like the logical operators AND (&&) and OR (||).

For example, the security group policy we mentioned in the previous section – where port 9200 should not be open to the world – can be written thusly:

deny {
  input.ingress[i].from_port <= 9200
  input.ingress[i].to_port >= 9200
  input.ingress[i].cidr_blocks[_] == "0.0.0.0/0"
}

Let’s dissect this rule:

deny { ... }

This function states that any resource matching the query in the brackets should be assigned a FAIL compliance state.

input.ingress[i].from_port <= 9200

We’ll explain the syntax in more detail in a bit, but this means Fugue should look at the input (security group resources) for the from_port attribute and check whether the value is less than or equal to 9200.

input.ingress[i].to_port >= 9200

Likewise, this line means Fugue should check whether the to_port is greater than or equal to 9200.

input.ingress[i].cidr_blocks[_] == "0.0.0.0/0"

This line means checks whether the cidr_blocks attribute is set to “0.0.0.0/0” (meaning the port is open to the world).

Note that there are three queries. Because they’re all contained in the same deny {} function, all queries must apply in order for the resource to fail the compliance check. You can restate it in pseudocode like this:

IF the ingress port range starts at 9200 or below, AND the ingress port range ends at 9200 or above, AND the ingress CIDR block is “0.0.0.0/0” (open to the internet), THEN deny (fail) the resource.

To demonstrate how Rego rules handle OR logic, we can rewrite the same rule thusly:

deny {
  input.ingress[i].to_port == 9200
  input.ingress[i].from_port == 9200
  input.ingress[i].cidr_blocks[_] == "0.0.0.0/0"
}

deny {
   input.ingress[i].from_port < 9200
   input.ingress[i].from_port > 9200
   input.ingress[i].cidr_blocks[_] == "0.0.0.0/0"
}

Here, there are two deny functions, which means the rule is denied if either function applies.

The first deny function states that if the port range is set to 9200 and the CIDR block source is "0.0.0.0/0", the resource should fail.

The second deny function states that if the port range includes 9200 and the CIDR block source is "0.0.0.0/0", the resource should fail.

Should either of these conditions be met, Fugue will assign the resource a FAIL compliance state.

This is a less efficient way of writing the same rule, but it demonstrates how you can use OR logic in Rego rules.

Advanced Custom Rules

While simple custom rules focus on a single resource type, advanced custom rules look at the entire environment and can therefore optionally involve multiple resource types (though advanced rules can also involve a single resource type, and we’ll show you how here and here).

This makes it possible to mix and match different resource types and join them in a variety of ways. Advanced rules are far more expressive than simple rules, but they are also more complex.

The high-level workflow for advanced rules is usually:

  1. Query for resources by using the fugue.resources(resource_type) function.

  2. Declare a policy set rule that iterates over queried resources.

  3. Extend the policy set using one of the following primitives (explained in more detail here):

  • fugue.allow_resource(resource)

  • fugue.deny_resource(resource)

  • fugue.missing_resource(resource_type)

Advanced Custom Rule with a Single Resource Type

If we consider the simple rule to check whether security group port 9200 is open to the world, this would be an advanced port of that rule. Even though it checks a single resource type, the use of the functions fugue.resources(resource_type), fugue.allow_resource(resource), and fugue.deny_resource(resource) make it an advanced rule:

# Return all security groups in an environment
security_groups = fugue.resources("AWS.EC2.SecurityGroup")

# Security groups that have port 9200 open to the internet are considered invalid
invalid(sg) {
  sg.ingress[i].from_port <= 9200
  sg.ingress[i].to_port >= 9200
  sg.ingress[i].cidr_blocks[_] == "0.0.0.0/0"
}

# Build policy document; invalid security groups fail, valid ones pass
policy[r] {
   security_group = security_groups[_]
   invalid(security_group)
   r = fugue.deny_resource(security_group)
} {
   security_group = security_groups[_]
   not invalid(security_group)
   r = fugue.allow_resource(security_group)
}

Let’s dig into what these new functions do.

Advanced Rule Functions

fugue.resources(resource_type)

Returns a collection of resources of the given resource_type string. Fugue also keeps track of which resource types are requested. We use this information to decide whether there are simply no resources of a type, or if we do not have permission to scan that resource type.

fugue.allow_resource(resource)

Makes a PASS judgement about a resource retrieved using fugue.resources.

fugue.deny_resource(resource)

Makes a DENY judgement about a resource retrieved using fugue.resources.

fugue.missing_resource(resource_type)

Makes a DENY judgement about a resource type string without actually having access to a resource. This can be used to write a rule that checks for the presence of a specific resource.

Why do we need more than allow and deny? For example, if we require a CloudTrail alarm for failed logins, it does not mean that other CloudTrail alarms are invalid – they are simply irrelevant. Hence, we should not mark these other CloudTrail alarms as DENY using fugue.deny_resource, but rather use fugue.missing_resource to indicate that a resource is missing.

Expanding an Advanced Custom Rule

We can expand the example above to check only certain security groups based on tags. Here, we only look at security groups with the tag key Stage and value Prod to determine whether any have port 9200 open to the world:

# Return all security groups in an environment, then filter on the tag Stage:Prod
tagged_sgs[tags] = security_group {
   security_groups = fugue.resources("AWS.EC2.SecurityGroup")
   security_group = security_groups[tags]
   security_group.tags.Stage == "Prod"
}

# Security groups that have port 9200 open to the internet are considered invalid
invalid(sg) {
  sg.ingress[i].from_port <= 9200
  sg.ingress[i].to_port >= 9200
  sg.ingress[i].cidr_blocks[_] == "0.0.0.0/0"
}

# Build policy document; of the security groups tagged Stage:Prod, invalid SGs fail, valid ones pass
policy[r] {
   security_group = tagged_sgs[_]
   invalid(security_group)
   r = fugue.deny_resource(security_group)
} {
   security_group = tagged_sgs[_]
   not invalid(security_group)
   r = fugue.allow_resource(security_group)
}

Advanced Custom Rules with Multiple Resource Types

Here’s an example of an advanced custom rule with multiple resource types – in this case, AWS.EC2.Vpc and AWS.EC2.SecurityGroup. This rule checks whether all security groups attached to a production VPC have a specific tag. If the security groups have the correct tag, they pass the compliance check; if not, they fail:

# The following multi-resource type validation checks that all Security Groups
# attached to the production VPC have a Stage tag with the value Prod.
# The production VPC.
prod_vpc = vpc {
  vpcs = fugue.resources("AWS.EC2.Vpc")
  vpc = vpcs[_]
  vpc.tags.Name == "prod-vpc"
}
# Security groups attached to the prod VPC.
prod_security_groups[id] = security_group {
  security_groups = fugue.resources("AWS.EC2.SecurityGroup")
  security_group = security_groups[id]
  security_group.vpc_id == prod_vpc.id
}
# Check that the security group is tagged with {"Stage": "Prod"}.
tagged_security_group(security_group) {
  security_group.tags.Stage == "Prod"
}
# Build policy document.
policy[p] {
  security_group = prod_security_groups[_]
  tagged_security_group(security_group)
  p = fugue.allow_resource(security_group)
} {
  security_group = prod_security_groups[_]
  not tagged_security_group(security_group)
  p = fugue.deny_resource(security_group)
}

For further assistance with advanced custom rules, reach out to support@fugue.co.

Custom Rules Cheat Sheet

When writing a custom rule in Fugue, remember the following tips:

  • Custom rule policies do not require the package declaration at the start of a rule.

  • Simple rules must specify at least one allow or deny rule.

    • When more than one allow or deny rule are given, they are simply “OR”-ed together, following standard Rego semantics.

    • If both allow and deny are specified in the same policy, then deny rules will override allow rules.

  • We’ve provided Fugue-specific functions to make writing advanced rules easier.

Creating and Managing Custom Rules - UI

Ready to begin? Below are general instructions for creating and managing custom rules with Fugue via the Custom Rules page (UI).

In a hurry? Jump ahead:

Creating Custom Rules - UI

Note

For API instructions, see Creating Custom Rules - API.

1. In the Custom Rules page, click the Create New Custom Rule button. The Create New Custom Rule page displays, as shown below.

_images/Create_Custom_Rule_Page.png

2. Enter a name for your rule. The name entered here displays on the Rule or Rule Failed fields on the Compliance pages for your environments. Refer to Viewing Compliance Results for more information.

3. Enter a description for your rule. The description entered here displays on the Description field on the Compliance pages for your environments. Refer to Viewing Compliance Results for more information.

4. Select the Cloud Provider. Based on the cloud provider selected determines the resource types that display from the Resource Types drop-down.

5. Select the Resource Type from the drop-down. This is the resource type that you want your custom rule to evaluate. Refer to Writing Custom Rules for more information.

6. In the rule definition, enter your Rego. Refer to Writing Custom Rules for more information.

7. Optionally, click the Validate Rego button. If your rule has invalid syntax, an error displays, similar to what is shown below. Fix the invalid syntax or you can choose to save your rule and edit it later.

_images/Invalid_Rule_Validate_Endpoint.png
  • If the rule has valid syntax, it displays similar to what is shown below.

_images/Valid_Rule_Validate_Endpoint.png

8. Click the Create Custom Rule button. When created, a custom rule is set to an “enabled” status if the rule has valid syntax and it is automatically applied to all environments on the subsequent scheduled scans.

  • If the rule contains invalid syntax, the following error modal displays similar to what is shown below. You can chose to fix the rule now or later. Custom rules that contain invalid syntax are not automatically ran against your environments until there are no syntax errors and the custom rule is set to an “invalid” status.

_images/Syntax_Error_Modal.png

Note

You can manually kick off a scan for your environment(s) to see the result of your custom rule against the specified environment(s) using the Fugue API. Refer to Initiating a Scan for more information.

Modifying and Deleting Custom Rules - UI

Note

You cannot modify the cloud provider for an existing custom rule. If you would like to create a rule with a different cloud provider, you need to create a new custom rule.

Note

For API instructions, see Modifying and Deleting Custom Rules - API.

Below are general instructions for modifying or deleting custom rules with Fugue in the UI.

To edit or delete a custom rule in the UI, navigate to the Custom Rules tab and find the ellipsis ... next to the custom rule you want to edit or delete:

_images/Modify_Delete_Custom_Rule.png

Viewing Compliance Results - UI

Note

For API instructions, see Viewing Compliance Results - API.

All custom rules with valid syntax automatically run against your environment on the next scheduled scan. After the scheduled scan runs or is kicked off manually via the API, the Custom compliance standard displays for your environment. You can filter by Custom to only view the results for your custom rule(s).

_images/Custom_Compliance_Family.png

Additionally, the three compliance tabs allow you to take a deeper dive into why your custom rule is passing or failing.

Compliance by Rule - UI

For API instructions, see Compliance by Rule - API.

Custom rules display similar to what is shown below. The name you entered for the rule on the Create Custom Rule page displays in the Rule column and the description you entered displays in the Description column.

_images/Compliance_by_Rule.png

Clicking on a failed rule displays similar to what is shown below.

_images/Custom_Rule_Compliance_Modal.png

Compliance by Resource Type - UI

For API instructions, see Compliance by Resource Type - API.

Custom rules display similar to what is shown below on the Compliance by Resource Type tab.

_images/Compliance_by_ResourceType.png

Clicking on a noncompliant resource type displays similar to what is shown below. The name you entered for the rule on the Create Custom Rule page displays in the Rule Failed column and the description you entered displays in the Description column.

_images/Custom_Compliance_Family_Modal.png

Compliance by Resource - UI

Viewing compliance by resource is only supported in the UI, not the API.

Custom rules display similar to what is shown below on the Compliance by Resource tab.

_images/Compliance_by_Resource_Custom.png

Clicking on a noncompliant resource displays similar to what is shown below.The name you entered for the rule on the Create Custom Rule page displays in the Rule Failed column and the description you entered displays in the Description column.

_images/Compliance_By_Resource_Custom_Rules.png

Creating, Testing, and Managing Custom Rules - API

Note

For UI instructions, see Creating and Managing Custom Rules - UI.

Ready to begin? Below are general instructions for creating custom rules with Fugue via the API.

In a hurry? Jump ahead:

Creating Custom Rules - API

For UI instructions, see Creating Custom Rules - UI.

Creating a custom rule via the API generally involves three steps:

  1. Optional: Get the input for a custom rule test (Example request | API Reference)

  2. Optional: Test the custom rule (Example request | API Reference)

  3. Add the rule (Example request | API Reference)

This video demonstrates how to test and create a custom rule through the API:

Creating a Rule via API - Get Input for Test

(Example request | API Reference)

It’s a best practice to test a custom rule with the API before adding it to your organization. To test a custom rule, we recommend that you retrieve test input first, which is scan data to test the rule against. (When you test a rule with Fugue, it is tested against the resources recorded in that scan.) However, it is an optional step, since Fugue only needs the scan ID to test a rule – retrieving/viewing the input is strictly for the user’s benefit. You also don’t have to test a rule; you can skip ahead to adding it, and the API will reject it if the syntax is correct, but you won’t know if it produces the intended results until after the next scan. We highly recommend you test rules before adding them via the API.)

To retrieve the scan data from the API, start by finding the scan ID for the data you want to use. You can use the ID from any past scan, or you can manually kick off a new scan and use that ID – see Initiating a Scan for details.

Next, send a GET request to the Fugue API /rules/test/input?scan_id={scan_id} endpoint, replacing {scan_id} with your scan ID. Here’s an example curl command that returns the state of the infrastructure recorded in scan 535d21d2-700b-4b4f-9aa9-123456789012:

curl -X GET \
"https://api.riskmanager.fugue.co/v0/rules/test/input?scan_id=535d21d2-700b-4b4f-9aa9-123456789012" \
  -u $CLIENT_ID:$CLIENT_SECRET

(You can also use a GUI such as Postman. For more information, see the API User Guide.)

The API response is a JSON representation of each resource in your environment from that scan. Here’s an abbreviated example of two VPCs:

{
    "resources": {
        "aws_vpc.dnBjLTAyYjEwMjQ4123456789012": {
            "_skeleton": {
                "depends_on": null,
                ...
            },
            ...
            "cidr_block": "10.0.0.0/16",
            "id": "vpc-02b10248f12345678",
            "tags": {
                "Name": "my-vpc",
                "Application": "Portal"
            }
        },
        "aws_vpc.dnBjLT9999999999": {
            "_skeleton": {
                "depends_on": null,
                ...
            },
            ...
            "cidr_block": "172.31.0.0/16",
            "id": "vpc-92281234",
            "tags": {}
        }
    }
}

Once you have this information, you can determine the expected result of your custom rule test. For example, if you are writing a rule that tests whether all VPCs in an environment have the tag key Application and the tag value Portal, the scan data can tell you that one VPC should pass (vpc-02b10248f12345678) and one should fail (vpc-92281234).

Using Test Input to Write Rules

You can also use the infrastructure state JSON to determine the resource attributes and syntax for writing custom rules. The way resource configuration is structured in the scan data is the same structure Fugue expects for custom rules.

So, let’s say your scan data includes a VPC like this one (cut for length):

{
    "resources": {
        ...
        "aws_vpc.dnBjLTAyYjEwMjQ4123456789012": {
            ...
            "_type": "AWS.EC2.Vpc",
            "cidr_block": "10.0.0.0/16",
            "id": "vpc-02b10248f12345678",
            "tags": {
                "Name": "my-vpc",
                "Application": "Portal"
            }
        }
    }
}

In this case, a simple rule can refer to the VPC CIDR block property with input.cidr_block or the VPC ID with input.id. The example below uses the syntax input.tags.Application to pass VPCs tagged with the key Application and value Portal, following the JSON structure of the input above:

allow {
  input.tags.Application == "Portal"
}

For an advanced rule you could use <variable>.id to refer to the VPC ID, or <variable>.cidr_block to refer to the VPC CIDR block. In the example below, which is an advanced version of the simple rule above, we used the variable vpc to refer to an individual VPC and used vpc.tags.Application to refer to the VPC tag key Application following the JSON structure of the input:

policy[p] {
  vpcs = fugue.resources("AWS.EC2.Vpc")
  vpc = vpcs[_]
  vpc.tags.Application == "Portal"
  p = fugue.allow_resource(vpc)
} {
  vpcs = fugue.resources("AWS.EC2.Vpc")
  vpc = vpcs[_]
  not vpc.tags.Application == "Portal"
  p = fugue.deny_resource(vpc)
}

Creating a Rule via API - Test the Rule

(Example request | API Reference | YouTube video)

Note

There are some important things to note when sending an API request to test a rule – see Custom Rules API - Things to Know.

You can determine whether a custom rule will work by testing the code against infrastructure state from a particular scan. Note that getting the test input (scan data) from the API is not a prerequisite for testing a rule; it’s strictly for the user’s benefit, since Fugue needs only the scan ID in order to test the rule against the scan data.

Whether you retrieve the scan data or not, the next step is to test the rule against the API to determine correctness of syntax and accuracy of results.

Start by finding the scan ID for the data you want to use, if you haven’t already. You can use the ID from any past scan, or you can manually kick off a new scan and use that ID; see Initiating a Scan for details.

Next, send a POST request to the Fugue API /rules/test endpoint with a request body containing the custom rule code, resource type, and scan ID of the data Fugue will use to test the rule. You can save the JSON in a file and attach it with the -d @{filename} option. Here’s an example curl request to /rules/test:

curl -X POST \
"https://api.riskmanager.fugue.co/v0/rules/test" \
  -u $CLIENT_ID:$CLIENT_SECRET \
  -H "Content-Type: application/json" \
  -d @body.json

(You can also use a GUI such as Postman. For more information, see the API User Guide.)

And here’s an example request body for a rule that passes VPCs with the tag key Application and tag value Portal:

{
    "rule_text": "allow { input.tags.Application == 'Portal' }",
    "scan_id": "535d21d2-700b-4b4f-9aa9-123456789012",
    "resource_type": "AWS.EC2.Vpc"
}

Fugue returns the results of the rule if the syntax is correct, or an error if Fugue cannot parse it. (Note that the API cannot report semantic errors, only parsing errors – just like programming in general, confirm that your custom rule has the expected results before you add it to your organization!)

Example rule results:

{
    "errors": [],
    "resources": [
        {
            "id": "vpc-02b10248f12345678",
            "result": "PASS",
            "type": "AWS.EC2.Vpc"
        },
        {
            "id": "vpc-92281234",
            "result": "FAIL",
            "type": "AWS.EC2.Vpc"
        }
    ],
    "result": "FAIL"
}

Example error message:

{
    "errors": [
        {
            "severity": "error",
            "text": "fregot (custom_rules_engine error):\n  Expected at least one `allow` or `deny` rule in the module for a single resource type rule."
        }
    ],
    "resources": [],
    "result": "UNKNOWN"
}

Creating a Rule via API - Add the Rule

(Example request | API Reference | YouTube video)

Note

There are some important things to note when sending an API request to add a rule – see Custom Rules API - Things to Know.

Getting scan data and testing a rule are not prerequisites for adding a rule via the API, but we strongly recommend taking these steps first. The Fugue API checks the syntax when you add the rule but cannot report semantic errors, so you won’t know if the rule has the intended results until after the next scan.

To start, send a POST request to the Fugue API /rules endpoint with a request body containing the following information:

  • name: Name of the rule

  • source: Always "CUSTOM"

  • description: Description of the rule

  • provider: Either AWS, AWS_GOVCLOUD, or AZURE

  • resource_type:

    • Simple rules: The resource type the rule applies to, formatted according to the list on the Service Coverage page (e.g., "AWS.S3.Bucket", "Azure.Network.NetworkSecurityRule")

    • Advanced rules: Always "MULTIPLE", even if the rule only involves one resource type. See Custom Rules API - Things to Know

  • rule_text: Rego code used by the rule. See Custom Rules API - Things to Know

(See the API Reference for more information.)

You can save the JSON in a file and attach it with the -d @{filename} option. Here’s an example curl request to /rules:

curl -X POST \
"https://api.riskmanager.fugue.co/v0/rules" \
  -u $CLIENT_ID:$CLIENT_SECRET \
  -H "Content-Type: application/json" \
  -d @body.json

(You can also use a GUI such as Postman. For more information, see the API User Guide.)

And here’s an example request body to create a rule that passes VPCs with the tag key Application and tag value Portal:

{
  "name": "AWS VPCs should be tagged Application:Portal",
  "source": "CUSTOM",
  "description": "AWS VPCs in this environment should be tagged with the key 'Application' and value 'Portal'",
  "provider": "AWS",
  "rule_text": "allow { input.tags.Application == 'Portal' }",
  "resource_type": "AWS.EC2.Vpc"
}

Fugue returns the details about the newly added rule if the syntax is correct, or an error if Fugue cannot parse it or encounters another error. (Note that the API cannot report semantic errors, only parsing errors – just like programming in general, confirm that your custom rule has the expected results before you add it to your organization!)

Example rule creation response – note that errors is null and status is ENABLED:

{
   "compliance_controls": [
      "0b4dc932-d5e1-42f0-802e-a2d71a2b3c4d"
   ],
   "created_at": 1574881762,
   "created_by": "61ae1bed-1b6f-4829-0000-123456789012",
   "created_by_display_name": null,
   "description": "AWS VPCs in this environment should be tagged with the key 'Application' and value 'Portal'",
   "errors": null,
   "id": "a3896797-15d7-486e-8dbf-098765432109",
   "name": "AWS VPCs should be tagged Application:Portal",
   "provider": "AWS",
   "resource_type": "AWS.EC2.Vpc",
   "rule_text": "allow { input.tags.Application == 'Portal' }",
   "source": "CUSTOM",
   "status": "ENABLED",
   "updated_at": null,
   "updated_by": null,
   "updated_by_display_name": null
}

Example error message – note that errors lists an error and status is INVALID:

{
  "compliance_controls": [
    "717deb94-62e8-47eb-bbab-a2d71a2b3c4d"
  ],
  "created_at": 1574882676,
  "created_by": "61ae1bed-1b6f-4829-8c85-123456789012",
  "created_by_display_name": null,
  "description": "AWS VPCs in this environment should be tagged with the key 'Application' and value 'Portal'",
  "errors": [
    {
      "severity": "error",
      "text": "fregot (custom_rules_engine error):\n  Expected a `policy` rule in the module for a multi-resource rule."
    }
  ],
  "id": "4edb5efc-5b48-4d5c-9c64-098765432109",
  "name": "AWS VPCs should be tagged Application:Portal",
  "provider": "AWS",
  "resource_type": "MULTIPLE",
  "rule_text": "allow { input.tags.Application == 'Portal' }",
  "source": "CUSTOM",
  "status": "INVALID",
  "updated_at": null,
  "updated_by": null,
  "updated_by_display_name": null
}

Modifying and Deleting Custom Rules - API

Note

There are some important things to note when sending an API request to modify or delete a rule – see Custom Rules API - Things to Know.

For UI instructions, see Modifying and Deleting Custom Rules - UI.

Modifying Custom Rules - API

(Example request | API Reference)

Getting scan data and testing a rule are not prerequisites for modifying a rule via the API, but we strongly recommend taking these steps first. The Fugue API checks the syntax when you update the rule but cannot report semantic errors, so you won’t know if the rule has the intended results until after the next scan.

Start by finding the rule ID for the rule you want to update. You can do this by listing all custom rules and then looking for the ID of the desired rule. Here’s an example curl command that lists all custom rules for an organization:

curl -X GET \
"https://api.riskmanager.fugue.co/v0/rules" \
  -u $CLIENT_ID:$CLIENT_SECRET

In the output, look for the id property:

{
  "count": 1,
  "is_truncated": false,
  "items": [
    {
      "compliance_controls": [
        "0b4dc932-d5e1-42f0-802e-a2d71a2b3c4d"
      ],
      "created_at": 1574881765,
      "created_by": "61ae1bed-1b6f-4829-0000-123456789012",
      "created_by_display_name": null,
      "description": "AWS VPCs in this environment should be tagged with the key 'Application' and value 'Portal' to",
      "id": "a3896797-15d7-486e-8dbf-098765432109",
      "name": "AWS VPCs should be tagged Application:Portal",
      "provider": "AWS",
      "resource_type": "AWS.EC2.Vpc",
      "rule_text": "allow { input.tags.Application == 'Portal' }",
      "source": "CUSTOM",
      "status": "ENABLED",
      "updated_at": null,
      "updated_by": null,
      "updated_by_display_name": null
    }
  ]
}

In the example above, the rule ID is a3896797-15d7-486e-8dbf-098765432109.

Next, send a PATCH request to the Fugue API /rules/{rule_id} endpoint, replacing {rule_id} with your rule ID. You’ll also need a JSON request body containing the following information – name, description, and status are optional and resource_type and rule_text are required:

  • name: Name of the rule

  • description: Description of the rule

  • status: Status of the custom rule; currently, only ENABLED is supported

  • resource_type (required):

    • Simple rules: The resource type the rule applies to, formatted according to the list on the Service Coverage page (e.g., "AWS.S3.Bucket", "Azure.Network.NetworkSecurityRule")

    • Advanced rules: Always "MULTIPLE", even if the rule only involves one resource type. See Custom Rules API - Things to Know

  • rule_text (required): Rego code used by the rule. See Custom Rules API - Things to Know

(See the API Reference for more information.)

You can save the JSON in a file and attach it with the -d @{filename} option. Here’s an example curl request to /rules/{rule_id}:

curl -X PATCH \
"https://api.riskmanager.fugue.co/v0/rules/a3896797-15d7-486e-8dbf-098765432109" \
  -u $CLIENT_ID:$CLIENT_SECRET \
  -H "Content-Type: application/json" \
  -d @body.json

(You can also use a GUI such as Postman. For more information, see the API User Guide.)

And here’s an example request body to update a rule:

{
  "name": "AWS VPCs should be tagged Application:Finance",
  "description": "AWS VPCs in this environment should be tagged with the key 'Application' and value 'Finance'",
  "status": "ENABLED",
  "resource_type": "AWS.EC2.Vpc",
  "rule_text": "allow { input.tags.Application == 'Finance' }"
}

Fugue returns the details about the newly updated rule if the syntax is correct, or an error if Fugue cannot parse it or encounters another error. (Note that the API cannot report semantic errors, only parsing errors – just like programming in general, confirm that your custom rule has the expected results before you modify it using the API!)

Deleting Custom Rules - API

(Example request | API Reference)

Start by finding the rule ID for the rule you want to update, if you haven’t already. See instructions in Modifying Custom Rules - API.

Next, send a DELETE request to the Fugue API /rules/{rule_id} endpoint, replacing {rule_id} with your rule ID. Here’s an example curl command that deletes rule a3896797-15d7-486e-8dbf-098765432109:

curl -X DELETE \
"https://api.riskmanager.fugue.co/v0/rules/a3896797-15d7-486e-8dbf-098765432109" \
  -u $CLIENT_ID:$CLIENT_SECRET

(You can also use a GUI such as Postman. For more information, see the API User Guide.)

If the delete call is successful, there is no output.

Viewing Compliance Results - API

For UI instructions, see Viewing Compliance Results - UI.

All custom rules with valid syntax automatically run against your environment on the next scheduled scan. After the scheduled scan runs or is kicked off manually via the API, you can request the Custom compliance standard results from the API.

Additionally, the three compliance endpoints allow you to take a deeper dive into why your custom rule is passing or failing.

Compliance by Rule - API

For UI instructions, see Compliance by Rule - UI.

To view compliance by rule using the API, see Listing Compliance Results by Rule for a Scan.

Compliance by Resource Type - API

For UI instructions, see Compliance by Resource Type - UI.

To view compliance by resource type using the API, see Listing Compliance Results by Resource Type for a Scan.

Compliance by Resource - API

The API does not currently support viewing compliance by resource. To view compliance by resource results in the UI, see Compliance by Resource - UI.

Custom Rules API - Things to Know

There are three important things to remember about sending API requests for custom rules:

  1. Since the request body is JSON, certain special characters such as newlines and double quotes must be escaped.

  2. A simple rule should format the resource type according to the list on the Service Coverage page (e.g., "AWS.S3.Bucket", "Azure.Network.NetworkSecurityRule")

  3. An advanced rules should have a resource_type of MULTIPLE even if it involves only one resource type. (Hint: If your rule uses an allow or deny function, it’s a simple rule. If it uses one of the three advanced rule functions, it’s advanced.

This is especially applicable to testing a rule, adding a rule, and modifying a rule.

Example Rules

For examples of simple and advanced custom rules, see our GitHub repo.

Learning Rego

Below are some resources for learning Rego: