Using Firewall Rule in NSX created with PowerCLI

By | 23. March 2020

In this blog I will explain how to create Firewall Rules in NSX with PowerCLI (Policy API).

In my previous blogs I explained how to Tag VMs and create Groups. Now I use this Groups to create the Firewall Rules. I am using a simple 3 Tier APP.

  • All Internal Servers can reach the DNS Servers
  • Any can reach the Web Server via HTTP and HTTPS (80+443)
  • Web Server can reach App Server via HTTP (80)
  • App Server can reach DB Server via MySQL (3306)
  • All other traffic will to the Servers will be rejected

I have created the following Groups before and added the VMs with Tags:

  • Internal (all internal VMs belonging to this 3 Tier App)
  • web
  • app
  • db

I put all the Datas together in one .json file and convert the .json file into a variable to use it later in my script. You can also use a Excel file or a CMDB to get all relevant information.

With the introduction from the Policy API, VMware also included different Categories in the distributed Firewall.

Firewall Categories
NSX Firewall Categories

Inside each category you define the 5 Tuple Firewall Rules with following fields:

  • Rule Name
  • Source Group
  • Destination Group
  • Service (L4) and/or profile (L7 aware)
  • Applied to (If not defined it will be applied to the dfw)
  • Action (Allow, Drop, Reject)

Complete Script

$jsonfile = '[{
                    "category": "Application",
                    "sequence_number": "2",
                    "PolicyName": "DNS",
                    "RuleName": "Internal->DNS",
                    "rule_sequence_number" : "10",
                    "Source": "/infra/domains/default/groups/Internal",
                    "Destination": "ANY",
                    "Services": "/infra/services/DNS-UDP",
                    "profiles": "ANY",
                    "Action": "ALLOW",
                    "disabled": "false"
                },
                {
                  "category": "Application",
                  "sequence_number": "3",
                  "PolicyName": "3-TIER-APP",
                  "RuleName": "Any->WEB",
                  "rule_sequence_number" : "10",
                  "Source": "any",
                  "Destination": "/infra/domains/default/groups/web",
                  "Services": "/infra/services/HTTP,/infra/services/HTTPS",
                  "profiles": "ANY",
                  "Action": "ALLOW",
                  "disabled": "false"
                 },
                 {
                    "category": "Application",
                    "sequence_number": "3",
                    "PolicyName": "3-TIER-APP",
                    "RuleName": "WEB->APP",
                    "rule_sequence_number" : "20",
                    "Source": "/infra/domains/default/groups/web",
                    "Destination": "/infra/domains/default/groups/app",
                    "Services": "/infra/services/HTTP",
                    "profiles": "ANY",
                    "Action": "ALLOW",
                    "disabled": "false"
                   },
                   {
                    "category": "Application",
                    "sequence_number": "3",
                    "PolicyName": "3-TIER-APP",
                    "RuleName": "APP->DB",
                    "rule_sequence_number" : "30",
                    "Source": "/infra/domains/default/groups/app",
                    "Destination": "/infra/domains/default/groups/db",
                    "Services": "/infra/services/MySQL",
                    "profiles": "ANY",
                    "Action": "ALLOW",
                    "disabled": "false"
                    },
                   {
                    "category": "Application",
                    "sequence_number": "3",
                    "PolicyName": "3-TIER-APP",
                    "RuleName": "Deny-Any->Internal",
                    "rule_sequence_number" : "40",
                    "Source": "ANY",
                    "Destination": "/infra/domains/default/groups/Internal",
                    "Services": "ANY",
                    "profiles": "ANY",
                    "Action": "REJECT",
                    "disabled": "false"
                   },
                   {
                    "category": "Application",
                    "sequence_number": "3",
                    "PolicyName": "3-TIER-APP",
                    "RuleName": "Deny-Internal->ANY",
                    "rule_sequence_number" : "50",
                    "Source": "/infra/domains/default/groups/Internal",
                    "Destination": "ANY",
                    "Services": "ANY",
                    "profiles": "ANY",
                    "Action": "REJECT",
                    "disabled": "false"
                   }
                 ]'
$jsonfileobj = ConvertFrom-Json -InputObject $jsonfile
#Create Firewall Rules
foreach ($secitem in $jsonfileobj) {
$secpolicyrulesservices  = @($secitem.Services.split(","))
$secpolicyrulessource_groups = @($secitem.Source.split(","))
$secpolicyrulesdestination_groups = @($secitem.Destination.split(","))
$secpolicysvc = Get-NsxtPolicyService -Name com.vmware.nsx_policy.infra.domains.security_policies
#Create Security Policy
$secdomainspec = $secpolicysvc.Help.patch.domain_id
$secpolicyspec = $secpolicysvc.Help.patch.security_policy.Create()
$secpolicyidspec = $secpolicysvc.Help.patch.security_policy_id.Create()
$secdomainspec = "default"
$secpolicyidspec = $secitem.PolicyName
$secpolicyspec.display_name = $secitem.PolicyName
$secpolicyspec.category = $secitem.category
$secpolicyspec.sequence_number = $secitem.sequence_number
#Create Rules
$secpolicyrules = $secpolicysvc.Help.patch.security_policy.rules.Element.Create()
$secpolicyrules.display_name = $secitem.RuleName
$secpolicyrules.sequence_number = $secitem.rule_sequence_number
$secpolicyrules.source_groups = @($secpolicyrulessource_groups)
$secpolicyrules.destination_groups = @($secpolicyrulesdestination_groups)
$secpolicyrules.services = @($secpolicyrulesservices)
$secpolicyrules.action = $secitem.Action
$secpolicyrules.profiles = @($secitem.profiles)
$secpolicyrules.disabled = $secitem.disabled
$secpolicyrules.logged = "true"
$secpolicyspec.rules.Add($secpolicyrules) | Out-Null
#Run Command
$secpolicysvc.patch($secdomainspec, $secpolicyidspec, $secpolicyspec)
}

Let’s dive into the script:

First we will create the content with a .json file

$jsonfile = '[{
                    "category": "Application",
                    "sequence_number": "2",
                    "PolicyName": "DNS",
                    "RuleName": "Internal->DNS",
                    "rule_sequence_number" : "10",
                    "Source": "/infra/domains/default/groups/Internal",
                    "Destination": "ANY",
                    "Services": "/infra/services/DNS-UDP",
                    "profiles": "ANY",
                    "Action": "ALLOW",
                    "disabled": "false"
                },
                {
                  "category": "Application",
                  "sequence_number": "3",
                  "PolicyName": "3-TIER-APP",
                  "RuleName": "Any->WEB",
                  "rule_sequence_number" : "10",
                  "Source": "any",
                  "Destination": "/infra/domains/default/groups/web",
                  "Services": "/infra/services/HTTP,/infra/services/HTTPS",
                  "profiles": "ANY",
                  "Action": "ALLOW",
                  "disabled": "false"
                 },
....output ommitted

In this .json file we define the needed information for the Firewall. First we need to create a Section and than the Rule inside the Section.

category

Following Options are available (Case Sensitive):

  • Emergency
  • Infrastructure
  • Environment
  • Application

sequence_number

The Sequence Number should be used to place the Section in the right order. Otherwise the Section will be placed randomly.

PolicyName

I will use the Policyname as display Name from the Section and also as the Section ID.

Rulename

Now we create the Rule inside the Section. I use the Rulename as name for the Rule and also as Rule ID.

rule_sequence_number

The rule_sequence_number will be used to bring the Rules in the right order.

Source / Destination

Now we define the Source and Destination Group or Groups.

The Groups can be found under /infra/domains/default/groups/***

..and are Case Sensitive

If you would like to add more than one group you can separate the groups with comma.

example: “/infra/domains/default/groups/web,/infra/domains/default/groups/app”

Services

Services can be found under infra/services/***

…and are Case Sensitive.

If you would like to add more than one Service you can separate the Services with comma.

profiles

In my case should be any. Otherwise you must modify the script.

Action

The Action can be ALLOW, DROP or REJECT

disabled

Disable can be either true or false.  If true, the Rule is disabled, if false the Rule is enabled.

We will now put the json content into a variable.

$jsonfileobj = ConvertFrom-Json -InputObject $jsonfile

Now we will create the loop for each Firewall Section and Firewall Rule

foreach ($secitem in $jsonfileobj) {

With the following command we will bring the content into the right format

$secpolicyrulesservices  = @($secitem.Services.split(","))
$secpolicyrulessource_groups = @($secitem.Source.split(","))
$secpolicyrulesdestination_groups = @($secitem.Destination.split(","))

The following steps will create the Firewall Section. Domain is normally “default” if not changed by NSX Installation.

$secpolicysvc = Get-NsxtPolicyService -Name com.vmware.nsx_policy.infra.domains.security_policies
#Create Security Policy
$secdomainspec = $secpolicysvc.Help.patch.domain_id
$secpolicyspec = $secpolicysvc.Help.patch.security_policy.Create()
$secpolicyidspec = $secpolicysvc.Help.patch.security_policy_id.Create()
$secdomainspec = "default"
$secpolicyidspec = $secitem.PolicyName
$secpolicyspec.display_name = $secitem.PolicyName
$secpolicyspec.category = $secitem.category
$secpolicyspec.sequence_number = $secitem.sequence_number

Now we can create the Firewall Rule inside the Section.

$secpolicyrules.logged  is set to true. This means all Traffic passing this rule will be logged.

#Create Rules
$secpolicyrules = $secpolicysvc.Help.patch.security_policy.rules.Element.Create()
$secpolicyrules.display_name = $secitem.RuleName
$secpolicyrules.sequence_number = $secitem.rule_sequence_number
$secpolicyrules.source_groups = @($secpolicyrulessource_groups)
$secpolicyrules.destination_groups = @($secpolicyrulesdestination_groups)
$secpolicyrules.services = @($secpolicyrulesservices)
$secpolicyrules.action = $secitem.Action
$secpolicyrules.profiles = @($secitem.profiles)
$secpolicyrules.disabled = $secitem.disabled
$secpolicyrules.logged = "true"
$secpolicyspec.rules.Add($secpolicyrules) | Out-Null
$secpolicysvc.patch($secdomainspec, $secpolicyidspec, $secpolicyspec)
}
print
Daniel Stich
Follow me

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.