Config Rules

Jets supports creating AWS Config Rules and associating them with lambda functions. First, make sure you have a app/rules/application_rule.rb:

class ApplicationRule < Jets::Rule::Base
end

The config rule classes you create will look something like this:

class SecurityGroupRule < ApplicationRule
  desc "ensures security groups are hardened"
  scope "AWS::EC2::SecurityGroup"
  def protect
    check = SecurityGroupCheck.new(event, context)
    check.run
  end
end

In app/models, the SecurityGroupCheck class might look something like this:

class SecurityGroupCheck
  APPLICABLE_RESOURCES = ["AWS::EC2::SecurityGroup"]

  def run
    invoking_event = JSON.load(event['invokingEvent'])
    configuration_item = invoking_event['configurationItem']
    rule_parameters = JSON.load(event["ruleParameters"])

    evaluation = evaluate_compliance(configuration_item)

    put_evaluations(
      evaluations: [
        {
          compliance_resource_type: configuration_item['resourceType'],
          compliance_resource_id: configuration_item['resourceId'],
          compliance_type: evaluation['compliance_type'], # required, accepts COMPLIANT, NON_COMPLIANT, NOT_APPLICABLE, INSUFFICIENT_DATA
          annotation: evaluation['annotation'],
          ordering_timestamp: configuration_item['configurationItemCaptureTime'], # required
        },
      ],
      result_token: event['resultToken'], # required
    )
  end
  ...
end

Polymorphic Rules

AWS has provided many starter config rules at awslabs/aws-config-rules. Most of them are written in python. Jets allows you to take these python methods and use them as-is. For example, you could save these python lambda functions in the app/workers/rules/protect_rule folder like so:

app/rules/protect_rule/python/ec2_exposed_instance.py
app/rules/protect_rule/python/iam_mfa.py

Then in your Rule class, you would use Jet’s polymorphic ability:

class ProtectRule < ApplicationRule
  scope "AWS::EC2::Instance"
  python :ec2_exposed_instance

  scope "AWS::IAM::User"
  python :iam_mfa
end

Jets will create Python lambda functions using the files in the app/rules/protect_rule/python folder and associate the Config Rules with these functions. This saves you time from rewriting the python code.

Managed Config Rules

AWS Config Managed Rules are pre-built rules created by AWS. For example, there’s a iam-password-policy rule that you can set up to show that your AWS account follows a strong password policy. AWS supports a ton of Managed Rules. You use AWS managed rules and custom Lambda based rules similarly:

class CheckRule < ApplicationRule
  desc "CIS 4.1 - Ensure no security groups allow ingress from 0.0.0.0/0 to port 22"
  scope "AWS::EC2::SecurityGroup"
  managed_rule :incoming_ssh_disabled
end

Here’s a screenshot example of both types of rules mixed together:

Rule Namespace

The config rule classes above inherited from an ApplicationRule class. You can use the rule_namespace method to remove the namespace from the config rule names like so:

class CheckRule < ApplicationRule
  rule_namespace false
end

So this results in the rules looking something like this check-incoming-ssh-disabled instead of demo-dev-check-incoming-ssh-disabled. The namespace is demo-dev in this case. Note, this will result in you only being able to deploy one instance of the project per JETS_ENV since the rule names would collide.

Or you can set your own namespace:

class CheckRule < ApplicationRule
  rule_namespace "cis"
end

This results in the rules looking something like this cis-check-incoming-ssh-disabled.

Useful Resources