Exporting the NSX-T DFW configuration via the Policy API

Some times it’s useful to export the entire distributed firewall configuration. It could be used as an offline backup before applying configuration changes, provided to personnel who does not have direct access to the NSX manager for auditing or information, or leveraged as a starting point for a migration to a new environment. Unfortunately, NSX-T does not have an out of the box export/import functionality for the distributed firewall configuration such as the one available in NSXv.

That said, we can use the Policy API to retrieve the entire firewall configuration and store it in a single JSON file that can be later leveraged to restore the configuration on the same NSX installation or a different one.

The starting point of the process is the NSX hierarchical API. It natively stores the entire NSX configuration in a structured data model. Each configuration object is part of a tree whose parent-child relationships represent actual object dependencies. The Policy API provides the ability to filter certain types of objects via regular expressions. We can leverage this functionality to retrieve only the objects related to the distributed firewall configuration.

curl -k --user admin --request GET 'https://nsxmgr-01a/policy/api/v1/infra?filter=Type-Domain%7CGroup%7CSecurityPolicy%7CRule%7CPolicyContextProfile%7CService' > dfw_config.json

The filter in the example collects all the objects of the following types:

  • Domain
  • Group
  • Security Policy
  • Rule
  • PolicyContextProfile
  • Service

The JSON retrieved from the API call includes the entire distributed firewall configuration and can be regarded as an accurate snapshot of the current state of the environment. The problems arise when we try to use this JSON file to restore the configuration, maybe after a change that went wrong, or to a different SDDC. This is what happens if you try:

curl -k --user admin -H 'Content-Type: application/json' --request PATCH 'https://nsxmgr-01a/policy/api/v1/infra' --data '@./dfw_config.json'

{"module_name":"common-service","error_message":"Cannot connect to server","error_code":98}

The reason we receive an error is that the dfw_config.json file contains both user-defined and system owned objects. In this case, system owned objects are all the default services (SSH, HTTP, etc.) and context profile (SMB, SSL, NTP, etc.) that are part of the NSX configuration. At the moment (NSX-T version 3.0), a PATCH against system owned objects is not supported. To restore our setup, we have to remove all the system owned objects from the JSON file. This begs two questions:

  • How do I identify system owned objects in the JSON data structure?
  • How do I actually delete them? They are thousands.

How do I identify system owned objects in the JSON data structure?

Let’s take a look at system owned objects. In the snippet below, you can see the structure of the VNC Policy Context Profile. Policy Context Profiles are direct children of the root Infra object. Following the Hierarchical API data structure, the parent-child relationship is not direct. It is established via a wrapper object with “resource_type” Child<resource type of the child object>, in this case, ChildPolicyContextProfile. The actual configuration of the Policy Context Profile is stored in the property “PolicyContextProfile” of the wrapper object. The property “_system_owned” in the “PolyContextProfile” object is what we are looking for. It is set to true for any system owned object, and it allows us to identify all the objects that we cannot include in the PATCH API call to restore the configuration.

{
"resource_type" : "Infra",
"id" : "infra",
"display_name" : "infra",
"children" : [ {
  "resource_type" : "ChildPolicyContextProfile",
  "PolicyContextProfile" : {
    "resource_type" : "PolicyContextProfile",
    "id" : "VNC",
    "attributes" : [ {
      "key" : "APP_ID",
      "value" : [ "VNC" ],
      "datatype" : "STRING"
    } ],
    "display_name" : "VNC",
    "description" : "Traffic for Virtual Network Computing.",
    "marked_for_delete" : false,
    "overridden" : false,
    "_create_user" : "system",
    "_create_time" : 1585938474615,
    "_last_modified_user" : "system",
    "_last_modified_time" : 1585938474615,
    "_system_owned" : true,
    "_protection" : "NOT_PROTECTED",
    "_revision" : 0
  },
  "marked_for_delete" : false,
  "mark_for_override" : false,
  "_protection" : "NOT_PROTECTED"
}

The Service object has a similar structure, you can see an example in the snippet below. Service objects are direct children of the root Infra object, and they are wrapped in an object of “resource_type” ChildService. The actual configuration of the service is store in the “Service” property of the wrapper object. The Service object has a “_system_owned” property we can utilize to identify the system created services.

{
"resource_type" : "Infra",
"id" : "infra",
"display_name" : "infra",
"children" : [ {
  "resource_type" : "ChildService",
  "Service" : {
    "resource_type" : "Service",
    "is_default" : true,
    "service_entries" : [ {
      "l4_protocol" : "TCP",
      "source_ports" : [ ],
      "destination_ports" : [ "4363" ],
      "resource_type" : "L4PortSetServiceEntry",
      "id" : "SAP_IPC_Dispatcher_Mobile_client_2",
      "display_name" : "SAP IPC Dispatcher Mobile client 2",
      "marked_for_delete" : false,
      "overridden" : false,
      "_create_user" : "system",
      "_create_time" : 1585938461914,
      "_last_modified_user" : "system",
      "_last_modified_time" : 1585938461915,
      "_system_owned" : true,
      "_protection" : "NOT_PROTECTED",
      "_revision" : 0
    } ],
    "service_type" : "NON_ETHER",
    "id" : "SAP_IPC_Dispatcher_Mobile_client_2",
    "display_name" : "SAP IPC Dispatcher Mobile client 2",
    "description" : "SAP IPC Dispatcher Mobile client 2",
    "unique_id" : "a4220281-9e70-4927-b3e7-64f5ab18ef06",
    "children" : [ {
      "ServiceEntry" : {
        "l4_protocol" : "TCP",
        "source_ports" : [ ],
        "destination_ports" : [ "4363" ],
        "children" : [ ],
        "marked_for_delete" : false,
        "overridden" : false,
        "_protection" : "NOT_PROTECTED"
      },
      "resource_type" : "ChildServiceEntry",
      "marked_for_delete" : false,
      "mark_for_override" : false,
      "_protection" : "NOT_PROTECTED"
    } ],
    "marked_for_delete" : false,
    "overridden" : false,
    "_create_user" : "system",
    "_create_time" : 1585938461910,
    "_last_modified_user" : "system",
    "_last_modified_time" : 1585938461911,
    "_system_owned" : true,
    "_protection" : "NOT_PROTECTED",
    "_revision" : 0
  },
  "marked_for_delete" : false,
  "mark_for_override" : false,
  "_protection" : "NOT_PROTECTED"
  }

One peculiarity of the service object is the presence of a redundant children property that includes information about the service entries (protocols and ports that define the service). I say it’s unnecessary because the very same information is present in the “service_entries” property. Besides that, the objects in the “children” array do not contain a “resource_type” property causing an error if they are included in the restore PATCH API call. This behavior will be rectified in a future release, but for now, we need to delete the “children” property from all the service objects we want to include in the restore PATCH API call.

How do I actually delete them? They are thousands.

Now we get to the fun, or not too fun, part. How do I delete all the system owned object, and also the “Children” property from any user-defined service? You can do it manually, and it is not going to be the best use of your time, or you can do it programmatically. I tried to google my way to a script that scrub the JSON for me, and the result is here.

After you download it, edit your NSX credentials and FQDN.

import json
import requests
#prepare the http connection to NSX Manager
session = requests.Session()
session.verify = False
session.auth = ('admin', 'VMware1!VMware1!')
nsx_mgr = 'https://nsxmgr-01a.corp.local'

After that you can simply run it on host with python installed via the command:

python nsx_dfw_export.py

The script will create a file named dfw.json in the very same folder. You can use it directly in a PATCH API call to restore or migrate the configuration, like in the example below:

curl -k --user admin -H 'Content-Type: application/json' --request PATCH 'https://nsxmgr-01a/policy/api/v1/infra' --data '@./dfw.json'

The restore works on a different NSX manager too:

curl -k --user admin -H 'Content-Type: application/json' --request PATCH 'https://nsxmgr-01b/policy/api/v1/infra' --data '@./dfw.json'

Looks like an easy way to keep your security configuration consistent across different NSX installations! Run it as a cronjob every night, and you do not have any more configuration drifts between sites.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s