--- title: "Open Policy Agent" weight: 21 --- The Open Policy Agent (opa) command line comes with a test subcommand for testing Rego policies. The following steps describe how to install **opa**. </br> We strongly suggest you checkout: * [Running OPA](https://www.openpolicyagent.org/docs/latest/#running-opa) ### Install OPA CLI The full instructions can be reviewed [here](https://www.openpolicyagent.org/docs/latest/#running-opa) ### Mac ```bash curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_darwin_amd64 ``` ### Linux ```bash curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64 ``` ### Windows ```bash https://openpolicyagent.org/downloads/latest/opa_windows_amd64.exe ``` ### Verify OPA cli installation Check to see that you can run the cli for opa on your terminal. ``` $ opa An open source project to policy-enable your service. Usage: opa [command] Available Commands: bench Benchmark a Rego query build Build an OPA bundle check Check Rego source files deps Analyze Rego query dependencies eval Evaluate a Rego query fmt Format Rego source files help Help about any command parse Parse Rego source file run Start OPA in interactive or server mode sign Generate an OPA bundle signature test Execute Rego test cases version Print the version of OPA Flags: -h, --help help for opa Use "opa [command] --help" for more information about a command. ``` ### Creating Rego Policy Unit Tests Once opa is installed create a project directory called **securitygroup** as described below. ```markdown mkdir securitygroup cd securitygroup ``` You'll be creating two files. One will be the Rego policy for validating your CF template. The other is the unit test for your Rego Policy. Let's start with the unit test file (in keeping with [Test Driven Development](https://en.wikipedia.org/wiki/Test-driven_development)) Create a file named **securitygroup_tests.rego** with contents show below ``` package securitygroup.allow_secured_ports_only test_allow_secured_ports_only { not allow with input as { "Resources": { "CounterLBSecurityGroup63C1AB9D": { "Type": "AWS::EC2::SecurityGroup", "Properties": { "GroupDescription": "Automatically created Security Group for ELB fooCounterLBAEF24CE8", "SecurityGroupIngress": [{ "CidrIp": "0.0.0.0/0", "Description": "Allow from anyone on port 80", "FromPort": 80, "IpProtocol": "tcp", "ToPort": 80 }], "VpcId": { "Ref": "Vpc8378EB38" } }, "Metadata": { "aws:cdk:path": "foo/Counter/LB/SecurityGroup/Resource" } } } }} ``` Let's also create the rules file. The unit test is set to fail if the rules allow the snippet of cloudformation to pass with no issues. Our initial rules file always allow the cloudformation to pass. Let's create that file and discuss this a bit more. ``` package securitygroup.allow_secured_ports_only resource_type = "AWS::EC2::SecurityGroup" default allow = true ``` Next lets run our first test. ``` opa test . -v FAILURES -------------------------------------------------------------------------------- data.securitygroup.allow_secured_ports_only.test_allow_secured_ports_only: FAIL (264.006µs) query:1 Enter data.securitygroup.allow_secured_ports_only.test_allow_secured_ports_only = _ securitygroup_tests.rego:3 | Enter data.securitygroup.allow_secured_ports_only.test_allow_secured_ports_only securitygroup_tests.rego:4 | | Fail not data.securitygroup.allow_secured_ports_only.allow with input as {"Resources": {"CounterLBSecurityGroup63C1AB9D": {"Metadata": {"aws:cdk:path": "foo/Counter/LB/SecurityGroup/Resource"}, "Properties": {"GroupDescription": "Automatically created Security Group for ELB fooCounterLBAEF24CE8", "SecurityGroupIngress": [{"CidrIp": "0.0.0.0/0", "Description": "Allow from anyone on port 80", "FromPort": 80, "IpProtocol": "tcp", "ToPort": 80}], "VpcId": {"Ref": "Vpc8378EB38"}}, "Type": "AWS::EC2::SecurityGroup"}}} query:1 | Fail data.securitygroup.allow_secured_ports_only.test_allow_secured_ports_only = _ SUMMARY -------------------------------------------------------------------------------- data.securitygroup.allow_secured_ports_only.test_allow_secured_ports_only: FAIL (264.006µs) -------------------------------------------------------------------------------- FAIL: 1/1 ``` As expected our test fails because we are allowing the unit test snippet of cloudformation to pass. Because we use the **not allow with input as...** in our unit test we expect the cloudformation to be in violation of the policy we've written. We now need to craft a set of rules that successfully flags a violation of our policy. Let's do that by adding a set of rules. Let's modify the contents of our rules file, **securitygroup.rego** with the following: ``` package securitygroup.allow_secured_ports_only resource_type = "AWS::EC2::SecurityGroup" default allow = true allow = false { count(deny_non_secure_ports) > 0 } deny_non_secure_ports[resource] { some resource, j input.Resources[resource].Type == resource_type input.Resources[resource].Properties.SecurityGroupIngress[j].FromPort != 443 }{ some resource, j input.Resources[resource].Type == resource_type input.Resources[resource].Properties.SecurityGroupIngress[j].ToPort != 443 } ``` Once you have updated your **securitygroup_tests.rego** let's re-run our test. ``` opa test . -v data.securitygroup.allow_secured_ports_only.test_allow_secured_ports_only: PASS (445.252µs) -------------------------------------------------------------------------------- PASS: 1/1 ``` We have written our first unit test, watched it fail. We then wrote a set of rules to pass our test. At this point we should look at making this more robust.