Writing Rules

Note

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

Our open source tool Fregot allows you to easily evaluate, debug, and test custom rules (and more). For instructions, see Testing Custom Rules with Fregot.

Fugue’s custom rules feature allows you to add user-defined rules through the Rules page (see UI instructions), CLI, or API. Fugue evaluates your infrastructure against the rules and displays results for your custom rules as it would for Fugue-defined rules. The visualizer also denotes noncompliance with custom rules.

You can use custom rules to enforce enterprise policies.

You will need to explicitly enable families that contain relevant custom rules on an environment, for those custom rules to be evaluated on a given environment. To change the selected compliance families, see the FAQ.

Tip

What are Custom Rules?

A rule checks cloud infrastructure and/or infrastructure as code (IaC) configurations to determine whether a resource, region, or account complies with a specific set of configuration conditions. For example, the rule “VPC flow logging should be enabled” evaluates an Amazon VPC’s flow log configuration. To learn more about rules, see Compliance Concepts.

A custom rule is a user-defined rule.

Users can write custom rules with Rego, an open source policy-as-code language for Open Policy Agent (OPA). Rego is a query language. In Rego, a 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:

  • A simple rule tests a single resource type. See Simple Custom Rules. This example (without metadata) checks whether an Amazon S3 bucket has stage:prod tags:

package rules.bucket_tags

input_type = "tf"

resource_type = "aws_s3_bucket"

default allow = false

allow {
  input.tags.stage == "prod"
}
  • An advanced rule tests one or more resource types. See Advanced Custom Rules. This example (without metadata) checks whether Amazon S3 buckets containing CloudTrail logs have a private ACL:

package rules.trailbucketlogs
import data.fugue

input_type = "tf"

resource_type = "MULTIPLE"

trails = fugue.resources("aws_cloudtrail")
buckets = fugue.resources("aws_s3_bucket")

trail_bucket_ids[bucket] {
  bucket = trails[_].s3_bucket_name
}

policy[r] {
  bucket = buckets[_]
  trail_bucket_ids[bucket.bucket]
  bucket.acl == "private"
  r = fugue.allow_resource(bucket)
} {
  bucket = buckets[_]
  trail_bucket_ids[bucket.bucket]
  not bucket.acl == "private"
  r = fugue.deny_resource(bucket)
}

Steps for writing a rule

The basic process for writing a rule is as follows:

  1. Determine the environment provider(s) the rule should apply to

  2. Determine the resource type(s)

  3. Determine the input type

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

  5. Determine whether to write a simple or advanced rule

  6. Define pass/fail conditions for the resource

  7. Write metadata

See Simple Custom Rules and Advanced Custom Rules for step-by-step instructions for each rule type.

When to use simple vs. advanced rules

Simple rules evaluate every resource of a single resource type, and only in the context of that single resource – not the context of an environment. For instance, a simple rule to check whether Amazon S3 buckets have stage:prod tags will return a PASS or FAIL for every single S3 bucket in an environment (see our example rule above). It cannot reference other resource types to determine compliance (and in this case, there’s no need).

Examples of simple rules:

Advanced rules are more powerful and expressive. They evaluate resources in the full context of an environment, so it’s possible to evaluate multiple resource types at once and see if required resources are missing. Use cases:

  • Evaluate multiple resource types in a single rule (see example rule above)

  • Filter out nonapplicable resources of a given type (e.g., only evaluate EC2 instances with stage:prod tags, and ignore the rest) (example rule)

  • Determine if a required resource type is missing (e.g., if an environment doesn’t have an AWS password policy resource, create a FAIL rule result) (example rule)

Examples of rules that must be advanced:

Simple rules are typically easier to write. It’s a good rule of thumb to try writing a rule as a simple rule first, and if it’s not possible, make it an advanced rule. Additionally, any simple rule can also be expressed as an advanced rule.

Other rule parameters

After writing the basic rule logic, you’ll need to add additional information, either through the UI/CLI/API, metadata, or the code itself. When you’re done with the rule, it will contain all of the following elements:

  • Providers

    • Runtime:

      • AWS (AWS commercial)

      • AWS_GOVCLOUD

      • AZURE (Azure and Azure Government)

      • GOOGLE

    • Infrastructure as code (IaC):

      • REPOSITORY

  • Input type, for the REPOSITORY provider only:

    • tf (Terraform HCL)

    • tf_plan (Terraform JSON plans)

    • cfn (CloudFormation templates)

    • k8s (Kubernetes YAML manifests)

  • Resource types

For more detailed information about adding these rule parameters, see Custom Rules Reference. You’ll also find step-by-step instructions in Simple Custom Rules and Advanced Custom Rules.

Complete rules

Here’s an example of a complete simple rule for Google runtime environments, with metadata. It checks whether storage buckets have stage:prod labels:

package rules.storage_bucket_labels

__rego__metadoc__ := {
  "title": "Google storage buckets must be labeled stage:prod",
  "description": "Google storage buckets are required to have stage:prod labels",
  "custom": {
    "severity": "Medium",
    "providers": ["GOOGLE"]
  }
}

resource_type = "Google.Storage.Bucket"

default allow = false

allow {
  input.labels.stage == "prod"
}

Here’s an example of a complete advanced rule for Google runtime environments, with metadata. It checks whether a project has a default audit log configuration:

package rules.google_default_audit_log_config
import data.fugue

__rego__metadoc__ := {
  "title": "Google projects should have a default audit log config",
  "description": "All Google projects are required to have a default audit log configuration",
  "custom": {
    "providers": ["GOOGLE"],
    "severity": "Medium"
  }
}

input_type = "tf"

resource_type = "MULTIPLE"

configs = fugue.resources("Google.ResourceManager.ProjectIAMAuditConfig")

policy[r] {
  config = configs[_]
  r = fugue.allow_resource(config)
} {
  count(configs) == 0
  r = fugue.missing_resource("Google.ResourceManager.ProjectIAMAuditConfig")
}

You’ll find more examples of custom rules in our GitHub repo.

What’s Next?

Continue onward to learn about simple rules or advanced rules, or jump into the Custom Rules Reference.