github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/nsxt_distributed_firewall_test.go (about) 1 //go:build network || nsxt || functional || openapi || ALL 2 3 package govcd 4 5 import ( 6 "fmt" 7 "os" 8 "strconv" 9 "strings" 10 "text/tabwriter" 11 12 "github.com/vmware/go-vcloud-director/v2/util" 13 14 "github.com/vmware/go-vcloud-director/v2/types/v56" 15 . "gopkg.in/check.v1" 16 ) 17 18 // Test_NsxtDistributedFirewall creates a list of distributed firewall rules with randomized 19 // parameters in two modes: 20 // * System user 21 // * Org Admin user 22 func (vcd *TestVCD) Test_NsxtDistributedFirewallRules(check *C) { 23 skipNoNsxtConfiguration(vcd, check) 24 skipOpenApiEndpointTest(vcd, check, types.OpenApiPathVersion1_0_0+types.OpenApiEndpointEdgeGateways) 25 vcd.skipIfNotSysAdmin(check) 26 27 adminOrg, err := vcd.client.GetAdminOrgByName(vcd.config.VCD.Org) 28 check.Assert(adminOrg, NotNil) 29 check.Assert(err, IsNil) 30 31 nsxtExternalNetwork, err := GetExternalNetworkV2ByName(vcd.client, vcd.config.VCD.Nsxt.ExternalNetwork) 32 check.Assert(err, IsNil) 33 check.Assert(nsxtExternalNetwork, NotNil) 34 35 vdc, vdcGroup := test_CreateVdcGroup(check, adminOrg, vcd) 36 check.Assert(vdc, NotNil) 37 check.Assert(vdcGroup, NotNil) 38 39 // Run firewall tests as System user 40 fmt.Println("# Running Distributed Firewall tests as 'System' user") 41 test_NsxtDistributedFirewallRules(vcd, check, vdcGroup.VdcGroup.Id, vcd.client, vdc) 42 43 // Prep Org admin user and run firewall tests 44 userName := strings.ToLower(check.TestName()) 45 fmt.Printf("# Running Distributed Firewall tests as Org Admin user '%s'\n", userName) 46 orgUserVcdClient, _, err := newOrgUserConnection(adminOrg, userName, "CHANGE-ME", vcd.config.Provider.Url, true) 47 check.Assert(err, IsNil) 48 orgUserOrgAdmin, err := orgUserVcdClient.GetAdminOrgById(adminOrg.AdminOrg.ID) 49 check.Assert(err, IsNil) 50 orgUserVdc, err := orgUserOrgAdmin.GetVDCById(vdc.Vdc.ID, false) 51 check.Assert(err, IsNil) 52 test_NsxtDistributedFirewallRules(vcd, check, vdcGroup.VdcGroup.Id, orgUserVcdClient, orgUserVdc) 53 54 // Cleanup 55 err = vdcGroup.Delete() 56 check.Assert(err, IsNil) 57 err = vdc.DeleteWait(true, true) 58 check.Assert(err, IsNil) 59 } 60 61 func test_NsxtDistributedFirewallRules(vcd *TestVCD, check *C, vdcGroupId string, vcdClient *VCDClient, vdc *Vdc) { 62 adminOrg, err := vcdClient.GetAdminOrgByName(vcd.config.VCD.Org) 63 check.Assert(adminOrg, NotNil) 64 check.Assert(err, IsNil) 65 66 vdcGroup, err := adminOrg.GetVdcGroupById(vdcGroupId) 67 check.Assert(err, IsNil) 68 69 _, err = vdcGroup.ActivateDfw() 70 check.Assert(err, IsNil) 71 72 // Get existing firewall rule configuration 73 fwRules, err := vdcGroup.GetDistributedFirewall() 74 check.Assert(err, IsNil) 75 check.Assert(fwRules.DistributedFirewallRuleContainer.Values, NotNil) 76 77 // Create some prerequisites and generate firewall rule configurations to feed them into config 78 randomizedFwRuleDefs, ipSet, secGroup := createDistributedFirewallDefinitions(check, vcd, vdcGroup.VdcGroup.Id, vcdClient, vdc) 79 80 fwRules.DistributedFirewallRuleContainer.Values = randomizedFwRuleDefs 81 82 if testVerbose { 83 dumpDistributedFirewallRulesToScreen(randomizedFwRuleDefs) 84 } 85 86 fwUpdated, err := vdcGroup.UpdateDistributedFirewall(fwRules.DistributedFirewallRuleContainer) 87 check.Assert(err, IsNil) 88 check.Assert(fwUpdated, Not(IsNil)) 89 90 check.Assert(len(fwUpdated.DistributedFirewallRuleContainer.Values), Equals, len(randomizedFwRuleDefs)) 91 92 // Check that all created rules have the same attributes and order 93 for index := range fwUpdated.DistributedFirewallRuleContainer.Values { 94 check.Assert(fwUpdated.DistributedFirewallRuleContainer.Values[index].Name, Equals, randomizedFwRuleDefs[index].Name) 95 check.Assert(fwUpdated.DistributedFirewallRuleContainer.Values[index].Direction, Equals, randomizedFwRuleDefs[index].Direction) 96 check.Assert(fwUpdated.DistributedFirewallRuleContainer.Values[index].IpProtocol, Equals, randomizedFwRuleDefs[index].IpProtocol) 97 check.Assert(fwUpdated.DistributedFirewallRuleContainer.Values[index].Enabled, Equals, randomizedFwRuleDefs[index].Enabled) 98 check.Assert(fwUpdated.DistributedFirewallRuleContainer.Values[index].Logging, Equals, randomizedFwRuleDefs[index].Logging) 99 check.Assert(fwUpdated.DistributedFirewallRuleContainer.Values[index].Comments, Equals, randomizedFwRuleDefs[index].Comments) 100 check.Assert(fwUpdated.DistributedFirewallRuleContainer.Values[index].ActionValue, Equals, randomizedFwRuleDefs[index].ActionValue) 101 102 for fwGroupIndex := range fwUpdated.DistributedFirewallRuleContainer.Values[index].SourceFirewallGroups { 103 check.Assert(fwUpdated.DistributedFirewallRuleContainer.Values[index].SourceFirewallGroups[fwGroupIndex].ID, Equals, randomizedFwRuleDefs[index].SourceFirewallGroups[fwGroupIndex].ID) 104 } 105 106 for fwGroupIndex := range fwUpdated.DistributedFirewallRuleContainer.Values[index].DestinationFirewallGroups { 107 check.Assert(fwUpdated.DistributedFirewallRuleContainer.Values[index].DestinationFirewallGroups[fwGroupIndex].ID, Equals, randomizedFwRuleDefs[index].DestinationFirewallGroups[fwGroupIndex].ID) 108 } 109 110 // Ensure the same amount of Application Port Profiles are assigned and created 111 check.Assert(len(fwUpdated.DistributedFirewallRuleContainer.Values), Equals, len(randomizedFwRuleDefs)) 112 definedAppPortProfileIds := extractIdsFromOpenApiReferences(randomizedFwRuleDefs[index].ApplicationPortProfiles) 113 for _, appPortProfile := range fwUpdated.DistributedFirewallRuleContainer.Values[index].ApplicationPortProfiles { 114 check.Assert(contains(appPortProfile.ID, definedAppPortProfileIds), Equals, true) 115 } 116 117 // Ensure the same amount of Network Context Profiles are assigned and created 118 definedNetContextProfileIds := extractIdsFromOpenApiReferences(randomizedFwRuleDefs[index].NetworkContextProfiles) 119 for _, networkContextProfile := range fwUpdated.DistributedFirewallRuleContainer.Values[index].NetworkContextProfiles { 120 check.Assert(contains(networkContextProfile.ID, definedNetContextProfileIds), Equals, true) 121 } 122 } 123 124 // Cleanup 125 err = fwRules.DeleteAllRules() 126 check.Assert(err, IsNil) 127 // Check that rules were removed 128 newRules, err := vdcGroup.GetDistributedFirewall() 129 check.Assert(err, IsNil) 130 check.Assert(len(newRules.DistributedFirewallRuleContainer.Values) == 0, Equals, true) 131 132 // Cleanup remaining setup 133 _, err = vdcGroup.DisableDefaultPolicy() 134 check.Assert(err, IsNil) 135 _, err = vdcGroup.DeactivateDfw() 136 check.Assert(err, IsNil) 137 err = ipSet.Delete() 138 check.Assert(err, IsNil) 139 err = secGroup.Delete() 140 check.Assert(err, IsNil) 141 } 142 143 // createDistributedFirewallDefinitions creates some randomized firewall rule configurations to match possible configurations 144 func createDistributedFirewallDefinitions(check *C, vcd *TestVCD, vdcGroupId string, vcdClient *VCDClient, vdc *Vdc) ([]*types.DistributedFirewallRule, *NsxtFirewallGroup, *NsxtFirewallGroup) { 145 // This number does not impact performance because all rules are created at once in the API 146 numberOfRules := 40 147 148 // Pre-Create Firewall Groups (IP Set and Security Group to randomly configure them) 149 ipSet := preCreateVdcGroupIpSet(check, vcd, vdcGroupId, vdc) 150 secGroup := preCreateVdcGroupSecurityGroup(check, vcd, vdcGroupId, vdc) 151 fwGroupIds := []string{ipSet.NsxtFirewallGroup.ID, secGroup.NsxtFirewallGroup.ID} 152 fwGroupRefs := convertSliceOfStringsToOpenApiReferenceIds(fwGroupIds) 153 appPortProfileReferences := getRandomListOfAppPortProfiles(check, vcd) 154 networkContextProfiles := getRandomListOfNetworkContextProfiles(check, vcd, vcdClient) 155 156 firewallRules := make([]*types.DistributedFirewallRule, numberOfRules) 157 for a := 0; a < numberOfRules; a++ { 158 159 // Feed in empty value for source and destination or a firewall group 160 src := pickRandomOpenApiRefOrEmpty(fwGroupRefs) 161 var srcValue []types.OpenApiReference 162 dst := pickRandomOpenApiRefOrEmpty(fwGroupRefs) 163 var dstValue []types.OpenApiReference 164 if src != (types.OpenApiReference{}) { 165 srcValue = []types.OpenApiReference{src} 166 } 167 if dst != (types.OpenApiReference{}) { 168 dstValue = []types.OpenApiReference{dst} 169 } 170 171 firewallRules[a] = &types.DistributedFirewallRule{ 172 Name: check.TestName() + strconv.Itoa(a), 173 ActionValue: pickRandomString([]string{"ALLOW", "DROP", "REJECT"}), 174 Enabled: a%2 == 0, 175 SourceFirewallGroups: srcValue, 176 DestinationFirewallGroups: dstValue, 177 ApplicationPortProfiles: appPortProfileReferences[0:a], 178 IpProtocol: pickRandomString([]string{"IPV6", "IPV4", "IPV4_IPV6"}), 179 Logging: a%2 == 1, 180 Direction: pickRandomString([]string{"IN", "OUT", "IN_OUT"}), 181 } 182 183 // Network Context Profile can usually work with up to one Application Profile therefore this 184 // needs to be explicitly preset 185 if a%5 == 1 { // Every fifth rule 186 netCtxProfile := networkContextProfiles[0:a] 187 networkContextProfile := make([]types.OpenApiReference, 0) 188 for _, netCtxProf := range netCtxProfile { 189 if netCtxProf.ID != "" { 190 networkContextProfile = append(networkContextProfile, types.OpenApiReference{ID: netCtxProf.ID, Name: netCtxProf.Name}) 191 } 192 } 193 194 firewallRules[a].NetworkContextProfiles = networkContextProfile 195 // firewallRules[a].ApplicationPortProfiles = appPortProfileReferences[0:1] 196 firewallRules[a].ApplicationPortProfiles = nil 197 198 } 199 200 // API V36.2 introduced new field Comment which is shown in UI 201 if vcd.client.Client.APIVCDMaxVersionIs(">= 36.2") { 202 firewallRules[a].Comments = "Comment Rule" 203 } 204 205 } 206 207 return firewallRules, ipSet, secGroup 208 } 209 210 func preCreateVdcGroupIpSet(check *C, vcd *TestVCD, ownerId string, nsxtVdc *Vdc) *NsxtFirewallGroup { 211 ipSetDefinition := &types.NsxtFirewallGroup{ 212 Name: check.TestName() + "ipset", 213 Description: check.TestName() + "-Description", 214 Type: types.FirewallGroupTypeIpSet, 215 OwnerRef: &types.OpenApiReference{ID: ownerId}, 216 217 IpAddresses: []string{ 218 "12.12.12.1", 219 "10.10.10.0/24", 220 "11.11.11.1-11.11.11.2", 221 // represents the block of IPv6 addresses from 2001:db8:0:0:0:0:0:0 to 2001:db8:0:ffff:ffff:ffff:ffff:ffff 222 "2001:db8::/48", 223 "2001:db6:0:0:0:0:0:0-2001:db6:0:ffff:ffff:ffff:ffff:ffff", 224 }, 225 } 226 227 // Create IP Set and add to cleanup if it was created 228 createdIpSet, err := nsxtVdc.CreateNsxtFirewallGroup(ipSetDefinition) 229 check.Assert(err, IsNil) 230 openApiEndpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointFirewallGroups + createdIpSet.NsxtFirewallGroup.ID 231 PrependToCleanupListOpenApi(createdIpSet.NsxtFirewallGroup.Name, check.TestName(), openApiEndpoint) 232 233 return createdIpSet 234 } 235 236 func preCreateVdcGroupSecurityGroup(check *C, vcd *TestVCD, ownerId string, nsxtVdc *Vdc) *NsxtFirewallGroup { 237 fwGroupDefinition := &types.NsxtFirewallGroup{ 238 Name: check.TestName() + "security-group", 239 Description: check.TestName() + "-Description", 240 Type: types.FirewallGroupTypeSecurityGroup, 241 OwnerRef: &types.OpenApiReference{ID: ownerId}, 242 } 243 244 // Create firewall group and add to cleanup if it was created 245 createdSecGroup, err := nsxtVdc.CreateNsxtFirewallGroup(fwGroupDefinition) 246 check.Assert(err, IsNil) 247 openApiEndpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointFirewallGroups + createdSecGroup.NsxtFirewallGroup.ID 248 PrependToCleanupListOpenApi(createdSecGroup.NsxtFirewallGroup.Name, check.TestName(), openApiEndpoint) 249 250 return createdSecGroup 251 } 252 253 func getRandomListOfNetworkContextProfiles(check *C, vcd *TestVCD, vdcClient *VCDClient) []types.OpenApiReference { 254 networkContextProfiles, err := GetAllNetworkContextProfiles(&vcd.client.Client, nil) 255 check.Assert(err, IsNil) 256 openApiRefs := make([]types.OpenApiReference, 1) 257 for _, networkContextProfile := range networkContextProfiles { 258 // Skipping network context profile which has hardcoded destinations and throws error when used in firewall rules with specified destinations 259 if strings.Contains(networkContextProfile.Description, "ALG") || strings.Contains(networkContextProfile.Description, "includes the URL categories") { 260 continue 261 } 262 openApiRef := types.OpenApiReference{ 263 ID: networkContextProfile.ID, 264 Name: networkContextProfile.Name, 265 } 266 267 openApiRefs = append(openApiRefs, openApiRef) 268 } 269 270 return openApiRefs 271 } 272 273 func dumpDistributedFirewallRulesToScreen(rules []*types.DistributedFirewallRule) { 274 fmt.Println("# The following firewall rules will be created") 275 w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0) 276 fmt.Fprintln(w, "Name\tDirection\tIP Protocol\tEnabled\tAction\tLogging\tSrc Count\tDst Count\tAppPortProfile Count\tNet Context Profile Count") 277 278 for _, rule := range rules { 279 fmt.Fprintf(w, "%s\t%s\t%s\t%t\t%s\t%t\t%d\t%d\t%d\t%d\n", rule.Name, rule.Direction, rule.IpProtocol, 280 rule.Enabled, rule.Action, rule.Logging, len(rule.SourceFirewallGroups), len(rule.DestinationFirewallGroups), len(rule.ApplicationPortProfiles), len(rule.NetworkContextProfiles)) 281 } 282 err := w.Flush() 283 if err != nil { 284 util.Logger.Printf("Error while dumping Distributed Firewall rules to screen: %s", err) 285 } 286 } 287 288 // Test_NsxtDistributedFirewallRule tests the capability of managing Firewall Rules one by one using 289 // `DistributedFirewallRule` type. 290 func (vcd *TestVCD) Test_NsxtDistributedFirewallRule(check *C) { 291 if vcd.skipAdminTests { 292 check.Skip(fmt.Sprintf(TestRequiresSysAdminPrivileges, check.TestName())) 293 } 294 skipNoNsxtConfiguration(vcd, check) 295 skipOpenApiEndpointTest(vcd, check, types.OpenApiPathVersion1_0_0+types.OpenApiEndpointEdgeGateways) 296 297 adminOrg, err := vcd.client.GetAdminOrgByName(vcd.config.VCD.Org) 298 check.Assert(adminOrg, NotNil) 299 check.Assert(err, IsNil) 300 301 nsxtExternalNetwork, err := GetExternalNetworkV2ByName(vcd.client, vcd.config.VCD.Nsxt.ExternalNetwork) 302 check.Assert(nsxtExternalNetwork, NotNil) 303 check.Assert(err, IsNil) 304 305 vdc, vdcGroup := test_CreateVdcGroup(check, adminOrg, vcd) 306 check.Assert(vdc, NotNil) 307 check.Assert(vdcGroup, NotNil) 308 309 defer func() { 310 // Cleanup 311 err = vdcGroup.Delete() 312 check.Assert(err, IsNil) 313 err = vdc.DeleteWait(true, true) 314 check.Assert(err, IsNil) 315 }() 316 317 fmt.Println("# Running Distributed Firewall tests for single Rule") 318 test_NsxtDistributedFirewallRule(vcd, check, vdcGroup.VdcGroup.Id, vcd.client, vdc) 319 } 320 321 func test_NsxtDistributedFirewallRule(vcd *TestVCD, check *C, vdcGroupId string, vcdClient *VCDClient, vdc *Vdc) { 322 adminOrg, err := vcdClient.GetAdminOrgByName(vcd.config.VCD.Org) 323 check.Assert(adminOrg, NotNil) 324 check.Assert(err, IsNil) 325 326 vdcGroup, err := adminOrg.GetVdcGroupById(vdcGroupId) 327 check.Assert(err, IsNil) 328 329 _, err = vdcGroup.ActivateDfw() 330 check.Assert(err, IsNil) 331 332 // Prep firewall rule sample to operate with 333 randomizedFwRuleDefs, ipSet, secGroup := createDistributedFirewallDefinitions(check, vcd, vdcGroup.VdcGroup.Id, vcdClient, vdc) 334 // defer cleanup function in case something goes wrong 335 defer func() { 336 dfw, err := vdcGroup.GetDistributedFirewall() 337 check.Assert(err, IsNil) 338 err = dfw.DeleteAllRules() 339 check.Assert(err, IsNil) 340 _, err = vdcGroup.DisableDefaultPolicy() 341 check.Assert(err, IsNil) 342 err = ipSet.Delete() 343 check.Assert(err, IsNil) 344 err = secGroup.Delete() 345 check.Assert(err, IsNil) 346 }() 347 348 randomizedFwRuleSubSet := randomizedFwRuleDefs[0:5] // taking only first 5 rules to limit time of testing 349 350 // removing default firewall rule which is created by VCD when vdcGroup.ActivateDfw() is executed 351 err = vdcGroup.DeleteAllDistributedFirewallRules() 352 check.Assert(err, IsNil) 353 354 // Adding firewal rules one by one and checking that each of them is 355 testDistributedFirewallRuleSequence(vcd, check, randomizedFwRuleSubSet, vdcGroup, false) 356 testDistributedFirewallRuleSequence(vcd, check, randomizedFwRuleSubSet, vdcGroup, true) 357 } 358 359 // testDistributedFirewallRuleSequence tests the following: 360 // * create firewall rules one one by one 361 // * check that the order of firewall rules is the same as requested (or exactly reverse if 362 // reverseOrder=true) 363 // * check that all IDs of created firewall rules persisted during further updates (means that no 364 // firewall rules were recreated during addition of new ones) 365 func testDistributedFirewallRuleSequence(vcd *TestVCD, check *C, randomizedFwRuleSubSet []*types.DistributedFirewallRule, vdcGroup *VdcGroup, reverseOrder bool) { 366 createdIdsFound := make(map[string]bool) 367 fmt.Printf("# Creating '%d' rules one by one (reverseOrder: %t)\n", len(randomizedFwRuleSubSet), reverseOrder) 368 previousRuleId := "" 369 for _, rule := range randomizedFwRuleSubSet { 370 if testVerbose { 371 fmt.Printf("%s\t%s\t%s\t%t\t%s\t%t\t%d\t%d\t%d\t%d\n", rule.Name, rule.Direction, rule.IpProtocol, 372 rule.Enabled, rule.Action, rule.Logging, len(rule.SourceFirewallGroups), len(rule.DestinationFirewallGroups), len(rule.ApplicationPortProfiles), len(rule.NetworkContextProfiles)) 373 } 374 375 completeDfw, singleCreatedFwRule, err := vdcGroup.CreateDistributedFirewallRule(previousRuleId, rule) 376 check.Assert(err, IsNil) 377 check.Assert(completeDfw, NotNil) 378 check.Assert(singleCreatedFwRule, NotNil) 379 createdIdsFound[singleCreatedFwRule.Rule.ID] = false 380 381 // caching ID to use as previous rule in case 382 if reverseOrder { 383 previousRuleId = singleCreatedFwRule.Rule.ID 384 } 385 } 386 fmt.Printf("# Done creating '%d' rules one by one (reverseOrder: %t)\n", len(randomizedFwRuleSubSet), reverseOrder) 387 388 // Retrieve all firewall rules and check that order matches 389 allRules, err := vdcGroup.GetDistributedFirewall() 390 check.Assert(err, IsNil) 391 check.Assert(len(allRules.DistributedFirewallRuleContainer.Values), Equals, len(randomizedFwRuleSubSet)) 392 393 // check that rule order is exactly as expected (either reverse of randomizedFwRuleSubSet or exactly the same based on reverseOrder parameter) 394 if reverseOrder { 395 for ruleIndex, rule := range allRules.DistributedFirewallRuleContainer.Values { 396 reverseRuleIndex := len(randomizedFwRuleSubSet) - ruleIndex - 1 397 check.Assert(rule.Name, Equals, randomizedFwRuleSubSet[reverseRuleIndex].Name) 398 createdIdsFound[rule.ID] = true 399 } 400 } else { 401 for ruleIndex, rule := range allRules.DistributedFirewallRuleContainer.Values { 402 check.Assert(rule.Name, Equals, randomizedFwRuleSubSet[ruleIndex].Name) 403 createdIdsFound[rule.ID] = true 404 } 405 } 406 407 // Check that all created IDs are in the final output (none of the firewall rules were recreated) 408 for _, value := range createdIdsFound { 409 check.Assert(value, Equals, true) 410 } 411 412 // Perform Update 413 ruleById, err := vdcGroup.GetDistributedFirewallRuleById(allRules.DistributedFirewallRuleContainer.Values[0].ID) 414 check.Assert(err, IsNil) 415 416 updatedRuleName := check.TestName() + "-updated" 417 ruleById.Rule.Name = updatedRuleName 418 updatedRule, err := ruleById.Update(ruleById.Rule) 419 check.Assert(err, IsNil) 420 check.Assert(updatedRule.Rule.Name, Equals, updatedRuleName) 421 422 // Delete 423 err = updatedRule.Delete() 424 check.Assert(err, IsNil) 425 426 notFoundById, err := vdcGroup.GetDistributedFirewallRuleById(updatedRule.Rule.ID) 427 check.Assert(err, NotNil) 428 check.Assert(notFoundById, IsNil) 429 430 // Clean up created firewall rules for next phase 431 err = vdcGroup.DeleteAllDistributedFirewallRules() 432 check.Assert(err, IsNil) 433 }