NSX Security Groups and Firewall Rules

As a next-gen firewall, NSX allows us to get very dynamic with our firewall rules and create complex behaviors out of comparably very few rules.  Let's look at the same example 3 Tier Application rule set that I wrote about in my post about the Direction and Applied To fields in NSX Firewall rules post, and look at how these groups could be configured and then we can look at how these rules could be made even more secure.  First though, here's the example rule set:

SourceDestinationActionApplied To
AnyInfraServicesAllowDFW
Client DevicesWebAllowWeb
DB, AppWebDenyWeb
WebAppAllowApp
DBAppDenyApp
AppDBAllowDB
WebDBDenyDB
SolutionASolutionAAllowSolutionA
SolutionBSolutionBAllowSolutionB
SolutionAAnyDenySolutionA
SolutionBAnyDenySolutionB

These rules will allow default 3 Tier Application communications (Web can talk to App, App can talk to DB) while blocking nonstandard communication within the solution and blocking all communication between solutions.  I'm sure that you can see how easily this could be expanded to work for SolutionC, SolutionD... SolutionN.

So, how do these groups work?  When a Security Group is defined in NSX, it has three main sections: Dynamic Members, Included Objects, and Excluded Objects.  It is very simple to determine which VMs are members of a given group: a group's membership is Dynamic Members + Included Objects - Excluded Objects.  So, if you have a given VM on all 3 of those lists, the end result is that it is not a member of the Security Group, because the final step is to remove any Excluded Objects.

Included and Excluded objects are just static lists of VMs, Port Groups, vApps, other Security Groups, or a few other things... so they're pretty much self explanatory.  But, what about Dynamic Members?  Well, Dynamic Members are determined in real time based on just about any criteria that you could want to use.  You can define multiple criteria and allow the membership to require either any or all of those criteria.  You can use (some) negative logic, as well as wildcards when defining Dynamic Members, and it's extremely powerful.  But, that doesn't mean that Included objects should be just cast aside, or even that, in practice, they are particularly static.

Within NSX, we can create Security Tags and then apply them to VMs.  These tags could denote just about anything, but in this example we'll use them to denote which solution(s) and tier(s) a given VM is part of.  So, we have created a few different Security Tags: SolutionA, SolutionB (to represent different customer solutions, such as SAP, SharePoint, etc.), Web, App, DB, ClientDevices, and InfraServices.  We have created Security Groups to correspond with each of these tags, then set the membership of each Security Group to be based on Security Tags (except for ClientDevices, which would probably be an IP range of non-VM objects).

That means that, if we are provisioning a VM to be the Web tier for the SharePoint solution, we would apply the Web and SharePoint tags to that VM.  That would dynamically add it to the Web and SharePoint Security Groups, which would apply appropriate firewall rules to allow that VM to function for that role in that application.  Given that the ability to dynamically adjust a Security Group's membership based on VM tag is so powerful, I have to ask, why would I ever use the static Included Objects list on a Security Group?

Because I can statically include a Security Tag, that's why.  Dynamic Membership is great and all, but isn't necessarily needed here.  If I knew that I might have an unknown number of SharePointXXX Security Tags at some point in the future and that I wanted them to all belong to the SharePoint Security Group, then I would use Dynamic Membership to grab all Security Tags that start with "SharePoint".  In this case, I don't actually need that level of flexibility, so I can use the somewhat more user friendly Included Objects list (where I can select the existing Security Tag instead of having to accurately type in the name).

So, I can set up my Security Groups to populate themselves based on Security Tags that can then be dynamically assigned to VMs.  What about a situation where a single VM belongs to multiple solutions (such as a shared SQL server with several database instances)?  Just tag it for both Solutions.  What if it's a consolidated 2 Tier application?  Whichever server is effectively doing two roles (frequently Web and App are combined, but not always) should just be tagged with both tiers.  We can very easily use this model to adjust our firewalling behavior for these situations without creating any new rules.

But now, I think that it's time to address the elephant in the room.  All of these example firewall rules have been missing an important column: Service.  I've been assuming that they all allow (or deny) Any service, so we have very effectively locked down which VMs can communicate, but we haven't done anything about *how* they communicate.  So, how does that fit into this model?  Well, to give you a clue, it's harder for me to adjust the table to add another column in this blog's WYSIWYG editor than it is to actually configure that in NSX (although that's more a condemnation of this editor than it is anything else...)

Source Destination Action Applied To Service
Any InfraServices Allow DFW Active Directory, NTP, DNS, DHCP
Client Devices Web Allow Web HTTPS
DB, App Web Deny Web ANY
Web App.HTTPS Allow App.HTTPS HTTPS
DB App Deny App ANY
App DB.MSSQL Allow DB.MSSQL Microsoft SQL
Web DB Deny DB ANY
SolutionA SolutionA Allow SolutionA ANY
SolutionB SolutionB Allow SolutionB ANY
SolutionA ANY Deny SolutionA ANY
SolutionB ANY Deny SolutionB ANY

There are a ton of services that are already defined in NSX, so we can modify our rules and Security Groups/Tags to allow us to very easily take advantage of them.

You'll notice that I used a form of dotted notation for the Security Group names.  In this model, we would have different Security Tags that are named along the standard of "Tier.Platform", such as DB.MSSQL or DB.Oracle or DB.PostgreSQL.  We would have a Security Group defined for each of those tags (with the Security Tags as Included Objects, as before), and we would need firewall rules that are Applied To each of those groups that allow the services that are appropriate to that platform.

The secret sauce is that we're keeping the generic Tier Security Groups, too.  Instead of using the Include Objects static list like before though, we would use Dynamic Membership to include all Security Tags that start with the appropriate prefix.  For example, our DB Security Group would include, as Dynamic Members, all Security Tags that start with "DB".  That way, every single DB VM will be members of both its platform specific DB group and the general DB group.

Keeping the general Web, App, and DB groups allows us to both keep our firewall rules simple and dynamic.  Look at our DB.MSSQL Allow rule, for example.  The Source for that rule is the general App Security Group.  That rule will allow any App tier platform to talk to the MS SQL servers (once again, within its own Solution Group) on MS SQL network ports.  So, when future SolutionX comes along with a totally new App tier platform but the classic MS SQL DB tier, we can simply define that App.NewPlatform Security Group, Service, and Inbound Allow rule, then we're good to go.  The existing SQL DB Inbound Allow rule will automatically detect this new solution's App tier and will allow the appropriate traffic in, as defined.

So, to recap, we can easily take advantage of Services to make our firewall rules far more secure (either based on classic Port/Protocol or by Deep Packet Inspection!), by simply making more granular destination groups and associating those appropriate services with those destinations.  Using Dynamic Membership to populate generalized tier groups allows us to keep the firewall rules minimal while maintaining flexibility and security.

Comments

Popular posts from this blog

PowerShell Sorting by Multiple Columns

Clone a Standard vSwitch from one ESXi Host to Another

Deleting Orphaned (AKA Zombie) VMDK Files