Using vRA for NSX-V Firewall



Hey All, back again. This time with some NSX-V Automation. NSX I have seen has really kicked off in the industry. In a world of Zero-Trust networks, NSX-V provides an ideal platform to create per-app micro-segmentation using the Distributed Firewall feature which deploys a Firewall on the ESXi Layer and then puts a per vNIC Firewall on every Virtual Machine. And then using Tags as a means of permitting access to and from resources.

Although this is a great tool, getting traditional FW admins to transition to the new Software Defined Networking can be a bit daughting, hence today we are going to look at how we can use vRA to automate the creating of Firewall Rules in NSX and assocate them to Tags which can be then assigned to VMs.

In this post we will look at the below:

  • Using RESTHTTP to create NSX Tag
  • Create NSX Group
  • Create either an inbound or outbound FW rule to the destination (or source) IP and Port to (or from) the NSX Tagged device.

As we are using HTTP-REST API to interface with the NSX Manager we will start off by creating a REST Host inside VRO connecting to the NSX Manager.

We are now going to create the NSX Tag. To create a Tag we run the below REST API Call:

POST /2.0/services/securitytags/tag

With the body:

<securityTag>
  <objectTypeName>SecurityTag</objectTypeName>
  <name>ST_TagName</name>
  <description> 
</description>
  <type>
    <typeName>SecuirtyTag</typeName>
  </type>
</securityTag>

In vRO we will do this with the below:

var tagDescription = new XML('<tagDescription />');
tagDescription.email = AppOwnerEmail
tagDescription.AppDescription = Description
// Add NAT Rule on Edge
System.debug(tagDescription.toString())
var xmlbody = new XML('<securityTag />');
xmlbody.objectTypeName = 'SecurityTag';
xmlbody.name = 'ST_'+AppName;
xmlbody.description = tagDescription.toString()
// Configure NAT Rule XML
var type = new XML('<type />');
xmlbody.type.typeName = 'SecuirtyTag';

System.debug("XML: " + xmlbody.toString());
var request = nsxManagerRestHost.createRequest("POST", "/2.0/services/securitytags/tag",xmlbody.toString());
request.contentType = "application/xml";
System.debug("Adding SecurityTag " + AppName);

// Execute the query
System.debug("Request URL: " + request.fullUrl);
var response = request.execute();

// Evaluate the Response 
System.debug("Response is " + response.statusCode);
if (response.statusCode == 201) {
    System.debug("Security Tag " + AppName + " successfully added on NSX ");
}
else {
    throw("Failed to add Secuirty Tag " + AppName);
}

// return Rule id (it's the last element in a returned URI in the "Location" http header
scriptAction = response.contentAsString;
System.debug("Created Firewall Rule ID is "  + scriptAction);

Next the NSX Security Group. To create the Security Group the REST API Call is:

POST /2.0/services/securitygroup/bulk/globalroot-0

With the body:

<securitygroup>
  <revision>2</revision>
  <name>SecurtiyGroupName</name>
  <description> SecurtiyGroupDescription</description>
  <dynamicMemberDefinition>
    <dynamicSet>
      <operator>OR</operator>
      <dynamicCriteria>
        <operator>OR</operator>
        <key>VM.SECURITY_TAG</key>
        <criteria>contains</criteria>
        <value>SecrutiyTagName</value>
      </dynamicCriteria>
    </dynamicSet>
  </dynamicMemberDefinition>

The vRO Script is

// Add NAT Rule on Edge

var xmlbody = new XML('<securitygroup />');
xmlbody.revision = '2';
xmlbody.name = 'SG_'+AppName;
xmlbody.description = AppName
// Configure NAT Rule XML
var dynamicMemberDefinition = new XML('<dynamicMemberDefinition />');
xmlbody.dynamicMemberDefinition.dynamicSet = new XML('<dynamicSet />');

xmlbody.dynamicMemberDefinition.dynamicSet.operator = 'OR';
xmlbody.dynamicMemberDefinition.dynamicSet.dynamicCriteria = new XML ('<dynamicCriteria />');
xmlbody.dynamicMemberDefinition.dynamicSet.dynamicCriteria.operator = 'OR';
xmlbody.dynamicMemberDefinition.dynamicSet.dynamicCriteria.key = 'VM.SECURITY_TAG';
xmlbody.dynamicMemberDefinition.dynamicSet.dynamicCriteria.criteria = 'contains';
xmlbody.dynamicMemberDefinition.dynamicSet.dynamicCriteria.value = AppName;
                
// Prepare the configuration Update REST query
System.debug("XML: " + xmlbody.toString());
var request = nsxManagerRestHost.createRequest("POST", "/2.0/services/securitygroup/bulk/globalroot-0",xmlbody.toString());
request.contentType = "application/xml";
System.debug("Adding SecurityGroup " + AppName);

// Execute the query
System.debug("Request URL: " + request.fullUrl);
var response = request.execute();

// Evaluate the Response 
System.debug("Response is " + response.statusCode);
if (response.statusCode == 201) {
                System.debug("SecurityGroup " + AppName + " successfully added on NSX ");
}
else {
                throw("Failed to add Secuirty Group " + AppName);
}

scriptAction = response.contentAsString;;
System.debug("Created Firewall Rule ID is "  + scriptAction);

What this will create is a Security Group with a filter for the Security Tag.

Now to create the Firewall Rule itself, for this we need to get some details. There are two details we need to get for us to provision a Firewall. First is the ScopeId, the ScopeId defines which section in the Distributed Firewall Rule list we will use. The Second is an Etag which is an identifier for the specific version of a resource, (In our case the Etag of the SectionId) which can change every time a policy push occurs.

To find the SectionId (aka ScopeId) we need to download the whole ruleset as an xml and, then find a rule under the section you want and it will list it’s sectionId. To get the whole ruleset we will run the below:

GET /api/4.0/firewall/globalroot-0/config

Depending on how may FirewallRules you already have this may return a large XML Config file (in my case it was 1.2MB File), open this up in your favorite XML Decoder software, and find a rule under the section you wish to use.

In my case it is section 1073. We will now retrive the ETag for this Section, for this we query the Distributed FW for that SectionId and get the Etag, for this we will run the below Get Command:

GET /4 .0/firewall/globalroot-0/config/layer3sections/<SectionId>

To do this in vRA we will run the below script:

// Add NAT Rule on Edge

// Prepare the configuration Update REST query
//System.debug("XML: " + xmlbody.toString());
var request = nsxManagerRestHost.createRequest("GET", "4.0/firewall/globalroot-0/config/layer3sections/"+ScopeID);
request.contentType = "application/xml";
// Execute the query
System.debug("Request URL: " + request.fullUrl);
var response = request.execute();

// Evaluate the Response 
System.debug("Response is " + response.statusCode);
if (response.statusCode == 200) {
    System.debug("Policy was found ");
}
else {
    throw("Failed to Policies ");
}

var headers = response.getAllHeaders();
System.debug(response.contentAsString );
System.debug("Etag Value = "  + headers.get("ETag"));
resultActions = response.contentAsString;
etag = headers.get("ETag")

At the end of this we will get the objects Etag which is a header value. We will keep this and it will become a Header in the FW Rule creation Script we run next. For this we will execute the POST command:

POST / 4.0/firewall/globalroot-0/config/layer3sections/<SectionId>/rules

Header:

If-Match = <eTag>

Body:

<rule disable="false" logged="true">
  <name><FirewallName></name>
  <action>Allow</action>
  <notes><Description></notes>
  <appliedToList>
    <appliedTo>
      <value><SecuritygroupId></value>
      <type>SecurityGroup</type>
      <isValid>true</isValid>
    </appliedTo>
  </appliedToList>
  <sources excluded="false">
    <source>
      <value>s<SecuritygroupId></value>
      <type>SecurityGroup</type>
      <isValid>true</isValid>
    </source>
  </sources>
  <destinations excluded="false">
    <destination>
      <value>50.20.100.12</value>
      <type>Ipv4Address</type>
      <isValid>true</isValid>
    </destination>
  </destinations>
  <services>
    <service>
      <destinationPort>443</destinationPort>
      <protocolName>TCP</protocolName>
      <isValid>true</isValid>
    </service>
  </services>
  <direction>inout</direction>
  <packetType>any</packetType>
</rule>

To do this in vRO we will run the below code:

// Add NAT Rule on Edge

var xmlbody = new XML('<rule disable="false" logged="true" />');

xmlbody.name = 'FW_Rule_'+AppName;
xmlbody.action = 'Allow'
xmlbody.notes = 'TSM: ' + TsmTicketNumber + " , Implementer: " + RequestorEmail
// Configure NAT Rule XML
//appliedToList = new XML('<appliedToList />');
//xmlbody.appliedToList.appliedTo = new XML('<appliedTo />');

xmlbody.appliedToList.appliedTo.value = SG_GroupID;
xmlbody.appliedToList.appliedTo.type = 'SecurityGroup';
xmlbody.appliedToList.appliedTo.isValid = 'true';
xmlbody.destinations = new XML('<destinations excluded="false" />');
xmlbody.destinations.destination.value = SG_GroupID;
xmlbody.destinations.destination.type = 'SecurityGroup';
xmlbody.destinations.destination.isValid = 'true';
xmlbody.dynamicMemberDefinition.dynamicSe
var sources = new XML('<sources excluded="false" />');
var source = new XML('<source></source>');
for each (ip in SourceIPs){
source.value=ip;
source.type = 'Ipv4Address';
source.isValid = 'true';
sources.appendChild(source.toString());}
xmlbody.appendChild(sources.toString());

var services = new XML('<services />');
var service = new XML('<service></service>');
for each (port in ports){
service.destinationPort = port;
service.protocolName = Protocol;
service.isValid = 'true';
services.appendChild(service.toString());}
xmlbody.appendChild(services.toString());
xmlbody.direction='inout';
xmlbody.packetType='any';

// Prepare the configuration Update REST query
System.debug("XML: " + xmlbody.toString());

var request = nsxManagerRestHost.createRequest("POST", "4.0/firewall/globalroot-0/config/layer3sections/"+ScopeID+"/rules",xmlbody.toString());
request.contentType = "application/xml";
request.setHeader("If-Match",etag);
System.debug("Adding SecurityGroup " + AppName);

// Execute the query
System.debug("Request URL: " + request.fullUrl);
var response = request.execute();

// Evaluate the Response 
System.debug("Response is " + response.statusCode);
if (response.statusCode == 201) {
    System.debug("SecurityGroup " + AppName + " successfully added on NSX ");
}
else {
    throw("Failed to add Secuirty Group " + AppName);
}

// return Rule id (it's the last element in a reteurned URI in the "Location" http header
var ruleID = response.contentAsString;
System.debug("Created Firewall Rule ID is "  + ruleID);

Putting all of this together and publishing it via vRA we get a nice easy form which will create all the objects and all is required is to tag a VM to get the Firewall:

And the end result of running it is a Firewall Rule nice and easy.

I Hope you enjoyed this blog and look forward to your comments below.