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
To waive a rule in a specific environment, see Rule Waivers.
To disable or enable a rule in all environments, see Enabling and Disabling Rules.
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:
Determine the environment provider(s) the rule should apply to
Determine the resource type(s)
Determine the input type
Determine the resource attribute(s) you want Fugue to check
Determine whether to write a simple or advanced rule
Define pass/fail conditions for the resource
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:
Amazon S3 buckets containing CloudTrail logs must be private
Why it’s advanced: It involves two resource types
-
Why it’s advanced: It involves two resource types
Google projects should have a default audit log config
Why it’s advanced: It checks whether a required resource is missing
Kubernetes job containers in the “prod” namespace must not be named “test”
Why it’s advanced: It filters out jobs that are not in the “prod” namespace
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:
-
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)
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.