This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

NSX

Performing compliance automation tasks for NSX.

1 - Audit NSX 4.x

Auditing NSX 4.x for STIG Compliance

Overview

Auditing NSX 4.x for STIG compliance involves scanning the NSX Managers, DFW, and any gateways deployed.

To audit NSX using InSpec we utilize the local transport to connect via the REST API and query it’s configuration.

Prerequisites

Versions listed below were used for this documentation. Other versions of these tools may work as well but if issues are found it is recommended to try the versions listed here.

  • InSpec/CINC Auditor 6.6.0
  • SAF CLI 1.4.0
  • STIG Viewer 2.17
  • An NSX 4.x environment. The environment used in these examples has 1 T0 Gateway configured with BGP to an upstream router and 1 T1 Gateway deployed.

Assumptions

  • Commands are being ran from a Linux machine. Windows will also work but paths and commands may need to be adjusted from the examples.
  • The DOD Compliance and Automation repository downloaded and extracted to /usr/share/stigs.
  • CINC Auditor is used in lieu of InSpec. If InSpec is used replace cinc-auditor with inspec when running commands.

Auditing NSX

Generate API Session Token

This profile uses Session-Based authentication to authenticate with NSX for auditing. A session token and cookie must be generated and provided an input for the profile. This can be generated in various ways via curl, tools like Postman, etc. For more information see the NSX API Documentation.

Note: If the user is a remote user, append “@domain” to the username, for example, “joe@example.com”. The domain must match a domain for a configured VIDM identity source or a configured LDAP identity source.

Curl example:

curl -k -i -X POST -d 'j_username=admin&j_password=C3.UwJ7TTK1P' https://10.215.77.149/api/session/create

# Example response
HTTP/1.1 200 OK
set-cookie: JSESSIONID=A6903A10F3AE7EB328F12EAF796053F5; Path=/; Secure; HttpOnly; SameSite=Lax
x-xsrf-token: ead781b8-0e0c-456f-a04a-584e9ae2e45a
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
content-type: application/json
content-length: 107
date: Thu, 29 Jun 2023 21:39:58 GMT
strict-transport-security: max-age=31536000; includeSubDomains
content-security-policy: frame-src 'self' blob:; frame-ancestors 'self'
server: envoy

{"roles":[{"role":"superusers","permissions":["read-api","read-write-api","read-cli","read-write-cli"]}]}
curl -k -i -X POST -d 'j_username=admin&j_password=C3.UwJ7TTK1P' https://10.215.77.149/api/session/create

# Example response
HTTP/1.1 200 OK
set-cookie: JSESSIONID=A6903A10F3AE7EB328F12EAF796053F5; Path=/; Secure; HttpOnly; SameSite=Lax
x-xsrf-token: ead781b8-0e0c-456f-a04a-584e9ae2e45a
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
content-type: application/json
content-length: 107
date: Thu, 29 Jun 2023 21:39:58 GMT
strict-transport-security: max-age=31536000; includeSubDomains
content-security-policy: frame-src 'self' blob:; frame-ancestors 'self'
server: envoy

{"roles":[{"role":"superusers","permissions":["read-api","read-write-api","read-cli","read-write-cli"]}]}

Update profile inputs

Included in the vmware-nsx-4.x-stig-baseline is an example inputs file with variables relevant to NSX. This is used to provide InSpec with values specific to the environment being audited.

Open the inputs file for editing.

# Navigate to the InSpec profile folder
cd /usr/share/stigs/nsx/4.x/v1r2-srg/inspec/vmware-nsx-4.x-stig-baseline/

# Edit the inputs file
vi inputs-nsx-4.x-example.yml
# Navigate to the InSpec profile folder
cd /usr/share/stigs/nsx/4.x/v1r1-srg/inspec/vmware-nsx-4.x-stig-baseline/

# Edit the inputs file
vi inputs-nsx-4.x-example.yml

Update the inputs as shown below with values relevant to your environment. Specifically nsxManager,sessionToken,sessionCookieId,syslogServers,sftpServer,ntpServers,ntpServers,and nsxtVersion at a minimum. The other inputs are optional depending on your environment.

# NSX Manager IP or FQDN
nsxManager: '10.1.2.3'
# Session token generated for access to NSX. Example ead781b8-0e0c-456f-a04a-584e9ae2e45a
sessionToken: ''
# Session cookie id generated for access to NSX. Example 'JSESSIONID=2A165FCF851CA50FCD038DFC8E770038'
sessionCookieId: ''
# Manager
# Provide a list of authorized users and their roles to validate assigned permissions in NSX. The default local users and their roles are provided as an example. This currently only validates roles assigned to all of NSX and not to Projects or other scopes.
authorizedPermissions:
  admin:
    role: 'Enterprise Admin'
  audit:
    role: 'Auditor'
  guestuser1:
    role: 'Auditor'
  guestuser2:
    role: 'Auditor'
# Enter the environment specific syslog server vCenter should be forwarding logs to.
syslogServers:
  - 'loginsight.test.com'
# Enter the environment specific time servers.
ntpServers:
  - 'time-a-g.nist.gov'
  - 'time-b-g.nist.gov'
# Enter latest NSX version. Example '4.1.1.0'
nsxtVersion: '4.1.2.3'
# Enter an array of T0 Gateways that are approved to have multicast enabled.
t0multicastlist: []
# Enter an array of T0 Gateways interfaces that are approved to have multicast enabled.
t0mcinterfacelist: []
# Enter an array of T0 Gateways that are approved to have DHCP enabled.
t0dhcplist: []
# Enter an array of T1 Gateways that are approved to have DHCP enabled.
t1dhcplist: []
# Enter an array of T1 Gateways that are approved to have multicast enabled.
t1multicastlist: []
# NSX Manager IP or FQDN
nsxManager: ''
# Session token generated for access to NSX
sessionToken: ''
# Session cookie id generated for access to NSX. Example 'JSESSIONID=2A165FCF851CA50FCD038DFC8E770038'
sessionCookieId: ''
# Manager
# Enter the environment specific syslog server vCenter should be forwarding logs to.
syslogServers:
  - 'loginsight.test.com'
# Enter the environment specific time servers.
ntpServer1: 'time-a-g.nist.gov'
ntpServer2: 'time-b-g.nist.gov'
# Enter latest NSX version. Example '4.1.1.0'
nsxtVersion: '4.1.1.0'
# Enter an array of T0 Gateways that are approved to have multicast enabled.
t0multicastlist: []
# Enter an array of T0 Gateways interfaces that are approved to have multicast enabled.
t0mcinterfacelist: []
# Enter an array of T0 Gateways that are approved to have DHCP enabled.
t0dhcplist: []
# Enter an array of T1 Gateways that are approved to have DHCP enabled.
t1dhcplist: []
# Enter an array of T1 Gateways that are approved to have multicast enabled.
t1multicastlist: []

Run the audit

In this example we will be scanning all NSX components, specifying an inputs file, and outputting a report to the CLI and to a JSON file.

# Navigate to the InSpec profile folder
cd /usr/share/stigs/nsx/4.x/v1r2-srg/inspec/vmware-nsx-4.x-stig-baseline/

# Run the audit
cinc-auditor exec . --show-progress --enhanced-outcomes --input-file inputs-nsx-4.x-example.yml --reporter=cli json:/tmp/reports/MyNSXReport.json

# Shown below is the last part of the output at the CLI.
  ×  NT1F-4X-000020: The NSX Tier-1 Gateway Firewall must be configured to send traffic log entries to a central audit server. (1 failed)
     ✔  HTTP GET on https://10.215.77.149/policy/api/v1/search?query=( resource_type:TransportNode AND node_deployment_info.resource_type:EdgeNode ) status is expected to cmp == 200
     ✔  HTTP GET on https://10.215.77.149/api/v1/transport-nodes/a40c4ea4-16a1-11ee-8640-000c296f3e4c/node/services/syslog/exporters status is expected to cmp == 200
     ×  No syslog servers are configured on Edge Node: rlakey-svc.nsxedge-ob-21981742-1-stigtest is expected not to cmp == []

     expected: []
          got: []

     (compared using `cmp` matcher)

  ↺  NT1F-4X-000027: The NSX Tier-1 Gateway Firewall must be configured to inspect traffic at the application layer. (1 skipped)
     ✔  HTTP GET on https://10.215.77.149/policy/api/v1/infra/tier-1s status is expected to cmp == 200
     ↺  This check is a manual or policy based check and must be reviewed manually.


Profile:   VMware NSX 4.x Tier-1 Gateway Router STIG InSpec Profile (VMware NSX 4.x Tier-1 Gateway Router STIG InSpec Profile)
Version:   1.1
Target:    local://
Target ID: e45dd517-9256-59e3-8503-3351c863444c

  ↺  NT1R-4X-000016: The NSX Tier-1 Gateway must be configured to have all inactive interfaces removed. (1 skipped)
     ✔  HTTP GET on https://10.215.77.149/policy/api/v1/infra/tier-1s status is expected to cmp == 200
     ↺  This is a manual check. Review T1 interfaces and determine if any existing interfaces are orphaned and should be removed.
  ✔  NT1R-4X-000027: The NSX Tier-1 Gateway must be configured to have the DHCP service disabled if not in use.
     ✔  HTTP GET on https://10.215.77.149/policy/api/v1/infra/tier-1s status is expected to cmp == 200
{"tier0_path"=>"/infra/tier-0s/Tier0Gateway1", "failover_mode"=>"NON_PREEMPTIVE", "enable_standby_relocation"=>false, "route_advertisement_types"=>["TIER1_CONNECTED", "TIER1_STATIC_ROUTES"], "route_advertisement_rules"=>[{"name"=>"Rule 1", "subnets"=>["192.168.1.0/24", "192.168.2.0/24"], "prefix_operator"=>"GE", "action"=>"PERMIT"}], "force_whitelisting"=>false, "default_rule_logging"=>false, "disable_firewall"=>false, "ipv6_profile_paths"=>["/infra/ipv6-ndra-profiles/default", "/infra/ipv6-dad-profiles/default"], "pool_allocation"=>"ROUTING", "advanced_config"=>{"traffic_back_to_source"=>false, "centralized_mode_enabled"=>false}, "resource_type"=>"Tier1", "id"=>"Tier1Gateway1", "display_name"=>"Tier1Gateway1", "description"=>"Tier1-1 created through automation", "path"=>"/infra/tier-1s/Tier1Gateway1", "relative_path"=>"Tier1Gateway1", "parent_path"=>"/infra", "remote_path"=>"", "unique_id"=>"4f4dd7f0-30d7-4dff-8e9b-14524d6284a1", "realization_id"=>"4f4dd7f0-30d7-4dff-8e9b-14524d6284a1", "owner_id"=>"f1a08ebb-158a-4bed-908d-14cd342e4f9a", "marked_for_delete"=>false, "overridden"=>false, "_create_time"=>1688059851013, "_create_user"=>"admin", "_last_modified_time"=>1688059851013, "_last_modified_user"=>"admin", "_system_owned"=>false, "_protection"=>"NOT_PROTECTED", "_revision"=>0} ["dhcp_config_paths"] is expected to equal nil
  ↺  NT1R-4X-000102: The NSX Tier-1 Gateway must be configured to advertise a hop limit of at least 32 in Router Advertisement messages for IPv6 stateless auto-configuration deployments. (1 skipped)
     ✔  HTTP GET on https://10.215.77.149/policy/api/v1/infra/global-config status is expected to cmp == 200
     ↺  IPv6 Forwarding is not enabled. This is Not Applicable.
  ✔  NT1R-4X-000107: The NSX Tier-1 Gateway must be configured to have multicast disabled if not in use.
     ✔  HTTP GET on https://10.215.77.149/policy/api/v1/infra/tier-1s status is expected to cmp == 200
     ✔  HTTP GET on https://10.215.77.149/policy/api/v1/infra/tier-1s/Tier1Gateway1/locale-services/Tier1LocalServices-1/multicast status is expected to cmp == 404


Profile Summary: 21 successful controls, 30 control failures, 10 controls skipped
Test Summary: 162 successful, 57 failures, 12 skipped
# Navigate to the InSpec profile folder
cd /usr/share/stigs/nsx/4.x/v1r1-srg/inspec/vmware-nsx-4.x-stig-baseline/

# Run the audit
cinc-auditor exec . --show-progress --enhanced-outcomes --input-file inputs-nsx-4.x-example.yml --reporter=cli json:/tmp/reports/MyNSXReport.json

# Shown below is the last part of the output at the CLI.
  ×  NT1F-4X-000020: The NSX Tier-1 Gateway Firewall must be configured to send traffic log entries to a central audit server. (1 failed)
     ✔  HTTP GET on https://10.215.77.149/policy/api/v1/search?query=( resource_type:TransportNode AND node_deployment_info.resource_type:EdgeNode ) status is expected to cmp == 200
     ✔  HTTP GET on https://10.215.77.149/api/v1/transport-nodes/a40c4ea4-16a1-11ee-8640-000c296f3e4c/node/services/syslog/exporters status is expected to cmp == 200
     ×  No syslog servers are configured on Edge Node: rlakey-svc.nsxedge-ob-21981742-1-stigtest is expected not to cmp == []

     expected: []
          got: []

     (compared using `cmp` matcher)

  ↺  NT1F-4X-000027: The NSX Tier-1 Gateway Firewall must be configured to inspect traffic at the application layer. (1 skipped)
     ✔  HTTP GET on https://10.215.77.149/policy/api/v1/infra/tier-1s status is expected to cmp == 200
     ↺  This check is a manual or policy based check and must be reviewed manually.


Profile:   VMware NSX 4.x Tier-1 Gateway Router STIG InSpec Profile (VMware NSX 4.x Tier-1 Gateway Router STIG InSpec Profile)
Version:   1.1
Target:    local://
Target ID: e45dd517-9256-59e3-8503-3351c863444c

  ↺  NT1R-4X-000016: The NSX Tier-1 Gateway must be configured to have all inactive interfaces removed. (1 skipped)
     ✔  HTTP GET on https://10.215.77.149/policy/api/v1/infra/tier-1s status is expected to cmp == 200
     ↺  This is a manual check. Review T1 interfaces and determine if any existing interfaces are orphaned and should be removed.
  ✔  NT1R-4X-000027: The NSX Tier-1 Gateway must be configured to have the DHCP service disabled if not in use.
     ✔  HTTP GET on https://10.215.77.149/policy/api/v1/infra/tier-1s status is expected to cmp == 200
{"tier0_path"=>"/infra/tier-0s/Tier0Gateway1", "failover_mode"=>"NON_PREEMPTIVE", "enable_standby_relocation"=>false, "route_advertisement_types"=>["TIER1_CONNECTED", "TIER1_STATIC_ROUTES"], "route_advertisement_rules"=>[{"name"=>"Rule 1", "subnets"=>["192.168.1.0/24", "192.168.2.0/24"], "prefix_operator"=>"GE", "action"=>"PERMIT"}], "force_whitelisting"=>false, "default_rule_logging"=>false, "disable_firewall"=>false, "ipv6_profile_paths"=>["/infra/ipv6-ndra-profiles/default", "/infra/ipv6-dad-profiles/default"], "pool_allocation"=>"ROUTING", "advanced_config"=>{"traffic_back_to_source"=>false, "centralized_mode_enabled"=>false}, "resource_type"=>"Tier1", "id"=>"Tier1Gateway1", "display_name"=>"Tier1Gateway1", "description"=>"Tier1-1 created through automation", "path"=>"/infra/tier-1s/Tier1Gateway1", "relative_path"=>"Tier1Gateway1", "parent_path"=>"/infra", "remote_path"=>"", "unique_id"=>"4f4dd7f0-30d7-4dff-8e9b-14524d6284a1", "realization_id"=>"4f4dd7f0-30d7-4dff-8e9b-14524d6284a1", "owner_id"=>"f1a08ebb-158a-4bed-908d-14cd342e4f9a", "marked_for_delete"=>false, "overridden"=>false, "_create_time"=>1688059851013, "_create_user"=>"admin", "_last_modified_time"=>1688059851013, "_last_modified_user"=>"admin", "_system_owned"=>false, "_protection"=>"NOT_PROTECTED", "_revision"=>0} ["dhcp_config_paths"] is expected to equal nil
  ↺  NT1R-4X-000102: The NSX Tier-1 Gateway must be configured to advertise a hop limit of at least 32 in Router Advertisement messages for IPv6 stateless auto-configuration deployments. (1 skipped)
     ✔  HTTP GET on https://10.215.77.149/policy/api/v1/infra/global-config status is expected to cmp == 200
     ↺  IPv6 Forwarding is not enabled. This is Not Applicable.
  ✔  NT1R-4X-000107: The NSX Tier-1 Gateway must be configured to have multicast disabled if not in use.
     ✔  HTTP GET on https://10.215.77.149/policy/api/v1/infra/tier-1s status is expected to cmp == 200
     ✔  HTTP GET on https://10.215.77.149/policy/api/v1/infra/tier-1s/Tier1Gateway1/locale-services/Tier1LocalServices-1/multicast status is expected to cmp == 404


Profile Summary: 21 successful controls, 30 control failures, 10 controls skipped
Test Summary: 162 successful, 57 failures, 12 skipped

Convert the results to CKL

If a STIG Viewer CKL file is needed then the results from the scans can be converted to CKL with the SAF CLI.

# Converting the scan results from the prior section to CKL
saf convert hdf2ckl -i /tmp/reports/MyNSXReport.json -o /tmp/reports/MyNSXReport.ckl --hostname 10.215.77.149 --fqdn 10.215.77.149 --ip 10.215.77.149 --mac 00:00:00:00:00:00

Opening the CKL file in STIG Viewer will look like the screenshot below. Note the InSpec results are included in the Finding Details pane.

alt text

2 - Remediate NSX 4.x

Remediating NSX 4.x for STIG Compliance

Overview

Remediating NSX 4.x for STIG compliance involves configuring the NSX Managers, DFW, and any gateways deployed.

To remediate NSX, Ansible is the automation tool used to interact with the NSX REST API.

Prerequisites

Versions listed below were used for this documentation. Other versions of these tools may work as well but if issues are found it is recommended to try the versions listed here.

  • Ansible 2.14.2
  • Install JMESPath for community.general.json_query collection.
  • An NSX 4.x environment. The environment used in these examples has 1 T0 Gateway configured with BGP to an upstream router and 1 T1 Gateway deployed.
  • An account with sufficient privileges to configure NSX.

Assumptions

  • Commands are being ran from a Linux machine.
  • The DOD Compliance and Automation repository downloaded and extracted to /usr/share/stigs.
  • Ansible installed and all playbook dependencies resolved as provided in the requirements.yml file in each playbook. Install with ansible-galaxy roles install -r requirements.yml.

Important Considerations

Below is a table of controls selected for consideration but all controls should be examined for impact before implementing.

These can be turned on/off by with a variable that must be set to true as a condition for these tasks to run. See Update vars file for more details.

STIG IDTitleNotes
NDFW-4X-000015The NSX Distributed Firewall must limit the effects of packet flooding types of denial-of-service (DoS) attacks.
NDFW-4X-000016The NSX Distributed Firewall must deny network communications traffic by default and allow network communications traffic by exception.Ensure DFW rules are created to allow authorized traffic.
NDFW-4X-000029The NSX Distributed Firewall must configure SpoofGuard to restrict it from accepting outbound packets that contain an illegitimate address in the source address.Develop an operational plan to manage Spoofguard and identity workloads multiple IPs, etc, that may have issues.
NDFW-4X-000034The NSX Distributed Firewall must configure an IP Discovery profile to disable trust on every use methods.Develop an operational plan to manage Spoofguard and identity workloads multiple IPs, etc, that may have issues.
NT0F-4X-000015The NSX Tier-0 Gateway Firewall must manage excess bandwidth to limit the effects of packet flooding types of denial-of-service (DoS) attacks.
NT0F-4X-000016The NSX Tier-0 Gateway Firewall must deny network communications traffic by default and allow network communications traffic by exception.Ensure gateway firewall rules are created to allow authorized traffic.
NT0R-4X-000013The NSX Tier-0 Gateway must be configured to disable Protocol Independent Multicast (PIM) on all interfaces that are not required to support multicast routing.Ensure any gateways that are authorized to enable multicast are listed in the vars file.
NT0R-4X-000027The NSX Tier-0 Gateway must be configured to have the DHCP service disabled if not in use.Ensure any gateways that are authorized to enable DHCP are listed in the vars file.
NT0R-4X-000107The NSX Tier-0 Gateway must be configured to have multicast disabled if not in use.Ensure any gateways that are authorized to enable multicast are listed in the vars file.
NT1F-4X-000015The NSX Tier-1 Gateway Firewall must manage excess bandwidth to limit the effects of packet flooding types of denial-of-service (DoS) attacks.
NT1F-4X-000016The NSX Tier-1 Gateway Firewall must deny network communications traffic by default and allow network communications traffic by exception.Ensure gateway firewall rules are created to allow authorized traffic.
NT1R-4X-000027The NSX Tier-1 Gateway must be configured to have the DHCP service disabled if not in use.Ensure any gateways that are authorized to enable DHCP are listed in the vars file.
NT1R-4X-000107The NSX Tier-1 Gateway must be configured to have multicast disabled if not in use.Ensure any gateways that are authorized to enable multicast are listed in the vars file.

Also not all controls are covered by the Ansible playbook and may require manual remediation.

Remediating NSX

To remediate NSX we have provided an Ansible playbook that will target an NSX Manager over the REST API and configure any non-compliant controls.

Generate API Session Token

This profile uses Session-Based authentication to authenticate with NSX for auditing. A session token and cookie must be generated and provided an input for the profile. This can be generated in various ways via curl, tools like Postman, etc. For more information see the NSX API Documentation.

Note: If the user is a remote user, append “@domain” to the username, for example, “joe@example.com”. The domain must match a domain for a configured VIDM identity source or a configured LDAP identity source.

Curl example:

curl -k -i -X POST -d 'j_username=admin&j_password=C3.UwJ7TTK1P' https://10.215.77.149/api/session/create

# Example response
HTTP/1.1 200 OK
set-cookie: JSESSIONID=A6903A10F3AE7EB328F12EAF796053F5; Path=/; Secure; HttpOnly; SameSite=Lax
x-xsrf-token: ead781b8-0e0c-456f-a04a-584e9ae2e45a
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
content-type: application/json
content-length: 107
date: Thu, 29 Jun 2023 21:39:58 GMT
strict-transport-security: max-age=31536000; includeSubDomains
content-security-policy: frame-src 'self' blob:; frame-ancestors 'self'
server: envoy

{"roles":[{"role":"superusers","permissions":["read-api","read-write-api","read-cli","read-write-cli"]}]}
curl -k -i -X POST -d 'j_username=admin&j_password=C3.UwJ7TTK1P' https://10.215.77.149/api/session/create

# Example response
HTTP/1.1 200 OK
set-cookie: JSESSIONID=A6903A10F3AE7EB328F12EAF796053F5; Path=/; Secure; HttpOnly; SameSite=Lax
x-xsrf-token: ead781b8-0e0c-456f-a04a-584e9ae2e45a
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: 0
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
content-type: application/json
content-length: 107
date: Thu, 29 Jun 2023 21:39:58 GMT
strict-transport-security: max-age=31536000; includeSubDomains
content-security-policy: frame-src 'self' blob:; frame-ancestors 'self'
server: envoy

{"roles":[{"role":"superusers","permissions":["read-api","read-write-api","read-cli","read-write-cli"]}]}

Update vars file

In order to run the playbook, environment specific values need to be provided. An example vars file vars-nsx-4x-example.yml is provided.

In order to run the playbook, environment specific values need to be provided. An example vars file vars-nsx-4x-example.yml is provided and values need to be updated for the var_nsx_manager, var_jsession_id, var_session_token, var_ntp_server1, var_ntp_server2 variables at a minimum.

Open the inputs file for editing.

# Navigate to the InSpec profile folder
cd /usr/share/stigs/nsx/4.x/v1r2-srg/ansible/vmware-nsx-4.x-stig-ansible-hardening

# Edit the inputs file
vi vars-nsx-4x-example.yml
# Navigate to the InSpec profile folder
cd /usr/share/stigs/nsx/4.x/v1r1-srg/ansible/vmware-nsx-4.x-stig-ansible-hardening

# Edit the inputs file
vi vars-nsx-4x-example.yml

Update the variables as shown below with values relevant to your environment. Specifically var_nsx_manager, var_jsession_id, var_session_token, var_ntp_server1, var_ntp_server2 variables at a minimum.

# Connection information
var_nsx_manager: '10.180.98.230'
var_jsession_id: 'JSESSIONID=BDE1B5A54690B453F8968293D3C8A1E4'
var_session_token: '1d140509-ee7c-4cbf-9c22-9efdef982631'

# Manager
var_ntp_server1: 'time-a-g.nist.gov'
var_ntp_server2: 'time-b-g.nist.gov'

# DFW
# NDFW-4X-000004
run_dfw_enable_rule_logging: true
# NDFW-4X-000015
run_dfw_flood_protection: true
# NDFW-4X-000016
run_dfw_default_rule_action: false
var_dfw_default_rule_action: 'DROP'
# NDFW-4X-000029
run_dfw_spoofguard_profile: true
# NDFW-4X-000034
run_dfw_ip_discovery_profile: false

# T0 Firewall
# NT0F-4X-000016
run_t0fw_default_rule_action: false
var_t0fw_default_rule_action: 'DROP'

# T0 Router
# NT0R-4X-000013
run_t0rtr_disable_pim_on_interfaces: false
# array of t0 interface ids that should have PIM enabled
var_t0rtr_gateway_interfaces_with_multicast_enabled:
  - Tier0Interface1
# NT0R-4X-000027
run_t0rtr_disable_dhcp: false
# array of t0 ids that should have dhcp enabled
var_t0rtr_gateways_with_dhcp_enabled: []
# NT0R-4X-000107
run_t0rtr_disable_multicast: true
# array of t0 ids that should have multicast enabled
var_t0rtr_gateways_with_multicast_enabled:
  - Tier0Gateway1

# T1 Firewall
# NT1F-4X-000016
run_t1fw_default_rule_action: true
var_t1fw_default_rule_action: 'DROP'

# T1 Router
# NT1R-4X-000027
run_t1rtr_disable_dhcp: true
# array of t1 ids that should have dhcp enabled
var_t1rtr_gateways_with_dhcp_enabled: []
# NT1R-4X-000107
run_t1rtr_disable_multicast: true
# array of t1 ids that should have multicast enabled
var_t1rtr_gateways_with_multicast_enabled:
  - Tier1Gateway1
# Example vars file
# Connection information
var_nsx_manager: '10.180.98.230'
var_jsession_id: 'JSESSIONID=BDE1B5A54690B453F8968293D3C8A1E4'
var_session_token: '1d140509-ee7c-4cbf-9c22-9efdef982631'

# Manager
var_ntp_server1: 'time-a-g.nist.gov'
var_ntp_server2: 'time-b-g.nist.gov'

# DFW
# NDFW-4X-000004
run_dfw_enable_rule_logging: true
# NDFW-4X-000015
run_dfw_flood_protection: true
# NDFW-4X-000016
run_dfw_default_rule_action: false
var_dfw_default_rule_action: 'DROP'
# NDFW-4X-000029
run_dfw_spoofguard_profile: true
# NDFW-4X-000034
run_dfw_ip_discovery_profile: false

# T0 Firewall
# NT0F-4X-000016
run_t0fw_default_rule_action: false
var_t0fw_default_rule_action: 'DROP'

# T0 Router
# NT0R-4X-000013
run_t0rtr_disable_pim_on_interfaces: false
# array of t0 interface ids that should have PIM enabled
var_t0rtr_gateway_interfaces_with_multicast_enabled:
  - Tier0Interface1
# NT0R-4X-000027
run_t0rtr_disable_dhcp: false
# array of t0 ids that should have dhcp enabled
var_t0rtr_gateways_with_dhcp_enabled: []
# NT0R-4X-000107
run_t0rtr_disable_multicast: true
# array of t0 ids that should have multicast enabled
var_t0rtr_gateways_with_multicast_enabled:
  - Tier0Gateway1

# T1 Firewall
# NT1F-4X-000016
run_t1fw_default_rule_action: true
var_t1fw_default_rule_action: 'DROP'

# T1 Router
# NT1R-4X-000027
run_t1rtr_disable_dhcp: true
# array of t1 ids that should have dhcp enabled
var_t1rtr_gateways_with_dhcp_enabled: []
# NT1R-4X-000107
run_t1rtr_disable_multicast: true
# array of t1 ids that should have multicast enabled
var_t1rtr_gateways_with_multicast_enabled:
  - Tier1Gateway1

Running the playbook

To run all of the NSX controls, follow the example below.

# Navigate to the InSpec profile folder
cd /usr/share/stigs/nsx/4.x/v1r2-srg/ansible/vmware-nsx-4.x-stig-ansible-hardening

# Run the playbook
ansible-playbook playbook.yml -v --extra-vars @vars-nsx-4x-example.yml

# Output example
PLAY [NSX 4.x Remediation Automation] ***************************************************************************************************************************************************************************************

TASK [dfw : NDFW-4X-000016 - Find DFW default layer 3 rule] ***********************************************************************************************************************************************************
ok: [127.0.0.1] => {"cache_control": "no-cache, no-store, max-age=0, must-revalidate", "changed": false, "connection": "close", "content_type": "application/json", "cookies": {}, "cookies_string": "", "date": "Mon, 10 Jul 2023 16:49:52 GMT", "elapsed": 0, "expires": "0", "json": {"_create_time": 1688751880285, "_create_user": "system", "_last_modified_time": 1689007787614, "_last_modified_user": "admin", "_protection": "NOT_PROTECTED", "_revision": 1, "_system_owned": false, "action": "ALLOW", "destination_groups": ["ANY"], "destinations_excluded": false, "direction": "IN_OUT", "disabled": false, "display_name": "default-layer3-rule", "id": "default-layer3-rule", "ip_protocol": "IPV4_IPV6", "is_default": true, "logged": true, "marked_for_delete": false, "origin_site_id": "9d96be5a-afca-498c-8c04-8ca4514f7b40", "overridden": false, "owner_id": "9d96be5a-afca-498c-8c04-8ca4514f7b40", "parent_path": "/infra/domains/default/security-policies/default-layer3-section", "path": "/infra/domains/default/security-policies/default-layer3-section/rules/default-layer3-rule", "profiles": ["ANY"], "realization_id": "aa423bd5-1ab7-41b0-958c-aa101f264df6", "relative_path": "default-layer3-rule", "remote_path": "", "resource_type": "Rule", "rule_id": 2, "scope": ["ANY"], "sequence_number": 2147483647, "services": ["ANY"], "source_groups": ["ANY"], "sources_excluded": false, "unique_id": "aa423bd5-1ab7-41b0-958c-aa101f264df6"}, "msg": "OK (unknown bytes)", "pragma": "no-cache", "redirected": false, "server": "envoy", "status": 200, "strict_transport_security": "max-age=31536000; includeSubDomains", "transfer_encoding": "chunked", "url": "https://10.180.98.230/policy/api/v1/infra/domains/default/security-policies/default-layer3-section/rules/default-layer3-rule", "vary": "Accept-Encoding", "x_content_type_options": "nosniff", "x_envoy_upstream_service_time": "20", "x_frame_options": "SAMEORIGIN", "x_nsx_requestid": "1b66a182-4959-4cbf-8438-ec304ce83c7b", "x_xss_protection": "1; mode=block"}

TASK [dfw : NDFW-4X-000016 - Update DFW default layer 3 rule action to desired value] *********************************************************************************************************************************
changed: [127.0.0.1] => {"cache_control": "no-cache, no-store, max-age=0, must-revalidate", "changed": true, "connection": "close", "content_length": "0", "cookies": {}, "cookies_string": "", "date": "Mon, 10 Jul 2023 16:49:54 GMT", "elapsed": 0, "expires": "0", "msg": "OK (0 bytes)", "pragma": "no-cache", "redirected": false, "server": "envoy", "status": 200, "strict_transport_security": "max-age=31536000; includeSubDomains", "url": "https://10.180.98.230/policy/api/v1/infra/domains/default/security-policies/default-layer3-section/rules/default-layer3-rule", "x_content_type_options": "nosniff", "x_envoy_upstream_service_time": "69", "x_frame_options": "SAMEORIGIN", "x_nsx_requestid": "ce242eda-7340-4097-a72c-ff10ae72c4e8", "x_xss_protection": "1; mode=block"}
# Navigate to the InSpec profile folder
cd /usr/share/stigs/nsx/4.x/v1r1-srg/ansible/vmware-nsx-4.x-stig-ansible-hardening

# Run the playbook
ansible-playbook playbook.yml -v --extra-vars @vars-nsx-4x-example.yml

# Output example
PLAY [NSX 4.x Remediation Automation] ***************************************************************************************************************************************************************************************

TASK [dfw : NDFW-4X-000016 - Find DFW default layer 3 rule] ***********************************************************************************************************************************************************
ok: [127.0.0.1] => {"cache_control": "no-cache, no-store, max-age=0, must-revalidate", "changed": false, "connection": "close", "content_type": "application/json", "cookies": {}, "cookies_string": "", "date": "Mon, 10 Jul 2023 16:49:52 GMT", "elapsed": 0, "expires": "0", "json": {"_create_time": 1688751880285, "_create_user": "system", "_last_modified_time": 1689007787614, "_last_modified_user": "admin", "_protection": "NOT_PROTECTED", "_revision": 1, "_system_owned": false, "action": "ALLOW", "destination_groups": ["ANY"], "destinations_excluded": false, "direction": "IN_OUT", "disabled": false, "display_name": "default-layer3-rule", "id": "default-layer3-rule", "ip_protocol": "IPV4_IPV6", "is_default": true, "logged": true, "marked_for_delete": false, "origin_site_id": "9d96be5a-afca-498c-8c04-8ca4514f7b40", "overridden": false, "owner_id": "9d96be5a-afca-498c-8c04-8ca4514f7b40", "parent_path": "/infra/domains/default/security-policies/default-layer3-section", "path": "/infra/domains/default/security-policies/default-layer3-section/rules/default-layer3-rule", "profiles": ["ANY"], "realization_id": "aa423bd5-1ab7-41b0-958c-aa101f264df6", "relative_path": "default-layer3-rule", "remote_path": "", "resource_type": "Rule", "rule_id": 2, "scope": ["ANY"], "sequence_number": 2147483647, "services": ["ANY"], "source_groups": ["ANY"], "sources_excluded": false, "unique_id": "aa423bd5-1ab7-41b0-958c-aa101f264df6"}, "msg": "OK (unknown bytes)", "pragma": "no-cache", "redirected": false, "server": "envoy", "status": 200, "strict_transport_security": "max-age=31536000; includeSubDomains", "transfer_encoding": "chunked", "url": "https://10.180.98.230/policy/api/v1/infra/domains/default/security-policies/default-layer3-section/rules/default-layer3-rule", "vary": "Accept-Encoding", "x_content_type_options": "nosniff", "x_envoy_upstream_service_time": "20", "x_frame_options": "SAMEORIGIN", "x_nsx_requestid": "1b66a182-4959-4cbf-8438-ec304ce83c7b", "x_xss_protection": "1; mode=block"}

TASK [dfw : NDFW-4X-000016 - Update DFW default layer 3 rule action to desired value] *********************************************************************************************************************************
changed: [127.0.0.1] => {"cache_control": "no-cache, no-store, max-age=0, must-revalidate", "changed": true, "connection": "close", "content_length": "0", "cookies": {}, "cookies_string": "", "date": "Mon, 10 Jul 2023 16:49:54 GMT", "elapsed": 0, "expires": "0", "msg": "OK (0 bytes)", "pragma": "no-cache", "redirected": false, "server": "envoy", "status": 200, "strict_transport_security": "max-age=31536000; includeSubDomains", "url": "https://10.180.98.230/policy/api/v1/infra/domains/default/security-policies/default-layer3-section/rules/default-layer3-rule", "x_content_type_options": "nosniff", "x_envoy_upstream_service_time": "69", "x_frame_options": "SAMEORIGIN", "x_nsx_requestid": "ce242eda-7340-4097-a72c-ff10ae72c4e8", "x_xss_protection": "1; mode=block"}

A more conservative and preferred approach is to target any non-compliant controls or run each component separately allowed you to perform any functional testing in between.

# Providing the tag "dfw" will instruct the playbook to only run the dfw role. This tag can be seen in each roles task/main.yml file.
> ansible-playbook playbook.yml -v --extra-vars @vars-nsx-4.x-example.yml --tags dfw

# Providing the tag " NDFW-4X-000004" will instruct the playbook to only run task tagged with the STIG ID of  NDFW-4X-000004.
> ansible-playbook playbook.yml -v --extra-vars @vars-nsx-4.x-example.yml --tags NDFW-4X-000004
# Providing the tag "dfw" will instruct the playbook to only run the dfw role. This tag can be seen in each roles task/main.yml file.
> ansible-playbook playbook.yml -v --extra-vars @vars-nsx-4.x-example.yml --tags dfw

# Providing the tag " NDFW-4X-000004" will instruct the playbook to only run task tagged with the STIG ID of  NDFW-4X-000004.
> ansible-playbook playbook.yml -v --extra-vars @vars-nsx-4.x-example.yml --tags NDFW-4X-000004

3 - Audit NSX-T 3.x

Auditing NSX-T 3.x for STIG Compliance

Overview

Auditing NSX-T 3.x for STIG compliance involves scanning the NSX Managers, DFW, and any gateways deployed.

To audit NSX-T using InSpec we utilize the local transport to connect via the REST API and query it’s configuration.

Prerequisites

Versions listed below were used for this documentation. Other versions of these tools may work as well but if issues are found it is recommended to try the versions listed here.

  • The vmware-nsxt-3.x-stig-baseline profile downloaded.
  • InSpec/Cinc Auditor 5.22.3
  • SAF CLI 1.2.20
  • STIG Viewer 2.17
  • An NSX-T 3.x environment. 3.2.3 was used in these examples with 1 T0 Gateway configured with BGP to an upstream router and 1 T1 Gateway deployed.

Auditing NSX-T

Generate API Session Token

This profile uses Session-Based authentication to authenticate with NSX for auditing. A session token and cookie must be generated and provided an input for the profile. This can be generated in various ways via curl, tools like Postman, etc. For more information see the NSX API Documentation.

Note: If the user is a remote user, append “@domain” to the username, for example, “joe@example.com”. The domain must match a domain for a configured VIDM identity source or a configured LDAP identity source.

Curl example

curl -k -i -X POST -d 'j_username=admin&j_password=replacethis' https://10.43.173.83/api/session/create

# Example response
HTTP/1.1 200
Set-Cookie: JSESSIONID=6A0F43FCD07947BB21890CDA05DF26C0; Path=/; Secure; HttpOnly
X-XSRF-TOKEN: fe3d6167-09d5-4302-b6cd-be2e20947d58
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Content-Type: application/json
Content-Length: 107
Date: Thu, 06 Jul 2023 17:15:07 GMT
Server: NSX

{"roles":[{"role":"superusers","permissions":["read-api","read-write-api","read-cli","read-write-cli"]}]}

Update profile inputs

Included in the vmware-nsxt-3.x-stig-baseline is an example inputs-nsxt-3.x.yml file with the following inputs relevant to NSX-T.

Update the inputs as shown below with values relevant to your environment.

# General
# NSX Manager IP or FQDN
nsxManager: '10.43.173.83'
# Session token generated for access to NSX
sessionToken: 'fe3d6167-09d5-4302-b6cd-be2e20947d58'
# Session cookie id generated for access to NSX. Example 'JSESSIONID=2A165FCF851CA50FCD038DFC8E770038'
sessionCookieId: 'JSESSIONID=6A0F43FCD07947BB21890CDA05DF26C0'
# Manager
# Enter the environment specific syslog server vCenter should be forwarding logs to.
syslogServers:
  - 'loginsight.test.com'
  - 'loginsight2.test.com'
# Enter the environment specific time servers.
ntpServer1: 'time-a-g.nist.gov'
ntpServer2: 'time-b-g.nist.gov'
# Enter latest NSX version. Example '3.2.3.0'
nsxtVersion: '3.2.3.0'
# Enter an array of T0 Gateways that are approved to have multicast enabled.
t0multicastlist: []
# Enter an array of T0 Gateways interfaces that are approved to have multicast enabled.
t0mcinterfacelist: []
# Enter an array of T0 Gateways that are approved to have DHCP enabled.
t0dhcplist: []
# Enter an array of T1 Gateways that are approved to have DHCP enabled.
t1dhcplist: []
# Enter an array of T1 Gateways that are approved to have multicast enabled.
t1multicastlist: []

Run the audit

In this example we will be scanning all NSX components, specifying an inputs file, and outputting a report to the CLI and to a JSON file.

# Note this command is being ran from the root of the profile folder. Update paths as needed if running from a different location.
> inspec exec . --show-progress --input-file inputs-nsxt-3.x.yml --reporter=cli json:/mnt/c/Inspec/Reports/MyNSX3Report.json

# Shown below is the last part of the output at the CLI.
  ×  T1FW-3X-000036: The NSX-T Tier-1 Gateway Firewall must configure SpoofGuard to block outbound IP packets that contain illegitimate packet attributes. (6 failed)
     ✔  HTTP GET on https://10.43.173.83/api/v1/logical-switches status is expected to cmp == 200
     ✔  HTTP GET on https://10.43.173.83/policy/api/v1/search?query=( resource_type:SpoofGuardProfile AND unique_id:fad98876-d7ff-11e4-b9d6-1681e6b88ec1 ) status is expected to cmp == 200
     ×  JSON content address_binding_allowlist is expected to cmp == "true"

     expected: true
          got: false

     (compared using `cmp` matcher)

     ×  JSON content address_binding_whitelist is expected to cmp == "true"

     expected: true
          got: false

     (compared using `cmp` matcher)

     ✔  HTTP GET on https://10.43.173.83/policy/api/v1/search?query=( resource_type:SpoofGuardProfile AND unique_id:fad98876-d7ff-11e4-b9d6-1681e6b88ec1 ) status is expected to cmp == 200
     ×  JSON content address_binding_allowlist is expected to cmp == "true"

     expected: true
          got: false

     (compared using `cmp` matcher)

     ×  JSON content address_binding_whitelist is expected to cmp == "true"

     expected: true
          got: false

     (compared using `cmp` matcher)

     ✔  HTTP GET on https://10.43.173.83/policy/api/v1/search?query=( resource_type:SpoofGuardProfile AND unique_id:fad98876-d7ff-11e4-b9d6-1681e6b88ec1 ) status is expected to cmp == 200
     ×  JSON content address_binding_allowlist is expected to cmp == "true"

     expected: true
          got: false

     (compared using `cmp` matcher)

     ×  JSON content address_binding_whitelist is expected to cmp == "true"

     expected: true
          got: false

     (compared using `cmp` matcher)



Profile:   VMware NSX-T Tier 1 Gateway RTR STIG InSpec Profile (VMware NSX-T Tier 1 Gateway RTR STIG InSpec Profile)
Version:   1.1
Target:    local://
Target ID: 91850cb0-e902-5c20-9e21-05288aec4f93

  ↺  T1RT-3X-000016: The NSX-T Tier-1 Gateway must be configured to have all inactive interfaces removed. (1 skipped)
     ✔  HTTP GET on https://10.43.173.83/policy/api/v1/infra/tier-1s status is expected to cmp == 200
     ↺  This is a manual check. Review T1 interfaces and determine if any existing interfaces are orphaned and should be removed.
  ✔  T1RT-3X-000027: The NSX-T Tier-1 Gateway must be configured to have the DHCP service disabled if not in use.
     ✔  HTTP GET on https://10.43.173.83/policy/api/v1/infra/tier-1s status is expected to cmp == 200
{"tier0_path"=>"/infra/tier-0s/Tier0Gateway1", "failover_mode"=>"NON_PREEMPTIVE", "enable_standby_relocation"=>false, "route_advertisement_types"=>["TIER1_CONNECTED", "TIER1_STATIC_ROUTES"], "route_advertisement_rules"=>[{"name"=>"Rule 1", "subnets"=>["192.168.1.0/24", "192.168.2.0/24"], "prefix_operator"=>"GE", "action"=>"PERMIT"}], "force_whitelisting"=>false, "default_rule_logging"=>false, "disable_firewall"=>false, "ipv6_profile_paths"=>["/infra/ipv6-ndra-profiles/default", "/infra/ipv6-dad-profiles/default"], "pool_allocation"=>"ROUTING", "advanced_config"=>{"traffic_back_to_source"=>false}, "resource_type"=>"Tier1", "id"=>"Tier1Gateway1", "display_name"=>"Tier1Gateway1", "description"=>"Tier1-1 created through automation", "path"=>"/infra/tier-1s/Tier1Gateway1", "relative_path"=>"Tier1Gateway1", "parent_path"=>"/infra", "unique_id"=>"74a2d444-07e6-49c9-bdb8-973c1ad81524", "realization_id"=>"74a2d444-07e6-49c9-bdb8-973c1ad81524", "marked_for_delete"=>false, "overridden"=>false, "_create_time"=>1688661706117, "_create_user"=>"admin", "_last_modified_time"=>1688661706117, "_last_modified_user"=>"admin", "_system_owned"=>false, "_protection"=>"NOT_PROTECTED", "_revision"=>0} ["dhcp_config_paths"] is expected to equal nil
  ↺  T1RT-3X-000034: The NSX-T Tier-1 Gateway must be configured to enforce a Quality-of-Service (QoS) policy to limit the effects of packet flooding denial-of-service (DoS) attacks. (1 skipped)
     ✔  HTTP GET on https://10.43.173.83/policy/api/v1/infra/tier-1s status is expected to cmp == 200
     ↺  This is a manual check. Review that QoS policies support traffic priorities specified by the Combatant Commands/Services/Agencies needed to ensure sufficient capacity for mission-critical traffic.
  ✔  T1RT-3X-000084: The NSX-T Tier-1 Gateway must be configured to have multicast disabled if not in use.
     ✔  HTTP GET on https://10.43.173.83/policy/api/v1/infra/tier-1s status is expected to cmp == 200
     ✔  HTTP GET on https://10.43.173.83/policy/api/v1/infra/tier-1s/Tier1Gateway1/locale-services/Tier1LocalServices-1/multicast status is expected to cmp == 404


Profile Summary: 24 successful controls, 35 control failures, 12 controls skipped
Test Summary: 176 successful, 60 failures, 15 skipped

Convert the results to CKL

If a STIG Viewer CKL file is needed then the results from the scans can be converted to CKL with the SAF CLI.

# Converting the scan results from the prior section to CKL
saf convert hdf2ckl -i C:\inspec\Reports\MyNSX3Report.json -o C:\inspec\Reports\MyNSX3Report.ckl --hostname 10.43.173.83 --fqdn 10.43.173.83 --ip 10.43.173.83 --mac 00:00:00:00:00:00

Opening the CKL file in STIG Viewer will look like the screenshot below. Note the InSpec results are included in the Finding Details pane.

alt text

4 - Remediate NSX-T 3.x

Remediating NSX-T 3.x for STIG Compliance

Overview

Remediating NSX-T 3.x for STIG compliance involves configuring the NSX Managers, DFW, and any gateways deployed.

To remediate NSX-T, Ansible is the automation tool used to interact with the NSX-T REST API.

Prerequisites

Versions listed below were used for this documentation. Other versions of these tools may work as well but if issues are found it is recommended to try the versions listed here.

  • The vmware-nsxt-3.x-stig-ansible-hardening playbook downloaded.
  • Ansible 2.14.4
  • Install JMESPath for community.general.json_query collection.
  • An NSX-T 3.x environment. 3.2.3 was used in this example.
  • An account with sufficient privileges to configure NSX-T.

Important Considerations

Please read carefully before proceeding!

Some NSX-T STIG controls can be very impactful to your environment if care is not taken during implementation especially in a brownfield scenario. For example, changing the default DFW rule to deny traffic without first creating rules to allow authorized traffic.

Below is a table of controls selected for consideration but all controls should be examined for impact before implementing.

These can be turned on/off by with a variable that must be set to true as a condition for these tasks to run. See Update vars file for more details.

STIG IDTitleNotes
TDFW-3X-000019The NSX-T Distributed Firewall must block outbound traffic containing denial-of-service (DoS) attacks to protect against the use of internal information systems to launch any DoS attacks against other networks or endpoints.
TDFW-3X-000021The NSX-T Distributed Firewall must deny network communications traffic by default and allow network communications traffic by exception (i.e., deny all, permit by exception).Ensure DFW rules are created to allow authorized traffic.
TDFW-3X-000036The NSX-T Distributed Firewall must configure SpoofGuard to block outbound IP packets that contain illegitimate packet attributes.Develop an operational plan to manage Spoofguard and identity workloads multiple IPs, etc, that may have issues.
T0FW-3X-000019The NSX-T Tier-0 Gateway Firewall must block outbound traffic containing denial-of-service (DoS) attacks to protect against the use of internal information systems to launch any DoS attacks against other networks or endpoints.
T0FW-3X-000021The NSX-T Tier-0 Gateway Firewall must deny network communications traffic by default and allow network communications traffic by exception (i.e., deny all, permit by exception).Ensure gateway firewall rules are created to allow authorized traffic.
T0FW-3X-000036The NSX-T Tier-0 Gateway Firewall must configure SpoofGuard to block outbound IP packets that contain illegitimate packet attributes.Develop an operational plan to manage Spoofguard and identity workloads multiple IPs, etc, that may have issues.
T1FW-3X-000019The NSX-T Tier-1 Gateway Firewall must block outbound traffic containing denial-of-service (DoS) attacks to protect against the use of internal information systems to launch any DoS attacks against other networks or endpoints.
T1FW-3X-000021The NSX-T Tier-1 Gateway Firewall must deny network communications traffic by default and allow network communications traffic by exception (i.e., deny all, permit by exception).Ensure gateway firewall rules are created to allow authorized traffic.
T1FW-3X-000036The NSX-T Tier-1 Gateway Firewall must configure SpoofGuard to block outbound IP packets that contain illegitimate packet attributes.Develop an operational plan to manage Spoofguard and identity workloads multiple IPs, etc, that may have issues.

Also not all controls are covered by the Ansible playbook and may require manual remediation.

Remediating NSX-T

To remediate NSX-T we have provided an Ansible playbook that will target an NSX-T Manager over the REST API and configure any non-compliant controls.

Since Ansible can only be ran from Linux based systems, the examples below are being ran on an Ubuntu 22.04 WSL2 instance on Windows 11 for reference.

Generate API Session Token

This playbook uses Session-Based authentication to authenticate with NSX for remediation. A session token and cookie must be generated and provided an input for the profile. This can be generated in various ways via curl, tools like Postman, etc. For more information see the NSX API Documentation.

Note: If the user is a remote user, append “@domain” to the username, for example, “joe@example.com”. The domain must match a domain for a configured VIDM identity source or a configured LDAP identity source.

Curl example

curl -k -i -X POST -d 'j_username=admin&j_password=replacethis' https://10.43.173.83/api/session/create

# Example response
HTTP/1.1 200
Set-Cookie: JSESSIONID=6A0F43FCD07947BB21890CDA05DF26C0; Path=/; Secure; HttpOnly
X-XSRF-TOKEN: fe3d6167-09d5-4302-b6cd-be2e20947d58
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Content-Type: application/json
Content-Length: 107
Date: Thu, 06 Jul 2023 17:15:07 GMT
Server: NSX

{"roles":[{"role":"superusers","permissions":["read-api","read-write-api","read-cli","read-write-cli"]}]}

Update vars file

In order to run the playbook, environment specific values need to be provided. An example vars file vars-nsxt-3.x-example.yml is provided and values need to be updated for the var_nsx_manager, var_jsession_id, var_session_token, var_ntp_server1, var_ntp_server2 variables at a minimum.

# Manager variables
# NSX Manager IP or FQDN
var_nsx_manager: '10.43.173.83'
# Session cookie id generated for access to NSX. Example 'JSESSIONID=2A165FCF851CA50FCD038DFC8E770038'
var_jsession_id: 'JSESSIONID=77EE4288C048073598F50493167A881A'
# Session token generated for access to NSX
var_session_token: 'cabda93b-f2e9-406d-bcfa-ec56b9a07bce'
# Enter the environment specific time servers.
var_ntp_server1: 'time-a-g.nist.gov'
var_ntp_server2: 'time-b-g.nist.gov'

## Set to true/false to enable or disable controls
# DFW
# TDFW-3X-000002
run_dfw_unpublished_rules: true
# TDFW-3X-000005
run_dfw_rule_logging: true
# TDFW-3X-000019-28
run_dfw_floodprotprof: true
# TDFW-3X-000021 !!Caution if set to true this will set the default DFW rule to DROP!!
run_dfw_default_rule_action: false
var_dfw_default_rule_action_desired: "DROP"
# TDFW-3X-000026
run_dfw_syslog: true
# TDFW-3X-000036
run_dfw_spoofguard_prof: false
# TDFW-3X-000042
run_dfw_verify_time_based_rules: true

# Manager
# TNDM-3X-000012 TNDM-3X-000041
run_mgr_auth_policy: true
# TNDM-3X-000052 TNDM-3X-000080 TNDM-3X-000101
run_mgr_api_session_timeout: true
run_mgr_cli_timeout: true
# TNDM-3X-000068
run_mgr_ntp_servers: true
# TNDM-3X-000069
run_mgr_timezone: true
# TNDM-3X-000083
run_service_log_levels: true
# TNDM-3X-000098
run_mgr_disable_ceip_acceptance: true
# TNDM-3X-000099
run_mgr_disable_ssh: true
# TNDM-3X-000100
run_mgr_disable_local_accounts: true
# TNDM-3X-000103
run_mgr_enable_fips_for_lbs: true

# T0FW
# T0FW-3X-000002
run_t0fw_unpublished_rules: true
# T0FW-3X-000006
run_t0fw_rule_logging: true
# T0FW-3X-000011
run_t0fw_syslog_tls: true
# T0FW-3X-000019-28
run_t0fw_floodprotprof: true
# T0FW-3X-000021
run_t0fw_default_rule_action: false
var_t0fw_default_rule_action_desired: "DROP"
# T0FW-3X-000030
run_t0fw_gwfw_rules: true
# T0FW-3X-000036
run_t0fw_spoofguard_prof: false

# T0RT
# T0RT-3X-000003
run_t0rt_bgp_reject_advertisements: true
# T0RT-3X-000013
run_t0rt_gateway_interface_pim_multicast: true
# array of t0 interface ids that should have multicast enabled
var_t0rt_gateway_interfaces_with_multicast_enabled: []
# T0RT-3X-000016
run_t0rt_remove_inactive_interfaces: true
# T0RT-3X-000027
run_t0rt_disable_dhcp: true
# array of t0 ids that should have dhcp enabled
var_t0rt_gateways_with_dhcp_enabled: []
# T0RT-3X-000034
run_t0rt_qos_segment_profile: true
# T0RT-3X-000038
run_t0rt_restrict_traffic: true
# T0RT-3X-000051
run_t0rt_gateway_urpf: true
# T0RT-3X-000054
run_t0rt_auth_routing_protocols: true
# T0RT-3X-000055
run_t0rt_uniq_key_per_as: true
# T0RT-3X-000064,65,66
run_t0rt_gateway_icmp: true
# T0RT-3X-000067
run_t0rt_bgp_nbr_maxroutes: true
var_t0rt_upd_bgp_nbr_route_filter_max_routes: 200
# T0RT-3X-000084
run_t0rt_loopback_source_ibgp: true
# T0RT-3X-000095
run_t0rt_gateway_bgp_ospf: true
# T0RT-3X-000096
run_t0rt_gateway_multicast: true
# array of t0 ids that should have multicast enabled
var_t0rt_gateways_with_multicast_enabled: []

# T1FW
# T1FW-3X-000002
run_t1fw_unpublished_rules: true
# T1FW-3X-000005-06
run_t1fw_rule_logging: true
# T1FW-3X-000011-26
run_t1fw_syslog_tls: true
# T1FW-3X-000019-28
run_t1fw_floodprotprof: true
# T1FW-3X-000021
run_t1fw_default_rule_action: false
var_t1fw_default_rule_action_desired: "DROP"
# T1FW-3X-000030
run_t1fw_gwfw_rules: true
# T1FW-3X-000036
run_t1fw_spoofguard_prof: false

# T1RT
# T1RT-3X-000016
run_t1rt_remove_inactive_interfaces: true
# T1RT-3X-000027
run_t1rt_disable_dhcp: true
# array of t1 ids that should have dhcp enabled
var_t1rt_gateways_with_dhcp_enabled: []
# T1RT-3X-000034
run_t1rt_qos: true
# T1RT-3X-000084
run_t1rt_gateway_multicast: true
# array of t1 ids that should have multicast enabled
var_t1rt_gateways_with_multicast_enabled: []

Running the playbook

To run all of the NSX-T controls, follow the example below.

# The -k parameter will prompt for password and we are using extra-vars to specify a variable file for the playbook to use. Command assumes it is being ran from the playbook folder.
> ansible-playbook playbook.yml -v --extra-vars @vars-nsxt-3.x-example.yml

# Output example
PLAY [NSX-T 3.x Remediation Automation] ***************************************************************************************************************************************************************************************

TASK [dfw : Include DFW] ******************************************************************************************************************************************************************************************************
included: /mnt/c/gitlab/vmware-nsxt-3.x-stig-ansible-hardening/roles/dfw/tasks/dfw.yml for 127.0.0.1

TASK [dfw : TDFW-3X-000005 - Find DFW rules without logging enabled excluding the default layer 2 rule] ***********************************************************************************************************************
ok: [127.0.0.1] => {"cache_control": "no-cache, no-store, max-age=0, must-revalidate", "changed": false, "connection": "close", "content_type": "application/json", "cookies": {}, "cookies_string": "", "date": "Fri, 07 Jul 2023 01:04:54 GMT", "elapsed": 0, "expires": "0", "json": {"cursor": "3", "result_count": 3, "results": [{"action": "ALLOW", "id": "default-layer3-rule", "logged": false, "path": "/infra/domains/default/security-policies/default-layer3-section/rules/default-layer3-rule", "rule_id": 2, "sequence_number": 2147483647}, {"action": "ALLOW", "id": "default_rule_DHCP", "logged": false, "path": "/infra/domains/default/security-policies/default-layer3-section/rules/default_rule_DHCP", "rule_id": 4, "sequence_number": 50000}, {"action": "ALLOW", "id": "default_rule_NDP", "logged": false, "path": "/infra/domains/default/security-policies/default-layer3-section/rules/default_rule_NDP", "rule_id": 3, "sequence_number": 25000}]}, "msg": "OK (unknown bytes)", "pragma": "no-cache", "redirected": false, "server": "NSX", "status": 200, "strict_transport_security": "max-age=31536000 ; includeSubDomains", "transfer_encoding": "chunked", "url": "https://10.43.173.83/policy/api/v1/search?query=(resource_type:Rule%20AND%20logged:false%20AND%20!id:default-layer2-rule%20AND%20parent_path:?infra?domains?default?security-policies*)&included_fields=id,rule_id,logged,path,sequence_number,action", "vary": "accept-encoding", "x_content_type_options": "nosniff", "x_frame_options": "SAMEORIGIN", "x_nsx_requestid": "dfc6dbcf-e0df-4893-ad42-29c7b5a55c40", "x_xss_protection": "1; mode=block"}

TASK [dfw : TDFW-3X-000005 - Enable logging on DFW rules without logging enabled excluding the default layer 2 rule] **********************************************************************************************************
changed: [127.0.0.1] => (item={'rule_id': 2, 'sequence_number': 2147483647, 'path': '/infra/domains/default/security-policies/default-layer3-section/rules/default-layer3-rule', 'logged': False, 'action': 'ALLOW', 'id': 'default-layer3-rule'}) => {"ansible_loop_var": "item", "cache_control": "no-cache, no-store, max-age=0, must-revalidate", "changed": true, "connection": "close", "content_length": "0", "cookies": {}, "cookies_string": "", "date": "Fri, 07 Jul 2023 01:04:56 GMT", "elapsed": 1, "expires": "0", "item": {"action": "ALLOW", "id": "default-layer3-rule", "logged": false, "path": "/infra/domains/default/security-policies/default-layer3-section/rules/default-layer3-rule", "rule_id": 2, "sequence_number": 2147483647}, "msg": "OK (0 bytes)", "pragma": "no-cache", "redirected": false, "server": "NSX", "status": 200, "strict_transport_security": "max-age=31536000 ; includeSubDomains", "url": "https://10.43.173.83/policy/api/v1/infra/domains/default/security-policies/default-layer3-section/rules/default-layer3-rule", "x_content_type_options": "nosniff", "x_frame_options": "SAMEORIGIN", "x_nsx_requestid": "9d03bce2-d284-46b6-8898-c12d6003f453", "x_xss_protection": "1; mode=block"}
changed: [127.0.0.1] => (item={'rule_id': 4, 'sequence_number': 50000, 'path': '/infra/domains/default/security-policies/default-layer3-section/rules/default_rule_DHCP', 'logged': False, 'action': 'ALLOW', 'id': 'default_rule_DHCP'}) => {"ansible_loop_var": "item", "cache_control": "no-cache, no-store, max-age=0, must-revalidate", "changed": true, "connection": "close", "content_length": "0", "cookies": {}, "cookies_string": "", "date": "Fri, 07 Jul 2023 01:04:57 GMT", "elapsed": 0, "expires": "0", "item": {"action": "ALLOW", "id": "default_rule_DHCP", "logged": false, "path": "/infra/domains/default/security-policies/default-layer3-section/rules/default_rule_DHCP", "rule_id": 4, "sequence_number": 50000}, "msg": "OK (0 bytes)", "pragma": "no-cache", "redirected": false, "server": "NSX", "status": 200, "strict_transport_security": "max-age=31536000 ; includeSubDomains", "url": "https://10.43.173.83/policy/api/v1/infra/domains/default/security-policies/default-layer3-section/rules/default_rule_DHCP", "x_content_type_options": "nosniff", "x_frame_options": "SAMEORIGIN", "x_nsx_requestid": "16ecd5a7-2b74-4255-8e2e-a16d4c33f076", "x_xss_protection": "1; mode=block"}
changed: [127.0.0.1] => (item={'rule_id': 3, 'sequence_number': 25000, 'path': '/infra/domains/default/security-policies/default-layer3-section/rules/default_rule_NDP', 'logged': False, 'action': 'ALLOW', 'id': 'default_rule_NDP'}) => {"ansible_loop_var": "item", "cache_control": "no-cache, no-store, max-age=0, must-revalidate", "changed": true, "connection": "close", "content_length": "0", "cookies": {}, "cookies_string": "", "date": "Fri, 07 Jul 2023 01:04:57 GMT", "elapsed": 0, "expires": "0", "item": {"action": "ALLOW", "id": "default_rule_NDP", "logged": false, "path": "/infra/domains/default/security-policies/default-layer3-section/rules/default_rule_NDP", "rule_id": 3, "sequence_number": 25000}, "msg": "OK (0 bytes)", "pragma": "no-cache", "redirected": false, "server": "NSX", "status": 200, "strict_transport_security": "max-age=31536000 ; includeSubDomains", "url": "https://10.43.173.83/policy/api/v1/infra/domains/default/security-policies/default-layer3-section/rules/default_rule_NDP", "x_content_type_options": "nosniff", "x_frame_options": "SAMEORIGIN", "x_nsx_requestid": "88ad2a48-7039-41d6-a758-84999467a2b3", "x_xss_protection": "1; mode=block"}

A more conservative and preferred approach is to target any non-compliant controls or run each component separately allowed you to perform any functional testing in between.

# Providing the tag "dfw" will instruct the playbook to only run the dfw role. This tag can be seen in each roles task/main.yml file.
> ansible-playbook playbook.yml -v --extra-vars @vars-nsxt-3.x-example.yml --tags dfw

# Providing the tag " TDFW-3X-000005" will instruct the playbook to only run task tagged with the STIG ID of  TDFW-3X-000005.
> ansible-playbook playbook.yml -v --extra-vars @vars-nsxt-3.x-example.yml --tags TDFW-3X-000005