Object Constraint Language
Object Constraint Language (OCL) grants immense flexibility within Guardrails. While complex, there are some key points that can help aid in implementation.
OCL is commonly used to define security group ingress and egress rules. In general, the pseudo-code for security group rule policy evaluation looks something like this:
For all security group rules for all OCL rules evaluate OCL against security group rule. note the resultCombine results from all rule evaluations Take actions based on policy and APPROVE/REJECT status.
OCL is very useful for restricting permissions in policies such as IAM or a KMS Key policy.
OCL incorporates implicit logic. Items separated by a space is implicitly AND
,
while commas indicate OR
. It must be noted that commas are only available in
each condition and cannot be used to join rules. Let's take a look at some
examples.
Policy Examples Using Object Constraint Language (OCL)
Guardrails allows the use of OCL formatting for a variety of policies. Syntax is consistent across all policy types that accept OCL as an input. For more in depth information regarding how OCL rules work, check out our OCL reference page
Below are example policy settings using OCL:
Example 1: AWS > VPC > Security Group > Egress Rules > Approved > Rules (CIDR REJECT)
Starting simple, we can reject the CIDR 0.0.0.0/0. ::/0
is the IPv6 version of
IPv4's zero block, and Guardrails policies allow us to also reject IPv6 addresses.
Note how the comma is used to implicitly say if either 0.0.0.0/0
or ::/0
exists in the rules, it will alarm/ remediate.
### Reject the default route, alwaysREJECT $.turbot.cidr:0.0.0.0/0,"::/0"
### Approve everything elseAPPROVE *
In this next example, not only are we restricting the 0.0.0.0/0
CIDR block,
but also any block that IS NOT 10.0.0.0/8
, 172.16.0.0/12
, 192.168.0.0/16
.
Here we can see the use of the backslash, \
, implicitly denotes a space, which
in turn denotes an AND operator.
### Reject if IP is outside (>) these CIDRsREJECT \ $.turbot.cidr:>10.0.0.0/8 \ $.turbot.cidr:>172.16.0.0/12 \ $.turbot.cidr:>192.168.0.0/16
### Approve anything elseAPPROVE *
Example 2: AWS > VPC > Security Group > Ingress Rules > Approved > Rules (CIDR and PORT REJECT)
Logic can be stacked to create complex rules. In the below case, we are allowing
the use of the default route (0.0.0.0/0
) IF AND ONLY IF using web ports 80
and 443. All other use of the default route will be flagged or removed by the
AWS > VPC > Security Group > Ingress Rules > Approved
control, depending on
the configuration. Any route outside of the above restriction is allowed.
### Approve use of default route if using web portsAPPROVE \ $.turbot.ports.+:80,443 \ $.turbot.cidr:0.0.0.0/0,"::/0"
### Reject all other use of default routeREJECT $.turbot.cidr:0.0.0.0/0,"::/0"
### Approve anything elseAPPROVE *
Again, we can stack the logic to reject specific ports (21 or 25), a specific port range (anything greater than 4, or -1. -1 is defined as the entire port range), and only allow the use of specific CIDR ranges.
### Reject if using port 25 or 21REJECT $.turbot.ports.+:25,21
### Reject if port range is > 4 or == -1REJECT $.turbot.portRangeSize:>4,-1
### Reject if IP is outside (>) these cidrsREJECT \ $.turbot.cidr:>10.0.0.0/8 \ $.turbot.cidr:>172.168.0.0/16 \ $.turbot.cidr:>192.168.0.0/24
### Approve anything elseAPPROVE *
Example 3: AWS > VPC > Security Group > Ingress Rules > Approved > Rules (PORT REJECT over Range)
In this example, let's suppose we want to block a specific port (say, port 22
)
but at the same time, allow large ranges of ports. The key caveat is that the
blocked port cannot be a part of the range. We saw in the above example that
we can block a port using the OCL:
REJECT $.turbot.ports.+:25APPROVE *
However, we can circumvent this restriction by defining a range that does not specifically mention port 25. To account for this possibility, we can tweak the rule:
REJECT $.turbot.fromPort:<=25 $.turbot.toPort:>=25APPROVE *
This rule can be stacked in a similar manner to example 2 to restrict a number
of ports, as well as restricting by CIDR (REJECT $.turbot.cidr:0.0.0.0
),
IpProtocol (REJECT $.IpProtocol:icmp
), or any other metadata about the
security group stored in the Guardrails CMDB.
Example 4: AWS > KMS > Key > Policy Statements > Approved > Rules (REJECT Permission Sets)
We can alarm on or remove any not approved rule using OCL. Often, KMS Key
policies will have multiple entries, such as a user with access to kms:Encrypt,
kms:Decrypt, and kms:DescribeKey. Using pipes (|
), we can concatenate the
rules to be easier to create and read.
In this example, we are going to reject any action that is NOT kms:Encrypt, kms:Decrypt, or kms:DescribeKey.
Example without pipes:
REJECT !$.Action:kms:Encrypt
REJECT !$.Action:kms:Decrypt
REJECT !$.Action:kms:DescribeKey
APPROVE *
Example with pipes:
REJECT !$.Action:/^kms:(Encrypt|Decrypt|DescribeKey)$/
APPROVE *
Much easier to read and write! Note the use of the ^
and $/
at the end of
the REJECT statement.
When setting this KMS rules policy, do not forget to also set AWS > KMS > Key > Policy Statements > Approved!
Example 5: AWS > IAM > Role > Policy Attachments > Approved > Rules (REJECT Policies)
OCL logic can also be applied to IAM roles and users. Often, organizations will
want to restrict the usage of the full access AdministratorAccess
policy in
AWS IAM. This below example will trigger an alarm (and possible remediation) if
the AdministratorAccess
policy is attached to any role.
### Reject administrator access policy attachments to an IAM roleREJECT $.PolicyName:/^.+FullAccess.*$/REJECT $.PolicyName:AdministratorAccess
### Approve anything elseAPPROVE *