Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle internal IPs in rule block #297

Merged
merged 16 commits into from
Mar 4, 2025
4 changes: 4 additions & 0 deletions pkg/analyzer/analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ var allTests = []analyzerTest{
name: "ExampleExclude",
exData: data.ExampleExclude,
},
{
name: "Example1dExternalWithSegments",
exData: data.Example1dExternalWithSegments,
},
}

func (a *analyzerTest) file() string {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Analyzed connectivity:
Source |Destination |Permitted connections
A |B |TCP dst-ports: 445
B |C-no-address |TCP dst-ports: 443

7 changes: 5 additions & 2 deletions pkg/configuration/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type nsxConfigParser struct {
groupPathsToObjects map[string]*collector.Group
servicePathsToObjects map[string]*collector.Service
topology *nsxTopology
ruleBlockPerEP map[topology.Endpoint][]*topology.RuleIPBlock // map from vm to its blocks
}

func (p *nsxConfigParser) init() {
Expand All @@ -61,6 +62,7 @@ func (p *nsxConfigParser) init() {
p.servicePathsToObjects = map[string]*collector.Service{}
p.groupToVMsListCache = map[*collector.Group][]topology.Endpoint{}
p.servicePathToConnCache = map[string]*netset.TransportSet{}
p.ruleBlockPerEP = map[topology.Endpoint][]*topology.RuleIPBlock{}
}

func (p *nsxConfigParser) runParser() error {
Expand Down Expand Up @@ -119,7 +121,7 @@ func (p *nsxConfigParser) storeParsedSegments() {
func (p *nsxConfigParser) removeVMsWithoutGroups() {
toRemove := []topology.Endpoint{}
for vm, groups := range p.configRes.GroupsPerVM {
if len(groups) == 0 {
if len(groups) == 0 && len(p.ruleBlockPerEP[vm]) == 0 {
logging.Debugf("ignoring VM without groups: %s", vm.Name())
toRemove = append(toRemove, vm)
}
Expand Down Expand Up @@ -308,7 +310,8 @@ func (p *nsxConfigParser) getAllGroups() {

func (p *nsxConfigParser) getEndpointsFromScopePaths(groupsPaths []string) ([]topology.Endpoint, []*collector.Group) {
if slices.Contains(groupsPaths, anyStr) {
return append(p.allGroupsVMs, p.configRes.externalIPs...), p.allGroups // all endpoints and groups
// in scope - "any" are all the vms and external endpoints
return p.configRes.Endpoints(), p.allGroups // all endpoints and groups
}
endPoints, groups, _ := p.getEndpointsFromGroupsPaths(groupsPaths, false)
return endPoints, groups
Expand Down
38 changes: 35 additions & 3 deletions pkg/configuration/topology.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func (p *nsxConfigParser) getTopology() (err error) {
}
p.getAllRulesIPBlocks()
p.getExternalIPs()
// todo - calc VMs of the block
p.getRuleBlocksVMs()
return nil
}

Expand Down Expand Up @@ -81,8 +81,7 @@ func (p *nsxConfigParser) getAllRulesIPBlocks() {
}
}
// remove duplications, "ANY" and paths to groups:
slices.Sort(allIPs)
allIPs = slices.Compact(allIPs)
allIPs = common.SliceCompact(allIPs)
allIPs = slices.DeleteFunc(allIPs, func(path string) bool { return path == anyStr || slices.Contains(p.allGroupsPaths, path) })
// create the blocks:
for _, ip := range allIPs {
Expand Down Expand Up @@ -117,7 +116,40 @@ func (p *nsxConfigParser) getExternalIPs() {
for _, externalIP := range p.configRes.externalIPs {
if externalIP.(*topology.ExternalIP).Block.IsSubset(ruleBlock.Block) {
ruleBlock.ExternalIPs = append(ruleBlock.ExternalIPs, externalIP)
p.ruleBlockPerEP[externalIP] = append(p.ruleBlockPerEP[externalIP], ruleBlock)
}
}
}
}

func (p *nsxConfigParser) getRuleBlocksVMs() {
for _, block := range p.topology.allRuleIPBlocks {
// iterate over VMs, look if the vm address is in the block:
for _, vm := range p.configRes.VMs {
for _, address := range vm.(*topology.VM).IPAddresses() {
address, err := netset.IPBlockFromIPAddress(address)
if err != nil {
logging.Warnf("Could not resolve address %s of vm %s", address, vm.Name())
continue
}
if address.IsSubset(block.Block) {
block.VMs = append(block.VMs, vm)
p.ruleBlockPerEP[vm] = append(p.ruleBlockPerEP[vm], block)
}
}
}
// iterate over segments, if segment is in the block, add all its vms
for _, segment := range p.topology.segments {
if segment.Block.IsSubset(block.Block) {
block.VMs = append(block.VMs, segment.VMs...)
for _, vm := range segment.VMs {
p.ruleBlockPerEP[vm] = append(p.ruleBlockPerEP[vm], block)
}
}
}
block.VMs = common.SliceCompact(block.VMs)
}
for _, vm := range p.configRes.VMs {
p.ruleBlockPerEP[vm] = common.SliceCompact(p.ruleBlockPerEP[vm])
}
}
41 changes: 41 additions & 0 deletions pkg/data/examples.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,47 @@ var Example1d = Example{
},
},
}
var Example1dExternalWithSegments = Example{
Name: "Example1dExternalWithSegments",
VMs: []string{"A", "B", "C-no-address"},
VMsAddress: map[string]string{
"A": "0.0.1.0",
"B": "0.0.1.192",
},
SegmentsByVMs: map[string][]string{
"seg_a_and_b": {"A", "B"},
"seg_c": {"C-no-address"},
},
SegmentsBlock: map[string]string{
"seg_a_and_b": "0.0.1.0/24",
"seg_c": "0.0.2.0/24",
},
Policies: []Category{
{
Name: "app-x",
CategoryType: "Application",
Rules: []Rule{
{
Name: "allow_smb_a_to_b",
ID: 1004,
Source: "0.0.1.0/25",
Dest: "0.0.1.128/25",
Services: []string{"/infra/services/SMB"},
Action: Allow,
},
{
Name: "allow_https_b_to_c",
ID: 1005,
Source: "0.0.1.128/25",
Dest: "0.0.2.0/24",
Services: []string{"/infra/services/HTTPS"},
Action: Allow,
},
DefaultDenyRule(denyRuleIDApp),
},
},
},
}

var Example1External = Example{
Name: "Example1External",
Expand Down
47 changes: 42 additions & 5 deletions pkg/data/examples_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ import (
// Example is in s single domain
type Example struct {
// nsx config spec fields below
VMs []string
VMsTags map[string][]nsx.Tag
GroupsByVMs map[string][]string
GroupsByExpr map[string]ExampleExpr // map from group name to its expr
Policies []Category
VMs []string
VMsTags map[string][]nsx.Tag
VMsAddress map[string]string
GroupsByVMs map[string][]string
SegmentsByVMs map[string][]string
SegmentsBlock map[string]string
GroupsByExpr map[string]ExampleExpr // map from group name to its expr
Policies []Category

// additional info about example, relevant for synthesis
DisjointGroupsTags [][]string
Expand Down Expand Up @@ -49,6 +52,40 @@ func ExamplesGeneration(e *Example, overrideJSON bool) (*collector.ResourcesCont
}
res.VirtualMachineList = append(res.VirtualMachineList, newVMRes)
}
for segmentName, ip := range e.SegmentsBlock {
segment := collector.Segment{
Segment: nsx.Segment{
UniqueId: &segmentName,
DisplayName: &segmentName,
Subnets: []nsx.SegmentSubnet{{Network: &ip}},
Path: &segmentName,
},
}
for _, vm := range e.SegmentsByVMs[segmentName] {
portName := "port_" + vm
port := collector.SegmentPort{
SegmentPort: nsx.SegmentPort{
DisplayName: &portName,
UniqueId: &portName,
ParentPath: &segmentName,
Attachment: &nsx.PortAttachment{
Id: &portName,
},
},
}
vmAddress := nsx.IPAddress(e.VMsAddress[vm])
vni := collector.VirtualNetworkInterface{
VirtualNetworkInterface: nsx.VirtualNetworkInterface{
LportAttachmentId: &portName,
OwnerVmId: &vm,
IpAddressInfo: []nsx.IpAddressInfo{{IpAddresses: []nsx.IPAddress{vmAddress}}},
},
}
res.VirtualNetworkInterfaceList = append(res.VirtualNetworkInterfaceList, vni)
segment.SegmentPorts = append(segment.SegmentPorts, port)
}
res.SegmentList = append(res.SegmentList, segment)
}
// set default domain
domainRsc := collector.Domain{}
defaultName := "default"
Expand Down
Loading