diff --git a/known-servewr.yaml b/known-servewr.yaml new file mode 100644 index 000000000..d4bf4aa62 --- /dev/null +++ b/known-servewr.yaml @@ -0,0 +1,13 @@ +apiVersion: spdx.softwarecomposition.kubescape.io/v1beta1 +kind: KnownServer +metadata: + name: armosec +spec: +- ipBlock: 16.170.0.0/15 + name: armo + server: cloud.io +- ipBlock: 13.50.180.111/24 + name: armo + server: cloud.io + + \ No newline at end of file diff --git a/pkg/apis/softwarecomposition/networkpolicy/labels.go b/pkg/apis/softwarecomposition/networkpolicy/labels.go index 5f5a4127f..180ec6b49 100644 --- a/pkg/apis/softwarecomposition/networkpolicy/labels.go +++ b/pkg/apis/softwarecomposition/networkpolicy/labels.go @@ -24,6 +24,6 @@ func init() { } // IsIgnoredLabel returns true if the label is ignored -func isIgnoredLabel(label string) bool { +func IsIgnoredLabel(label string) bool { return ignoreLabels[label] } diff --git a/pkg/apis/softwarecomposition/networkpolicy/networkpolicy.go b/pkg/apis/softwarecomposition/networkpolicy/v1/networkpolicy.go similarity index 99% rename from pkg/apis/softwarecomposition/networkpolicy/networkpolicy.go rename to pkg/apis/softwarecomposition/networkpolicy/v1/networkpolicy.go index 7837633e2..f722600f9 100644 --- a/pkg/apis/softwarecomposition/networkpolicy/networkpolicy.go +++ b/pkg/apis/softwarecomposition/networkpolicy/v1/networkpolicy.go @@ -15,6 +15,7 @@ import ( "github.com/kubescape/go-logger" "github.com/kubescape/go-logger/helpers" "github.com/kubescape/storage/pkg/apis/softwarecomposition" + "github.com/kubescape/storage/pkg/apis/softwarecomposition/networkpolicy" "golang.org/x/exp/maps" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -24,8 +25,6 @@ const ( storageV1ApiVersion = "spdx.softwarecomposition.kubescape.io" ) -// FIXME switch to NetworkNeighborhood - func GenerateNetworkPolicy(networkNeighbors softwarecomposition.NetworkNeighbors, knownServers []softwarecomposition.KnownServer, timeProvider metav1.Time) (softwarecomposition.GeneratedNetworkPolicy, error) { if !IsAvailable(networkNeighbors) { return softwarecomposition.GeneratedNetworkPolicy{}, fmt.Errorf("networkNeighbors %s/%s status annotation is not ready", networkNeighbors.Namespace, networkNeighbors.Name) @@ -453,7 +452,7 @@ func getSingleIP(ipAddress string) *softwarecomposition.IPBlock { func removeLabels(labels map[string]string) { for key := range labels { - if isIgnoredLabel(key) { + if networkpolicy.IsIgnoredLabel(key) { delete(labels, key) } } diff --git a/pkg/apis/softwarecomposition/networkpolicy/networkpolicy_test.go b/pkg/apis/softwarecomposition/networkpolicy/v1/networkpolicy_test.go similarity index 100% rename from pkg/apis/softwarecomposition/networkpolicy/networkpolicy_test.go rename to pkg/apis/softwarecomposition/networkpolicy/v1/networkpolicy_test.go diff --git a/pkg/apis/softwarecomposition/networkpolicy/v2/networkpolicy.go b/pkg/apis/softwarecomposition/networkpolicy/v2/networkpolicy.go new file mode 100644 index 000000000..ad236ac8c --- /dev/null +++ b/pkg/apis/softwarecomposition/networkpolicy/v2/networkpolicy.go @@ -0,0 +1,507 @@ +package networkpolicy + +import ( + "bytes" + "crypto/sha256" + "encoding/gob" + "encoding/hex" + "fmt" + "net" + "sort" + "strings" + + helpersv1 "github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers" + "github.com/kubescape/storage/pkg/apis/softwarecomposition" + "github.com/kubescape/storage/pkg/apis/softwarecomposition/networkpolicy" + + "github.com/kubescape/go-logger" + "github.com/kubescape/go-logger/helpers" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + storageV1ApiVersion = "spdx.softwarecomposition.kubescape.io" +) + +func GenerateNetworkPolicy(nn *softwarecomposition.NetworkNeighborhood, knownServers []softwarecomposition.KnownServer, timeProvider metav1.Time) (softwarecomposition.GeneratedNetworkPolicy, error) { + if !IsAvailable(nn) { + return softwarecomposition.GeneratedNetworkPolicy{}, fmt.Errorf("nn %s/%s status annotation is not ready", nn.Namespace, nn.Name) + } + + // get name from labels and clean labels + kind, ok := nn.Labels[helpersv1.KindMetadataKey] + if !ok { + return softwarecomposition.GeneratedNetworkPolicy{}, fmt.Errorf("nn %s/%s does not have a kind label", nn.Namespace, nn.Name) + } + name, ok := nn.Labels[helpersv1.NameMetadataKey] + if !ok { + return softwarecomposition.GeneratedNetworkPolicy{}, fmt.Errorf("nn %s/%s does not have a name label", nn.Namespace, nn.Name) + } + delete(nn.Labels, helpersv1.TemplateHashKey) + + networkPolicy := softwarecomposition.NetworkPolicy{ + Kind: "NetworkPolicy", + APIVersion: "networking.k8s.io/v1", + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%s", strings.ToLower(kind), name), + Namespace: nn.Namespace, + Annotations: map[string]string{ + "generated-by": "kubescape", + }, + Labels: nn.Labels, + }, + Spec: softwarecomposition.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{}, + PolicyTypes: []softwarecomposition.PolicyType{ + softwarecomposition.PolicyTypeIngress, + softwarecomposition.PolicyTypeEgress, + }, + }, + } + + if nn.Spec.MatchLabels != nil { + networkPolicy.Spec.PodSelector.MatchLabels = nn.Spec.MatchLabels + } + + if nn.Spec.MatchExpressions != nil { + networkPolicy.Spec.PodSelector.MatchExpressions = nn.Spec.MatchExpressions + } + + generatedNetworkPolicy := softwarecomposition.GeneratedNetworkPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: "GeneratedNetworkPolicy", + APIVersion: storageV1ApiVersion, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: nn.Name, + Namespace: nn.Namespace, + Labels: nn.Labels, + CreationTimestamp: timeProvider, + }, + PoliciesRef: []softwarecomposition.PolicyRef{}, + } + + ingressHash := make(map[string]bool) + for _, neighbor := range listIngressNetworkNeighbors(nn) { + + rule, policyRefs := generateIngressRule(neighbor, knownServers) + + if ruleHash, err := hash(rule); err == nil { + if ok := ingressHash[ruleHash]; !ok { + networkPolicy.Spec.Ingress = append(networkPolicy.Spec.Ingress, rule) + ingressHash[ruleHash] = true + } + } + + if refsHash, err := hash(policyRefs); err == nil { + if ok := ingressHash[refsHash]; !ok { + generatedNetworkPolicy.PoliciesRef = append(generatedNetworkPolicy.PoliciesRef, policyRefs...) + ingressHash[refsHash] = true + } + } + + } + + egressHash := make(map[string]bool) + for _, neighbor := range listEgressNetworkNeighbors(nn) { + + rule, policyRefs := generateEgressRule(neighbor, knownServers) + + if ruleHash, err := hash(rule); err == nil { + if ok := egressHash[ruleHash]; !ok { + networkPolicy.Spec.Egress = append(networkPolicy.Spec.Egress, rule) + egressHash[ruleHash] = true + } + } + + for i := range policyRefs { + if refsHash, err := hash(policyRefs[i]); err == nil { + if ok := egressHash[refsHash]; !ok { + generatedNetworkPolicy.PoliciesRef = append(generatedNetworkPolicy.PoliciesRef, policyRefs[i]) + egressHash[refsHash] = true + } + } + } + } + + networkPolicy.Spec.Egress = mergeEgressRulesByPorts(networkPolicy.Spec.Egress) + + networkPolicy.Spec.Ingress = mergeIngressRulesByPorts(networkPolicy.Spec.Ingress) + + generatedNetworkPolicy.Spec = networkPolicy + + return generatedNetworkPolicy, nil +} + +func listIngressNetworkNeighbors(nn *softwarecomposition.NetworkNeighborhood) []softwarecomposition.NetworkNeighbor { + var neighbors []softwarecomposition.NetworkNeighbor + for i := range nn.Spec.Containers { + neighbors = append(neighbors, nn.Spec.Containers[i].Ingress...) + } + for i := range nn.Spec.InitContainers { + neighbors = append(neighbors, nn.Spec.InitContainers[i].Ingress...) + } + for i := range nn.Spec.EphemeralContainers { + neighbors = append(neighbors, nn.Spec.EphemeralContainers[i].Ingress...) + } + return neighbors + +} + +func listEgressNetworkNeighbors(nn *softwarecomposition.NetworkNeighborhood) []softwarecomposition.NetworkNeighbor { + var neighbors []softwarecomposition.NetworkNeighbor + for i := range nn.Spec.Containers { + neighbors = append(neighbors, nn.Spec.Containers[i].Egress...) + } + for i := range nn.Spec.InitContainers { + neighbors = append(neighbors, nn.Spec.InitContainers[i].Egress...) + } + for i := range nn.Spec.EphemeralContainers { + neighbors = append(neighbors, nn.Spec.EphemeralContainers[i].Egress...) + } + return neighbors + +} + +func mergeIngressRulesByPorts(rules []softwarecomposition.NetworkPolicyIngressRule) []softwarecomposition.NetworkPolicyIngressRule { + type PortProtocolKey struct { + Port int32 + Protocol v1.Protocol + } + + merged := make(map[PortProtocolKey][]softwarecomposition.NetworkPolicyPeer) + var keys []PortProtocolKey + var nonMergedRules []softwarecomposition.NetworkPolicyIngressRule + + for _, rule := range rules { + hasSelector := false + for _, peer := range rule.From { + if peer.PodSelector != nil || peer.NamespaceSelector != nil { + hasSelector = true + break + } + } + + if hasSelector { + nonMergedRules = append(nonMergedRules, rule) + continue + } + + for _, port := range rule.Ports { + if port.Port == nil || port.Protocol == nil { + continue + } + key := PortProtocolKey{Port: *port.Port, Protocol: *port.Protocol} + if _, exists := merged[key]; !exists { + keys = append(keys, key) + } + for _, peer := range rule.From { + if peer.IPBlock != nil { + merged[key] = append(merged[key], peer) + } + } + } + } + + // Sort the keys + sort.Slice(keys, func(i, j int) bool { + if keys[i].Port != keys[j].Port { + return keys[i].Port < keys[j].Port + } + return keys[i].Protocol < keys[j].Protocol + }) + + // Construct merged rules using sorted keys + mergedRules := []softwarecomposition.NetworkPolicyIngressRule{} + for i := range keys { + peers := merged[keys[i]] + sort.Slice(peers, func(i, j int) bool { + if peers[i].IPBlock != nil && peers[j].IPBlock != nil { + return peers[i].IPBlock.CIDR < peers[j].IPBlock.CIDR + } + return false // Keep the order as is if softwarecomposition.IPBlock is nil + }) + + mergedRules = append(mergedRules, softwarecomposition.NetworkPolicyIngressRule{ + Ports: []softwarecomposition.NetworkPolicyPort{{Protocol: &keys[i].Protocol, Port: &keys[i].Port}}, + From: peers, + }) + } + + // Combine merged and non-merged rules + mergedRules = append(mergedRules, nonMergedRules...) + + return mergedRules +} + +func mergeEgressRulesByPorts(rules []softwarecomposition.NetworkPolicyEgressRule) []softwarecomposition.NetworkPolicyEgressRule { + type PortProtocolKey struct { + Port int32 + Protocol v1.Protocol + } + + merged := make(map[PortProtocolKey][]softwarecomposition.NetworkPolicyPeer) + var keys []PortProtocolKey + var nonMergedRules []softwarecomposition.NetworkPolicyEgressRule + + for _, rule := range rules { + hasSelector := false + for _, peer := range rule.To { + if peer.PodSelector != nil || peer.NamespaceSelector != nil { + hasSelector = true + break + } + } + + if hasSelector { + nonMergedRules = append(nonMergedRules, rule) + continue + } + + for _, port := range rule.Ports { + key := PortProtocolKey{Port: *port.Port, Protocol: *port.Protocol} + if _, exists := merged[key]; !exists { + keys = append(keys, key) + } + for _, peer := range rule.To { + if peer.IPBlock != nil { + merged[key] = append(merged[key], peer) + } + } + } + } + + // Sort the keys + sort.Slice(keys, func(i, j int) bool { + if keys[i].Port != keys[j].Port { + return keys[i].Port < keys[j].Port + } + return keys[i].Protocol < keys[j].Protocol + }) + + // Construct merged rules using sorted keys + mergedRules := []softwarecomposition.NetworkPolicyEgressRule{} + for i := range keys { + peers := merged[keys[i]] + sort.Slice(peers, func(i, j int) bool { + if peers[i].IPBlock != nil && peers[j].IPBlock != nil { + return peers[i].IPBlock.CIDR < peers[j].IPBlock.CIDR + } + return false // Keep the order as is if softwarecomposition.IPBlock is nil + }) + + mergedRules = append(mergedRules, softwarecomposition.NetworkPolicyEgressRule{ + Ports: []softwarecomposition.NetworkPolicyPort{{Protocol: &keys[i].Protocol, Port: &keys[i].Port}}, + To: peers, + }) + } + + // Combine merged and non-merged rules + mergedRules = append(mergedRules, nonMergedRules...) + + return mergedRules +} + +func generateEgressRule(neighbor softwarecomposition.NetworkNeighbor, knownServers []softwarecomposition.KnownServer) (softwarecomposition.NetworkPolicyEgressRule, []softwarecomposition.PolicyRef) { + egressRule := softwarecomposition.NetworkPolicyEgressRule{} + policyRefs := []softwarecomposition.PolicyRef{} + + if neighbor.PodSelector != nil { + removeLabels(neighbor.PodSelector.MatchLabels) + egressRule.To = append(egressRule.To, softwarecomposition.NetworkPolicyPeer{ + PodSelector: neighbor.PodSelector, + }) + } + + if neighbor.NamespaceSelector != nil { + // the ns label goes together with the pod label + if len(egressRule.To) > 0 { + egressRule.To[0].NamespaceSelector = neighbor.NamespaceSelector + } else { + egressRule.To = append(egressRule.To, softwarecomposition.NetworkPolicyPeer{ + NamespaceSelector: neighbor.NamespaceSelector, + }) + } + } + + if neighbor.IPAddress != "" { + isKnownServer := false + // look if this IP is part of any known server + for _, knownServer := range knownServers { + for _, entry := range knownServer.Spec { + _, subNet, err := net.ParseCIDR(entry.IPBlock) + if err != nil { + logger.L().Error("error parsing cidr", helpers.Error(err)) + continue + } + if subNet.Contains(net.ParseIP(neighbor.IPAddress)) { + egressRule.To = append(egressRule.To, softwarecomposition.NetworkPolicyPeer{ + IPBlock: &softwarecomposition.IPBlock{ + CIDR: entry.IPBlock, + }, + }) + isKnownServer = true + + policyRef := softwarecomposition.PolicyRef{ + Name: entry.Name, + OriginalIP: neighbor.IPAddress, + IPBlock: entry.IPBlock, + Server: entry.Server, + } + + if neighbor.DNS != "" { + policyRef.DNS = neighbor.DNS + } + + policyRefs = append(policyRefs, policyRef) + break + } + } + } + + if !isKnownServer { + ipBlock := getSingleIP(neighbor.IPAddress) + egressRule.To = append(egressRule.To, softwarecomposition.NetworkPolicyPeer{ + IPBlock: ipBlock, + }) + + if neighbor.DNS != "" { + policyRefs = append(policyRefs, softwarecomposition.PolicyRef{ + DNS: neighbor.DNS, + IPBlock: ipBlock.CIDR, + OriginalIP: neighbor.IPAddress, + }) + } + } + } + + for _, networkPort := range neighbor.Ports { + protocol := v1.Protocol(strings.ToUpper(string(networkPort.Protocol))) + portInt32 := networkPort.Port + + egressRule.Ports = append(egressRule.Ports, softwarecomposition.NetworkPolicyPort{ + Protocol: &protocol, + Port: portInt32, + }) + } + + return egressRule, policyRefs +} + +func generateIngressRule(neighbor softwarecomposition.NetworkNeighbor, knownServers []softwarecomposition.KnownServer) (softwarecomposition.NetworkPolicyIngressRule, []softwarecomposition.PolicyRef) { + ingressRule := softwarecomposition.NetworkPolicyIngressRule{} + policyRefs := []softwarecomposition.PolicyRef{} + + if neighbor.PodSelector != nil { + removeLabels(neighbor.PodSelector.MatchLabels) + ingressRule.From = append(ingressRule.From, softwarecomposition.NetworkPolicyPeer{ + PodSelector: neighbor.PodSelector, + }) + } + if neighbor.NamespaceSelector != nil { + // the ns label goes together with the pod label + if len(ingressRule.From) > 0 { + ingressRule.From[0].NamespaceSelector = neighbor.NamespaceSelector + } else { + ingressRule.From = append(ingressRule.From, softwarecomposition.NetworkPolicyPeer{ + NamespaceSelector: neighbor.NamespaceSelector, + }) + } + } + + if neighbor.IPAddress != "" { + isKnownServer := false + // look if this IP is part of any known server + for _, knownServer := range knownServers { + for _, entry := range knownServer.Spec { + _, subNet, err := net.ParseCIDR(entry.IPBlock) + if err != nil { + logger.L().Error("error parsing cidr", helpers.Error(err)) + continue + } + if subNet.Contains(net.ParseIP(neighbor.IPAddress)) { + ingressRule.From = append(ingressRule.From, softwarecomposition.NetworkPolicyPeer{ + IPBlock: &softwarecomposition.IPBlock{ + CIDR: entry.IPBlock, + }, + }) + isKnownServer = true + + policyRef := softwarecomposition.PolicyRef{ + Name: entry.Name, + OriginalIP: neighbor.IPAddress, + IPBlock: entry.IPBlock, + Server: entry.Server, + } + + if neighbor.DNS != "" { + policyRef.DNS = neighbor.DNS + } + + policyRefs = append(policyRefs, policyRef) + break + } + } + } + + if !isKnownServer { + ipBlock := getSingleIP(neighbor.IPAddress) + ingressRule.From = append(ingressRule.From, softwarecomposition.NetworkPolicyPeer{ + IPBlock: ipBlock, + }) + + if neighbor.DNS != "" { + policyRefs = append(policyRefs, softwarecomposition.PolicyRef{ + DNS: neighbor.DNS, + IPBlock: ipBlock.CIDR, + OriginalIP: neighbor.IPAddress, + }) + } + } + } + + for _, networkPort := range neighbor.Ports { + protocol := v1.Protocol(strings.ToUpper(string(networkPort.Protocol))) + portInt32 := networkPort.Port + + ingressRule.Ports = append(ingressRule.Ports, softwarecomposition.NetworkPolicyPort{ + Protocol: &protocol, + Port: portInt32, + }) + } + + return ingressRule, policyRefs +} + +func getSingleIP(ipAddress string) *softwarecomposition.IPBlock { + ipBlock := &softwarecomposition.IPBlock{CIDR: ipAddress + "/32"} + return ipBlock +} + +func removeLabels(labels map[string]string) { + for key := range labels { + if networkpolicy.IsIgnoredLabel(key) { + delete(labels, key) + } + } +} + +func IsAvailable(nn *softwarecomposition.NetworkNeighborhood) bool { + switch nn.GetAnnotations()[helpersv1.StatusMetadataKey] { + case helpersv1.Ready, helpersv1.Completed: + return true + default: + return false + } +} + +func hash(s any) (string, error) { + + var b bytes.Buffer + if err := gob.NewEncoder(&b).Encode(s); err != nil { + return "", err + } + vv := sha256.Sum256(b.Bytes()) + return hex.EncodeToString(vv[:]), nil +} diff --git a/pkg/apis/softwarecomposition/networkpolicy/v2/networkpolicy_test.go b/pkg/apis/softwarecomposition/networkpolicy/v2/networkpolicy_test.go new file mode 100644 index 000000000..fcfe69453 --- /dev/null +++ b/pkg/apis/softwarecomposition/networkpolicy/v2/networkpolicy_test.go @@ -0,0 +1,1995 @@ +package networkpolicy + +import ( + _ "embed" + "encoding/json" + "fmt" + "reflect" + "slices" + "testing" + + helpersv1 "github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers" + "github.com/kubescape/storage/pkg/apis/softwarecomposition" + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" +) + +func TestGenerateNetworkPolicy(t *testing.T) { + timeProvider := metav1.Now() + protocolTCP := v1.ProtocolTCP + + tests := []struct { + name string + networkNeighborhood softwarecomposition.NetworkNeighborhood + knownServers []softwarecomposition.KnownServer + expectedNetworkPolicy softwarecomposition.GeneratedNetworkPolicy + expectError bool + }{ + { + name: "basic ingress rule", + networkNeighborhood: softwarecomposition.NetworkNeighborhood{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-nginx", + Namespace: "kubescape", + Annotations: map[string]string{ + helpersv1.StatusMetadataKey: helpersv1.Ready, + }, + Labels: map[string]string{ + helpersv1.KindMetadataKey: "Deployment", + helpersv1.NameMetadataKey: "nginx", + }, + }, + Spec: softwarecomposition.NetworkNeighborhoodSpec{ + LabelSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "nginx", + }, + }, + Containers: []softwarecomposition.NetworkNeighborhoodContainer{ + { + Ingress: []softwarecomposition.NetworkNeighbor{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "nginx", + }, + }, + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptrToInt32(80), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-80", + }, + }, + }, + }, + }, + }, + }, + }, + expectedNetworkPolicy: softwarecomposition.GeneratedNetworkPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: "GeneratedNetworkPolicy", + APIVersion: "spdx.softwarecomposition.kubescape.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-nginx", + Namespace: "kubescape", + Labels: nil, + CreationTimestamp: timeProvider, + }, + PoliciesRef: []softwarecomposition.PolicyRef{}, + Spec: softwarecomposition.NetworkPolicy{ + Kind: "NetworkPolicy", + APIVersion: "networking.k8s.io/v1", + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-nginx", + Namespace: "kubescape", + Annotations: map[string]string{ + "generated-by": "kubescape", + }, + }, + Spec: softwarecomposition.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "nginx", + }, + }, + PolicyTypes: []softwarecomposition.PolicyType{ + softwarecomposition.PolicyTypeIngress, + softwarecomposition.PolicyTypeEgress, + }, + Ingress: []softwarecomposition.NetworkPolicyIngressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "nginx", + }, + }, + }, + }, + }, + }, + Egress: []softwarecomposition.NetworkPolicyEgressRule{}, + }, + }, + }, + expectError: false, + }, + { + name: "network neighborhood not ready", + networkNeighborhood: softwarecomposition.NetworkNeighborhood{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-nginx", + Namespace: "kubescape", + Annotations: map[string]string{ + helpersv1.StatusMetadataKey: "not-ready", + }, + Labels: map[string]string{ + helpersv1.KindMetadataKey: "Deployment", + helpersv1.NameMetadataKey: "nginx", + }, + }, + Spec: softwarecomposition.NetworkNeighborhoodSpec{}, + }, + expectedNetworkPolicy: softwarecomposition.GeneratedNetworkPolicy{}, + expectError: true, + }, + { + name: "network_policy_with_multiple_ports_and_labels", + networkNeighborhood: softwarecomposition.NetworkNeighborhood{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-multi", + Namespace: "kubescape", + Annotations: map[string]string{ + helpersv1.StatusMetadataKey: helpersv1.Ready, + }, + Labels: map[string]string{ + helpersv1.KindMetadataKey: "Deployment", + helpersv1.NameMetadataKey: "multi", + }, + }, + Spec: softwarecomposition.NetworkNeighborhoodSpec{ + LabelSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "multi-app", + "tier": "frontend", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "environment", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"prod", "staging"}, + }, + }, + }, + Containers: []softwarecomposition.NetworkNeighborhoodContainer{ + { + Ingress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "10.0.0.1", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(80)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-80", + }, + { + Port: ptr.To(int32(443)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-443", + }, + }, + }, + }, + Egress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "192.168.1.1", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(8080)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-8080", + }, + }, + }, + }, + }, + }, + }, + }, + expectedNetworkPolicy: softwarecomposition.GeneratedNetworkPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: "GeneratedNetworkPolicy", + APIVersion: "spdx.softwarecomposition.kubescape.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-multi", + Namespace: "kubescape", + CreationTimestamp: timeProvider, + }, + Spec: softwarecomposition.NetworkPolicy{ + Kind: "NetworkPolicy", + APIVersion: "networking.k8s.io/v1", + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-multi", + Namespace: "kubescape", + Annotations: map[string]string{ + "generated-by": "kubescape", + }, + }, + Spec: softwarecomposition.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "multi-app", + "tier": "frontend", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "environment", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"prod", "staging"}, + }, + }, + }, + PolicyTypes: []softwarecomposition.PolicyType{ + softwarecomposition.PolicyTypeIngress, + softwarecomposition.PolicyTypeEgress, + }, + Ingress: []softwarecomposition.NetworkPolicyIngressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptr.To(int32(80)), + Protocol: &protocolTCP, + }, + { + Port: ptr.To(int32(443)), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.1/32", + }, + }, + }, + }, + }, + Egress: []softwarecomposition.NetworkPolicyEgressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptr.To(int32(8080)), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "192.168.1.1/32", + }, + }, + }, + }, + }, + }, + }, + PoliciesRef: []softwarecomposition.PolicyRef{}, + }, + }, + { + name: "policy_with_known_servers", + networkNeighborhood: softwarecomposition.NetworkNeighborhood{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-known-servers", + Namespace: "kubescape", + Annotations: map[string]string{ + helpersv1.StatusMetadataKey: helpersv1.Ready, + }, + Labels: map[string]string{ + helpersv1.KindMetadataKey: "Deployment", + helpersv1.NameMetadataKey: "known-servers", + }, + }, + Spec: softwarecomposition.NetworkNeighborhoodSpec{ + LabelSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "known-app", + }, + }, + Containers: []softwarecomposition.NetworkNeighborhoodContainer{ + { + Ingress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "10.0.0.1", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptrToInt32(80), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-80", + }, + }, + }, + }, + }, + { + Egress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "192.168.1.1", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptrToInt32(8080), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-8080", + }, + }, + }, + }, + }, + }, + }, + }, + knownServers: []softwarecomposition.KnownServer{ + { + Spec: softwarecomposition.KnownServerSpec{ + { + IPBlock: "10.0.0.0/8", + Name: "known-server-1", + Server: "server-1", + }, + { + IPBlock: "192.168.0.0/16", + Name: "known-server-2", + Server: "server-2", + }, + }, + }, + }, + expectedNetworkPolicy: softwarecomposition.GeneratedNetworkPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: "GeneratedNetworkPolicy", + APIVersion: "spdx.softwarecomposition.kubescape.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-known-servers", + Namespace: "kubescape", + CreationTimestamp: timeProvider, + }, + Spec: softwarecomposition.NetworkPolicy{ + Kind: "NetworkPolicy", + APIVersion: "networking.k8s.io/v1", + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-known-servers", + Namespace: "kubescape", + Annotations: map[string]string{ + "generated-by": "kubescape", + }, + }, + Spec: softwarecomposition.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "known-app", + }, + }, + PolicyTypes: []softwarecomposition.PolicyType{ + softwarecomposition.PolicyTypeIngress, + softwarecomposition.PolicyTypeEgress, + }, + Ingress: []softwarecomposition.NetworkPolicyIngressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.0/8", + }, + }, + }, + }, + }, + Egress: []softwarecomposition.NetworkPolicyEgressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(8080), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "192.168.0.0/16", + }, + }, + }, + }, + }, + }, + }, + PoliciesRef: []softwarecomposition.PolicyRef{ + { + Name: "known-server-1", + OriginalIP: "10.0.0.1", + IPBlock: "10.0.0.0/8", + Server: "server-1", + }, + { + Name: "known-server-2", + OriginalIP: "192.168.1.1", + IPBlock: "192.168.0.0/16", + Server: "server-2", + }, + }, + }, + expectError: false, + }, + { + name: "policy_with_known_servers", + networkNeighborhood: softwarecomposition.NetworkNeighborhood{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-known-servers", + Namespace: "kubescape", + Annotations: map[string]string{ + helpersv1.StatusMetadataKey: helpersv1.Ready, + }, + Labels: map[string]string{ + helpersv1.KindMetadataKey: "Deployment", + helpersv1.NameMetadataKey: "known-servers", + }, + }, + Spec: softwarecomposition.NetworkNeighborhoodSpec{ + LabelSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "known-app", + }, + }, + Containers: []softwarecomposition.NetworkNeighborhoodContainer{ + { + Ingress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "10.0.0.1", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptrToInt32(80), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-80", + }, + }, + }, + }, + }, + { + Egress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "192.168.1.1", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptrToInt32(8080), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-8080", + }, + }, + }, + }, + }, + }, + }, + }, + knownServers: []softwarecomposition.KnownServer{ + { + Spec: softwarecomposition.KnownServerSpec{ + { + IPBlock: "10.0.0.0/8", + Name: "known-server-1", + Server: "server-1", + }, + { + IPBlock: "192.168.0.0/16", + Name: "known-server-2", + Server: "server-2", + }, + }, + }, + }, + expectedNetworkPolicy: softwarecomposition.GeneratedNetworkPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: "GeneratedNetworkPolicy", + APIVersion: "spdx.softwarecomposition.kubescape.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-known-servers", + Namespace: "kubescape", + CreationTimestamp: timeProvider, + }, + Spec: softwarecomposition.NetworkPolicy{ + Kind: "NetworkPolicy", + APIVersion: "networking.k8s.io/v1", + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-known-servers", + Namespace: "kubescape", + Annotations: map[string]string{ + "generated-by": "kubescape", + }, + }, + Spec: softwarecomposition.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "known-app", + }, + }, + PolicyTypes: []softwarecomposition.PolicyType{ + softwarecomposition.PolicyTypeIngress, + softwarecomposition.PolicyTypeEgress, + }, + Ingress: []softwarecomposition.NetworkPolicyIngressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.0/8", + }, + }, + }, + }, + }, + Egress: []softwarecomposition.NetworkPolicyEgressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(8080), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "192.168.0.0/16", + }, + }, + }, + }, + }, + }, + }, + PoliciesRef: []softwarecomposition.PolicyRef{ + { + Name: "known-server-1", + OriginalIP: "10.0.0.1", + IPBlock: "10.0.0.0/8", + Server: "server-1", + }, + { + Name: "known-server-2", + OriginalIP: "192.168.1.1", + IPBlock: "192.168.0.0/16", + Server: "server-2", + }, + }, + }, + expectError: false, + }, + { + name: "policy_with_dns_neighbors", + networkNeighborhood: softwarecomposition.NetworkNeighborhood{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-dns", + Namespace: "kubescape", + Annotations: map[string]string{ + helpersv1.StatusMetadataKey: helpersv1.Ready, + }, + Labels: map[string]string{ + helpersv1.KindMetadataKey: "Deployment", + helpersv1.NameMetadataKey: "dns", + }, + }, + Spec: softwarecomposition.NetworkNeighborhoodSpec{ + LabelSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "dns-app", + }, + }, + Containers: []softwarecomposition.NetworkNeighborhoodContainer{ + { + Egress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "192.168.1.1", + DNS: "example.com", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(80)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-80", + }, + }, + }, + }, + }, + }, + }, + }, + knownServers: []softwarecomposition.KnownServer{}, + expectedNetworkPolicy: softwarecomposition.GeneratedNetworkPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: "GeneratedNetworkPolicy", + APIVersion: "spdx.softwarecomposition.kubescape.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-dns", + Namespace: "kubescape", + CreationTimestamp: timeProvider, + }, + PoliciesRef: []softwarecomposition.PolicyRef{ + { + DNS: "example.com", + IPBlock: "192.168.1.1/32", + OriginalIP: "192.168.1.1", + }, + }, + Spec: softwarecomposition.NetworkPolicy{ + Kind: "NetworkPolicy", + APIVersion: "networking.k8s.io/v1", + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-dns", + Namespace: "kubescape", + Annotations: map[string]string{ + "generated-by": "kubescape", + }, + }, + Spec: softwarecomposition.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "dns-app", + }, + }, + PolicyTypes: []softwarecomposition.PolicyType{ + softwarecomposition.PolicyTypeIngress, + softwarecomposition.PolicyTypeEgress, + }, + Ingress: []softwarecomposition.NetworkPolicyIngressRule{}, + Egress: []softwarecomposition.NetworkPolicyEgressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptr.To(int32(80)), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "192.168.1.1/32", + }, + }, + }, + }, + }, + }, + }, + }, + expectError: false, + }, + { + name: "network_policy_with_multiple_containers", + networkNeighborhood: softwarecomposition.NetworkNeighborhood{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-multi-container", + Namespace: "kubescape", + Annotations: map[string]string{ + helpersv1.StatusMetadataKey: helpersv1.Ready, + }, + Labels: map[string]string{ + helpersv1.KindMetadataKey: "Deployment", + helpersv1.NameMetadataKey: "multi-container", + }, + }, + Spec: softwarecomposition.NetworkNeighborhoodSpec{ + LabelSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "multi-container", + }, + }, + Containers: []softwarecomposition.NetworkNeighborhoodContainer{ + { + Ingress: []softwarecomposition.NetworkNeighbor{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "nginx", + }, + }, + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptrToInt32(80), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-80", + }, + }, + }, + }, + }, + { + Ingress: []softwarecomposition.NetworkNeighbor{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "nginx", + }, + }, + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptrToInt32(443), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-443", + }, + }, + }, + }, + }, + }, + }, + }, + expectedNetworkPolicy: softwarecomposition.GeneratedNetworkPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: "GeneratedNetworkPolicy", + APIVersion: "spdx.softwarecomposition.kubescape.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-multi-container", + Namespace: "kubescape", + Labels: nil, + CreationTimestamp: timeProvider, + }, + PoliciesRef: []softwarecomposition.PolicyRef{}, + Spec: softwarecomposition.NetworkPolicy{ + Kind: "NetworkPolicy", + APIVersion: "networking.k8s.io/v1", + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-multi-container", + Namespace: "kubescape", + Annotations: map[string]string{ + "generated-by": "kubescape", + }, + }, + Spec: softwarecomposition.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "multi-container", + }, + }, + PolicyTypes: []softwarecomposition.PolicyType{ + softwarecomposition.PolicyTypeIngress, + softwarecomposition.PolicyTypeEgress, + }, + Ingress: []softwarecomposition.NetworkPolicyIngressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "nginx", + }, + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(443), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "nginx", + }, + }, + }, + }, + }, + }, + Egress: []softwarecomposition.NetworkPolicyEgressRule{}, + }, + }, + }, + expectError: false, + }, + { + name: "network_policy_with_multiple_containers_with_same_ip", + networkNeighborhood: softwarecomposition.NetworkNeighborhood{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-multi-containers", + Namespace: "kubescape", + Annotations: map[string]string{ + helpersv1.StatusMetadataKey: helpersv1.Ready, + }, + Labels: map[string]string{ + helpersv1.KindMetadataKey: "Deployment", + helpersv1.NameMetadataKey: "multi-containers", + }, + }, + Spec: softwarecomposition.NetworkNeighborhoodSpec{ + LabelSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "multi-container-app", + }, + }, + Containers: []softwarecomposition.NetworkNeighborhoodContainer{ + { + Name: "container-1", + Ingress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "10.0.0.1", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(80)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-80", + }, + { + Port: ptr.To(int32(443)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-443", + }, + }, + }, + }, + Egress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "192.168.1.1", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(8080)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-8080", + }, + }, + }, + }, + }, + { + Name: "container-2", + Ingress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "10.0.0.1", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(80)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-80", + }, + }, + }, + }, + Egress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "192.168.1.1", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(8080)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-8080", + }, + }, + }, + }, + }, + }, + }, + }, + expectedNetworkPolicy: softwarecomposition.GeneratedNetworkPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: "GeneratedNetworkPolicy", + APIVersion: "spdx.softwarecomposition.kubescape.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-multi-containers", + Namespace: "kubescape", + CreationTimestamp: timeProvider, + }, + PoliciesRef: []softwarecomposition.PolicyRef{}, + Spec: softwarecomposition.NetworkPolicy{ + Kind: "NetworkPolicy", + APIVersion: "networking.k8s.io/v1", + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-multi-containers", + Namespace: "kubescape", + Annotations: map[string]string{ + "generated-by": "kubescape", + }, + }, + Spec: softwarecomposition.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "multi-container-app", + }, + }, + PolicyTypes: []softwarecomposition.PolicyType{ + softwarecomposition.PolicyTypeIngress, + softwarecomposition.PolicyTypeEgress, + }, + Ingress: []softwarecomposition.NetworkPolicyIngressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptr.To(int32(80)), + Protocol: &protocolTCP, + }, + { + Port: ptr.To(int32(443)), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.1/32", + }, + }, + }, + }, + }, + Egress: []softwarecomposition.NetworkPolicyEgressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptr.To(int32(8080)), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "192.168.1.1/32", + }, + }, + }, + }, + }, + }, + }, + }, + expectError: false, + }, + { + name: "network_policy_with_multiple_different_containers", + networkNeighborhood: softwarecomposition.NetworkNeighborhood{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-multi-containers", + Namespace: "kubescape", + Annotations: map[string]string{ + helpersv1.StatusMetadataKey: helpersv1.Ready, + }, + Labels: map[string]string{ + helpersv1.KindMetadataKey: "Deployment", + helpersv1.NameMetadataKey: "multi-containers", + }, + }, + Spec: softwarecomposition.NetworkNeighborhoodSpec{ + LabelSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "multi-container-app", + }, + }, + Containers: []softwarecomposition.NetworkNeighborhoodContainer{ + { + Ingress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "10.0.0.1", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(80)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-80", + }, + { + Port: ptr.To(int32(443)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-443", + }, + }, + }, + }, + Egress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "192.168.1.1", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(8080)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-8080", + }, + }, + }, + }, + }, + { + Ingress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "10.0.0.2", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(8081)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-8081", + }, + }, + }, + }, + Egress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "192.168.1.2", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(8082)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-8082", + }, + }, + }, + }, + }, + }, + InitContainers: []softwarecomposition.NetworkNeighborhoodContainer{ + { + Ingress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "10.0.0.3", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(80)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-80", + }, + { + Port: ptr.To(int32(443)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-443", + }, + }, + }, + { + IPAddress: "10.0.0.1", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(90)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-90", + }, + { + Port: ptr.To(int32(443)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-443", + }, + }, + }, + }, + Egress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "192.168.1.1", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(8080)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-8080", + }, + }, + }, + }, + }, + { + Ingress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "10.0.0.2", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(8081)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-8081", + }, + }, + }, + }, + Egress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "192.168.1.2", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(8082)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-8082", + }, + }, + }, + }, + }, + }, + EphemeralContainers: []softwarecomposition.NetworkNeighborhoodContainer{ + { + Ingress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "10.0.0.4", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(80)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-80", + }, + { + Port: ptr.To(int32(443)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-443", + }, + }, + }, + { + IPAddress: "10.0.0.1", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(100)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-100", + }, + { + Port: ptr.To(int32(443)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-443", + }, + }, + }, + }, + Egress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "192.168.1.1", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(8080)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-8080", + }, + }, + }, + }, + }, + { + Ingress: []softwarecomposition.NetworkNeighbor{ + { + IPAddress: "10.0.0.2", + Ports: []softwarecomposition.NetworkPort{ + { + Port: ptr.To(int32(8081)), + Protocol: softwarecomposition.ProtocolTCP, + Name: "TCP-8081", + }, + }, + }, + }, + }, + }, + }, + }, + expectedNetworkPolicy: softwarecomposition.GeneratedNetworkPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: "GeneratedNetworkPolicy", + APIVersion: "spdx.softwarecomposition.kubescape.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-multi-containers", + Namespace: "kubescape", + CreationTimestamp: timeProvider, + }, + PoliciesRef: []softwarecomposition.PolicyRef{}, + Spec: softwarecomposition.NetworkPolicy{ + Kind: "NetworkPolicy", + APIVersion: "networking.k8s.io/v1", + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-multi-containers", + Namespace: "kubescape", + Annotations: map[string]string{ + "generated-by": "kubescape", + }, + }, + Spec: softwarecomposition.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "multi-container-app", + }, + }, + PolicyTypes: []softwarecomposition.PolicyType{ + softwarecomposition.PolicyTypeIngress, + softwarecomposition.PolicyTypeEgress, + }, + Ingress: []softwarecomposition.NetworkPolicyIngressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptr.To(int32(80)), + Protocol: &protocolTCP, + }, + { + Port: ptr.To(int32(90)), + Protocol: &protocolTCP, + }, + { + Port: ptr.To(int32(100)), + Protocol: &protocolTCP, + }, + { + Port: ptr.To(int32(443)), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.1/32", + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptr.To(int32(80)), + Protocol: &protocolTCP, + }, + { + Port: ptr.To(int32(443)), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.3/32", + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptr.To(int32(80)), + Protocol: &protocolTCP, + }, + { + Port: ptr.To(int32(443)), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.4/32", + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptr.To(int32(8081)), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + }, + Egress: []softwarecomposition.NetworkPolicyEgressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptr.To(int32(8080)), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "192.168.1.1/32", + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptr.To(int32(8082)), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "192.168.1.2/32", + }, + }, + }, + }, + }, + }, + }, + }, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GenerateNetworkPolicy(&tt.networkNeighborhood, tt.knownServers, timeProvider) + + if tt.expectError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Nil(t, compareNP(&tt.expectedNetworkPolicy, &got)) + } + }) + } +} + +func TestGetSingleIP(t *testing.T) { + ipAddress := "192.168.1.1" + expected := &softwarecomposition.IPBlock{CIDR: "192.168.1.1/32"} + + result := getSingleIP(ipAddress) + + if result.CIDR != expected.CIDR { + t.Errorf("getSingleIP() = %v, want %v", result, expected) + } +} +func TestRemoveLabels(t *testing.T) { + labels := map[string]string{ + "app.kubernetes.io/name": "value", + "app.kubernetes.io/instance": "1234", + } + + expected := map[string]string{ + "app.kubernetes.io/name": "value", + } + + removeLabels(labels) + + if !reflect.DeepEqual(labels, expected) { + t.Errorf("removeLabels() = %v, want %v", labels, expected) + } +} + +func TestMergeIngressRulesByPorts(t *testing.T) { + protocolTCP := v1.ProtocolTCP + + tests := []struct { + name string + rules []softwarecomposition.NetworkPolicyIngressRule + expected []softwarecomposition.NetworkPolicyIngressRule + }{ + { + name: "merge multiple rules with same ports and different IPs", + rules: []softwarecomposition.NetworkPolicyIngressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.1/32", + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + }, + expected: []softwarecomposition.NetworkPolicyIngressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.1/32", + }, + }, + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + }, + }, + { + name: "do not merge rules with different ports", + rules: []softwarecomposition.NetworkPolicyIngressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.1/32", + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(443), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + }, + expected: []softwarecomposition.NetworkPolicyIngressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.1/32", + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(443), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + }, + }, + { + name: "do not merge rules with selectors", + rules: []softwarecomposition.NetworkPolicyIngressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "nginx", + }, + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + }, + expected: []softwarecomposition.NetworkPolicyIngressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "nginx", + }, + }, + }, + }, + }, + }, + }, + { + name: "merge rules with no selectors", + rules: []softwarecomposition.NetworkPolicyIngressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.1/32", + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + }, + expected: []softwarecomposition.NetworkPolicyIngressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + From: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.1/32", + }, + }, + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := mergeIngressRulesByPorts(tt.rules) + assert.Equal(t, tt.expected, actual) + }) + } +} + +func TestMergeEgressRulesByPorts(t *testing.T) { + protocolTCP := v1.ProtocolTCP + + tests := []struct { + name string + rules []softwarecomposition.NetworkPolicyEgressRule + expected []softwarecomposition.NetworkPolicyEgressRule + }{ + { + name: "merge multiple rules with same ports and different IPs", + rules: []softwarecomposition.NetworkPolicyEgressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.1/32", + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + }, + expected: []softwarecomposition.NetworkPolicyEgressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.1/32", + }, + }, + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + }, + }, + { + name: "do not merge rules with different ports", + rules: []softwarecomposition.NetworkPolicyEgressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.1/32", + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(443), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + }, + expected: []softwarecomposition.NetworkPolicyEgressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.1/32", + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(443), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + }, + }, + { + name: "do not merge rules with selectors", + rules: []softwarecomposition.NetworkPolicyEgressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "nginx", + }, + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + }, + expected: []softwarecomposition.NetworkPolicyEgressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "nginx", + }, + }, + }, + }, + }, + }, + }, + { + name: "merge rules with no selectors", + rules: []softwarecomposition.NetworkPolicyEgressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.1/32", + }, + }, + }, + }, + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + }, + expected: []softwarecomposition.NetworkPolicyEgressRule{ + { + Ports: []softwarecomposition.NetworkPolicyPort{ + { + Port: ptrToInt32(80), + Protocol: &protocolTCP, + }, + }, + To: []softwarecomposition.NetworkPolicyPeer{ + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.1/32", + }, + }, + { + IPBlock: &softwarecomposition.IPBlock{ + CIDR: "10.0.0.2/32", + }, + }, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := mergeEgressRulesByPorts(tt.rules) + assert.Equal(t, tt.expected, actual) + }) + } +} + +func ptrToInt32(i int32) *int32 { + return &i +} + +// embed file + +func compareNP(p1, p2 *softwarecomposition.GeneratedNetworkPolicy) error { + if p1 == nil || p2 == nil { + return fmt.Errorf("one of the policies is nil") + } + + if err := compareEgress(p1.Spec.Spec.Egress, p1.Spec.Spec.Egress); err != nil { + return fmt.Errorf("Spec is different. p1.Spec.Spec.Egress: %v, p2.Spec.Spec.Egress: %v", p1.Spec.Spec.Egress, p2.Spec.Spec.Egress) + } + if err := compareIngress(p1.Spec.Spec.Ingress, p1.Spec.Spec.Ingress); err != nil { + return fmt.Errorf("Spec is different. p1.Spec.Spec.Ingress: %v, p2.Spec.Spec.Ingress: %v", p1.Spec.Spec.Ingress, p2.Spec.Spec.Ingress) + } + + return nil +} + +func toString(i interface{}) string { + b, _ := json.Marshal(i) + return string(b) +} + +func compareIngress(a, b []softwarecomposition.NetworkPolicyIngressRule) error { + if len(a) != len(b) { + return fmt.Errorf("len(a) != len(b). len(a): %d, len(b): %d", len(a), len(b)) + } + var al []string + var bl []string + for i := range a { + al = append(al, toString(a[i])) + bl = append(bl, toString(b[i])) + } + slices.Sort(al) + slices.Sort(bl) + if !reflect.DeepEqual(al, bl) { + return fmt.Errorf("a != b. a: %v, b: %v", a, b) + } + return nil +} + +func compareEgress(a, b []softwarecomposition.NetworkPolicyEgressRule) error { + if len(a) != len(b) { + return fmt.Errorf("len(a) != len(b). len(a): %d, len(b): %d", len(a), len(b)) + } + var al []string + var bl []string + for i := range a { + al = append(al, toString(a[i])) + bl = append(bl, toString(b[i])) + } + slices.Sort(al) + slices.Sort(bl) + if !reflect.DeepEqual(al, bl) { + return fmt.Errorf("a != b. a: %v, b: %v", a, b) + } + return nil +} diff --git a/pkg/apis/softwarecomposition/v1beta1/networkpolicy/networkpolicy.go b/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v1/networkpolicy.go similarity index 97% rename from pkg/apis/softwarecomposition/v1beta1/networkpolicy/networkpolicy.go rename to pkg/apis/softwarecomposition/v1beta1/networkpolicy/v1/networkpolicy.go index 2fe66e274..e3a5cd18e 100644 --- a/pkg/apis/softwarecomposition/v1beta1/networkpolicy/networkpolicy.go +++ b/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v1/networkpolicy.go @@ -2,7 +2,7 @@ package networkpolicy import ( sc "github.com/kubescape/storage/pkg/apis/softwarecomposition" - np "github.com/kubescape/storage/pkg/apis/softwarecomposition/networkpolicy" + np "github.com/kubescape/storage/pkg/apis/softwarecomposition/networkpolicy/v1" "github.com/kubescape/storage/pkg/apis/softwarecomposition/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -11,8 +11,6 @@ const ( storageV1Beta1ApiVersion = "spdx.softwarecomposition.kubescape.io/v1beta1" ) -// FIXME switch to NetworkNeighborhood - func GenerateNetworkPolicy(networkNeighbors v1beta1.NetworkNeighbors, knownServers []v1beta1.KnownServer, timeProvider metav1.Time) (v1beta1.GeneratedNetworkPolicy, error) { networkNeighborsV1, err := convertNetworkNeighbors(&networkNeighbors) if err != nil { diff --git a/pkg/apis/softwarecomposition/v1beta1/networkpolicy/networkpolicy_test.go b/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v1/networkpolicy_test.go similarity index 100% rename from pkg/apis/softwarecomposition/v1beta1/networkpolicy/networkpolicy_test.go rename to pkg/apis/softwarecomposition/v1beta1/networkpolicy/v1/networkpolicy_test.go diff --git a/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/networkpolicy.go b/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/networkpolicy.go new file mode 100644 index 000000000..c9991bd39 --- /dev/null +++ b/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/networkpolicy.go @@ -0,0 +1,59 @@ +package networkpolicy + +import ( + sc "github.com/kubescape/storage/pkg/apis/softwarecomposition" + np "github.com/kubescape/storage/pkg/apis/softwarecomposition/networkpolicy/v2" + "github.com/kubescape/storage/pkg/apis/softwarecomposition/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + storageV1Beta1ApiVersion = "spdx.softwarecomposition.kubescape.io/v1beta1" +) + +func GenerateNetworkPolicy(networkNeighborhood *v1beta1.NetworkNeighborhood, knownServers []v1beta1.KnownServer, timeProvider metav1.Time) (v1beta1.GeneratedNetworkPolicy, error) { + networkNeighborhoodV1, err := convertNetworkNeighborhood(networkNeighborhood) + if err != nil { + return v1beta1.GeneratedNetworkPolicy{}, err + } + knownServersV1, err := convertKnownServersList(knownServers) + if err != nil { + return v1beta1.GeneratedNetworkPolicy{}, err + } + + npv1, err := np.GenerateNetworkPolicy(networkNeighborhoodV1, knownServersV1, timeProvider) + if err != nil { + return v1beta1.GeneratedNetworkPolicy{}, err + } + + return convertGeneratedNetworkPolicy(&npv1) + +} + +func convertGeneratedNetworkPolicy(old *sc.GeneratedNetworkPolicy) (v1beta1.GeneratedNetworkPolicy, error) { + npv1beta1 := v1beta1.GeneratedNetworkPolicy{} + if err := v1beta1.Convert_softwarecomposition_GeneratedNetworkPolicy_To_v1beta1_GeneratedNetworkPolicy(old, &npv1beta1, nil); err != nil { + return v1beta1.GeneratedNetworkPolicy{}, err + } + npv1beta1.TypeMeta.APIVersion = storageV1Beta1ApiVersion + npv1beta1.TypeMeta.Kind = "GeneratedNetworkPolicy" + return npv1beta1, nil +} + +func convertNetworkNeighborhood(old *v1beta1.NetworkNeighborhood) (*sc.NetworkNeighborhood, error) { + neighbors := &sc.NetworkNeighborhood{} + err := v1beta1.Convert_v1beta1_NetworkNeighborhood_To_softwarecomposition_NetworkNeighborhood(old, neighbors, nil) + return neighbors, err +} +func convertKnownServersList(old []v1beta1.KnownServer) ([]sc.KnownServer, error) { + var servers []sc.KnownServer + for i := range old { + k := sc.KnownServer{} + err := v1beta1.Convert_v1beta1_KnownServer_To_softwarecomposition_KnownServer(&old[i], &k, nil) + if err != nil { + return nil, err + } + servers = append(servers, k) + } + return servers, nil +} diff --git a/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/networkpolicy_test.go b/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/networkpolicy_test.go new file mode 100644 index 000000000..b4c6335af --- /dev/null +++ b/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/networkpolicy_test.go @@ -0,0 +1,129 @@ +package networkpolicy + +import ( + _ "embed" + "encoding/json" + "fmt" + "reflect" + "slices" + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kubescape/storage/pkg/apis/softwarecomposition/v1beta1" + "github.com/stretchr/testify/assert" +) + +//go:embed testdata/nn-operator.json +var networkNeighborhoodFile string + +//go:embed testdata/np-operator.json +var networkPolicyFile string + +//go:embed testdata/known-servers.json +var knownServersFile string + +func TestGenerateNetworkPolicyFromFile(t *testing.T) { + timeProvider := metav1.Now() + + networkNeighborhood := &v1beta1.NetworkNeighborhood{} + knownServers := []v1beta1.KnownServer{} + expectedNetworkPolicy := &v1beta1.GeneratedNetworkPolicy{} + + if err := json.Unmarshal([]byte(networkNeighborhoodFile), networkNeighborhood); err != nil { + t.Fatalf("failed to unmarshal JSON data from file %s: %v", networkNeighborhoodFile, err) + } + if err := json.Unmarshal([]byte(knownServersFile), &knownServers); err != nil { + t.Fatalf("failed to unmarshal JSON data from file %s: %v", networkNeighborhoodFile, err) + } + if err := json.Unmarshal([]byte(networkPolicyFile), expectedNetworkPolicy); err != nil { + t.Fatalf("failed to unmarshal JSON data from file %s: %v", networkNeighborhoodFile, err) + } + // Generate the network policy + generatedNetworkPolicy, err := GenerateNetworkPolicy(networkNeighborhood, knownServers, timeProvider) + if err != nil { + t.Fatalf("failed to generate network policy: %v", err) + } + + // Compare the generated policy with the expected policy + assert.Nil(t, compareNP(&generatedNetworkPolicy, expectedNetworkPolicy)) +} + +func compareNP(p1, p2 *v1beta1.GeneratedNetworkPolicy) error { + if p1 == nil || p2 == nil { + return fmt.Errorf("one of the policies is nil") + } + if !reflect.DeepEqual(p1.TypeMeta, p2.TypeMeta) { + return fmt.Errorf("TypeMeta is different. p1.TypeMeta: %s, p2.TypeMeta: %s", toString(p1.TypeMeta), p2.TypeMeta) + } + p1.ObjectMeta.CreationTimestamp = metav1.Time{} + p2.ObjectMeta.CreationTimestamp = metav1.Time{} + if !reflect.DeepEqual(p1.ObjectMeta, p2.ObjectMeta) { + return fmt.Errorf("ObjectMeta is different. p1.ObjectMeta: %s, p2.ObjectMeta: %s", toString(p1.ObjectMeta), toString(p2.ObjectMeta)) + } + + if !reflect.DeepEqual(p1.Spec.GetAnnotations(), p2.Spec.GetAnnotations()) { + return fmt.Errorf("Spec is different. p1.Spec.GetAnnotations: %v, p2.Spec.GetAnnotations: %v", p1.Spec.GetAnnotations(), p2.Spec.GetAnnotations()) + } + if !reflect.DeepEqual(p1.Spec.GetLabels(), p2.Spec.GetLabels()) { + return fmt.Errorf("Spec is different. p1.Spec.GetLabels: %v, p2.Spec.GetLabels: %v", p1.Spec.GetLabels(), p2.Spec.GetLabels()) + } + if !reflect.DeepEqual(p1.Spec.Name, p2.Spec.Name) { + return fmt.Errorf("Spec is different. p1.Spec.Name: %v, p2.Spec.Name: %v", p1.Spec.Name, p2.Spec.Name) + } + if err := compareEgress(p1.Spec.Spec.Egress, p1.Spec.Spec.Egress); err != nil { + return fmt.Errorf("Spec is different. p1.Spec.Spec.Egress: %v, p2.Spec.Spec.Egress: %v", p1.Spec.Spec.Egress, p2.Spec.Spec.Egress) + } + if err := compareIngress(p1.Spec.Spec.Ingress, p1.Spec.Spec.Ingress); err != nil { + return fmt.Errorf("Spec is different. p1.Spec.Spec.Ingress: %v, p2.Spec.Spec.Ingress: %v", p1.Spec.Spec.Ingress, p2.Spec.Spec.Ingress) + } + + if !reflect.DeepEqual(p1.Spec.Spec.PodSelector, p2.Spec.Spec.PodSelector) { + return fmt.Errorf("Spec is different. p1.Spec.Spec.PodSelector: %v, p2.Spec.Spec.PodSelector: %v", p1.Spec.Spec.PodSelector, p2.Spec.Spec.PodSelector) + } + if !reflect.DeepEqual(p1.Spec.Spec.PolicyTypes, p2.Spec.Spec.PolicyTypes) { + return fmt.Errorf("Spec is different. p1.Spec.Spec.PolicyTypes: %v, p2.Spec.Spec.PolicyTypes: %v", p1.Spec.Spec.PolicyTypes, p2.Spec.Spec.PolicyTypes) + } + return nil +} + +func toString(i interface{}) string { + b, _ := json.Marshal(i) + return string(b) +} + +func compareIngress(a, b []v1beta1.NetworkPolicyIngressRule) error { + if len(a) != len(b) { + return fmt.Errorf("len(a) != len(b). len(a): %d, len(b): %d", len(a), len(b)) + } + var al []string + var bl []string + for i := range a { + al = append(al, toString(a[i])) + bl = append(bl, toString(b[i])) + } + slices.Sort(al) + slices.Sort(bl) + if !reflect.DeepEqual(al, bl) { + return fmt.Errorf("a != b. a: %v, b: %v", a, b) + } + return nil +} + +func compareEgress(a, b []v1beta1.NetworkPolicyEgressRule) error { + if len(a) != len(b) { + return fmt.Errorf("len(a) != len(b). len(a): %d, len(b): %d", len(a), len(b)) + } + var al []string + var bl []string + for i := range a { + al = append(al, toString(a[i])) + bl = append(bl, toString(b[i])) + } + slices.Sort(al) + slices.Sort(bl) + if !reflect.DeepEqual(al, bl) { + return fmt.Errorf("a != b. a: %v, b: %v", a, b) + } + return nil +} diff --git a/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/testdata/known-servers.json b/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/testdata/known-servers.json new file mode 100644 index 000000000..256f27201 --- /dev/null +++ b/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/testdata/known-servers.json @@ -0,0 +1,24 @@ +[ + { + "apiVersion": "spdx.softwarecomposition.kubescape.io/v1beta1", + "kind": "KnownServer", + "metadata": { + "creationTimestamp": "2024-06-02T08:04:02Z", + "name": "my-org", + "resourceVersion": "1", + "uid": "caf185d6-a59a-4fd1-81cd-0ce44be34b44" + }, + "spec": [ + { + "ipBlock": "16.170.0.0/15", + "name": "my-cloud", + "server": "cloud.io" + }, + { + "ipBlock": "13.50.180.111/24", + "name": "my-cloud", + "server": "cloud.io" + } + ] + } +] \ No newline at end of file diff --git a/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/testdata/nn-operator.json b/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/testdata/nn-operator.json new file mode 100644 index 000000000..f04bf5a54 --- /dev/null +++ b/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/testdata/nn-operator.json @@ -0,0 +1,304 @@ +{ + "apiVersion": "spdx.softwarecomposition.kubescape.io/v1beta1", + "kind": "NetworkNeighborhood", + "metadata": { + "annotations": { + "kubescape.io/completion": "complete", + "kubescape.io/resource-size": "13", + "kubescape.io/status": "completed", + "kubescape.io/wlid": "wlid://cluster-do-fra1-dwertent/namespace-kubescape/deployment-operator" + }, + "creationTimestamp": "2024-05-30T08:20:01Z", + "labels": { + "kubescape.io/instance-template-hash": "55df98fc6d", + "kubescape.io/workload-api-group": "apps", + "kubescape.io/workload-api-version": "v1", + "kubescape.io/workload-kind": "Deployment", + "kubescape.io/workload-name": "operator", + "kubescape.io/workload-namespace": "kubescape", + "kubescape.io/workload-resource-version": "5358810" + }, + "name": "replicaset-operator-55df98fc6d", + "namespace": "kubescape", + "resourceVersion": "1", + "uid": "98333be8-c05a-49ff-b0ff-fe029060b241" + }, + "spec": { + "containers": [ + { + "egress": [ + { + "dns": "version-check.ks-services.co.", + "dnsNames": [ + "version-check.ks-services.co." + ], + "identifier": "2393462a016456c7d3b0a027c2106de039bb34d36bf58ffff7fc8635304170aa", + "ipAddress": "35.186.253.219", + "namespaceSelector": null, + "podSelector": null, + "ports": [ + { + "name": "TCP-443", + "port": 443, + "protocol": "TCP" + } + ], + "type": "external" + }, + { + "dns": "", + "dnsNames": null, + "identifier": "ad98a9e00a1e4a5efbbd827f432595a31085d0e8dcec365dbdfd8141bf3cbe3e", + "ipAddress": "", + "namespaceSelector": null, + "podSelector": { + "matchLabels": { + "app": "otel-collector" + } + }, + "ports": [ + { + "name": "TCP-4317", + "port": 4317, + "protocol": "TCP" + } + ], + "type": "internal" + }, + { + "dns": "", + "dnsNames": null, + "identifier": "ed8a3fa8750dd7045e9abb755f5dbd1a2025f5ed49c1ed67b7d8e1c534899bbb", + "ipAddress": "", + "namespaceSelector": null, + "podSelector": { + "matchLabels": { + "app": "kubescape" + } + }, + "ports": [ + { + "name": "TCP-8080", + "port": 8080, + "protocol": "TCP" + } + ], + "type": "internal" + }, + { + "dns": "", + "dnsNames": null, + "identifier": "e6d07dcea08c02c35494f7aed68e7cff6d51843c5fbb36032a905f11ba833c13", + "ipAddress": "", + "namespaceSelector": null, + "podSelector": { + "matchLabels": { + "app": "gateway" + } + }, + "ports": [ + { + "name": "TCP-8001", + "port": 8001, + "protocol": "TCP" + } + ], + "type": "internal" + }, + { + "dns": "", + "dnsNames": null, + "identifier": "ba56b560f0008cb2227752015bf87c5fc365fb8dfd5599162cbe71f105dfce00", + "ipAddress": "", + "namespaceSelector": null, + "podSelector": { + "matchLabels": { + "app": "kubevuln" + } + }, + "ports": [ + { + "name": "TCP-8080", + "port": 8080, + "protocol": "TCP" + } + ], + "type": "internal" + }, + { + "dns": "report.armo.cloud.", + "dnsNames": [ + "report.armo.cloud." + ], + "identifier": "c6bf8190e40278af21d4d561ed3256e05a6c240a1865f19baf353cdb45d8c363", + "ipAddress": "16.170.46.131", + "namespaceSelector": null, + "podSelector": null, + "ports": [ + { + "name": "TCP-443", + "port": 443, + "protocol": "TCP" + } + ], + "type": "external" + }, + { + "dns": "report.armo.cloud.", + "dnsNames": [ + "report.armo.cloud." + ], + "identifier": "83260a3ba8236e69f12ebb706196a2d9541b6c2771cb481dde4f3f5816a1cd94", + "ipAddress": "16.171.184.118", + "namespaceSelector": null, + "podSelector": null, + "ports": [ + { + "name": "TCP-443", + "port": 443, + "protocol": "TCP" + } + ], + "type": "external" + }, + { + "dns": "", + "dnsNames": null, + "identifier": "e5e8ca3d76f701a19b7478fdc1c8c24ccc6cef9902b52c8c7e015439e2a1ddf3", + "ipAddress": "", + "namespaceSelector": { + "matchLabels": { + "kubernetes.io/metadata.name": "kube-system" + } + }, + "podSelector": { + "matchLabels": { + "k8s-app": "kube-dns" + } + }, + "ports": [ + { + "name": "UDP-53", + "port": 53, + "protocol": "UDP" + } + ], + "type": "internal" + }, + { + "dns": "", + "dnsNames": null, + "identifier": "275a177484719f71d0e1dc151f5bca143095b34c1bf4b3525131cce48970bedb", + "ipAddress": "10.245.0.1", + "namespaceSelector": { + "matchLabels": { + "kubernetes.io/metadata.name": "default" + } + }, + "podSelector": { + "matchLabels": { + "component": "apiserver", + "provider": "kubernetes" + } + }, + "ports": [ + { + "name": "TCP-443", + "port": 443, + "protocol": "TCP" + } + ], + "type": "internal" + }, + { + "dns": "report.armo.cloud.", + "dnsNames": [ + "report.armo.cloud." + ], + "identifier": "ba5459ff4343e49a03322aab030b548e688dcac5fd6814e3ab8415e949c71bb2", + "ipAddress": "13.50.180.111", + "namespaceSelector": null, + "podSelector": null, + "ports": [ + { + "name": "TCP-443", + "port": 443, + "protocol": "TCP" + } + ], + "type": "external" + } + ], + "ingress": [ + { + "dns": "", + "dnsNames": null, + "identifier": "e09f0b1719b5a3a09e401846cb4a171b6e1b9d5fb00df7d9f886e344aa42b861", + "ipAddress": "10.244.0.73", + "namespaceSelector": null, + "podSelector": null, + "ports": [ + { + "name": "TCP-8000", + "port": 8000, + "protocol": "TCP" + } + ], + "type": "external" + }, + { + "dns": "", + "dnsNames": null, + "identifier": "8d888c23b764ff6cf9fb900bf51c53287f10e889eac145c090890d546751d81e", + "ipAddress": "10.244.0.67", + "namespaceSelector": null, + "podSelector": null, + "ports": [ + { + "name": "TCP-4002", + "port": 4002, + "protocol": "TCP" + } + ], + "type": "external" + }, + { + "dns": "", + "dnsNames": null, + "identifier": "a7831eb3c44184545e41f512277c3e246e60b3fe83272223f114a4db1f9759c0", + "ipAddress": "", + "namespaceSelector": null, + "podSelector": { + "matchLabels": { + "app": "kubescape-scheduler", + "app.kubernetes.io/name": "kubescape-scheduler", + "armo.tier": "kubescape-scan", + "batch.kubernetes.io/controller-uid": "c3f4e988-0cca-40e6-bd25-69872d41281a", + "batch.kubernetes.io/job-name": "kubescape-scheduler-28618366", + "controller-uid": "c3f4e988-0cca-40e6-bd25-69872d41281a", + "job-name": "kubescape-scheduler-28618366", + "kubescape.io/tier": "core" + } + }, + "ports": [ + { + "name": "TCP-4002", + "port": 4002, + "protocol": "TCP" + } + ], + "type": "internal" + } + ], + "name": "operator" + } + ], + "ephemeralContainers": null, + "initContainers": null, + "matchLabels": { + "app.kubernetes.io/instance": "kubescape", + "app.kubernetes.io/name": "operator", + "tier": "ks-control-plane" + } + } +} diff --git a/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/testdata/np-operator.json b/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/testdata/np-operator.json new file mode 100644 index 000000000..94d54d604 --- /dev/null +++ b/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/testdata/np-operator.json @@ -0,0 +1,281 @@ +{ + "apiVersion": "spdx.softwarecomposition.kubescape.io/v1beta1", + "kind": "GeneratedNetworkPolicy", + "metadata": { + "creationTimestamp": null, + "labels": { + "kubescape.io/workload-api-group": "apps", + "kubescape.io/workload-api-version": "v1", + "kubescape.io/workload-kind": "Deployment", + "kubescape.io/workload-name": "operator", + "kubescape.io/workload-namespace": "kubescape", + "kubescape.io/workload-resource-version": "5358810" + }, + "name": "replicaset-operator-55df98fc6d", + "namespace": "kubescape" + }, + "policyRef": [ + { + "dns": "report.armo.cloud.", + "ipBlock": "13.50.180.111/24", + "name": "my-cloud", + "originalIP": "13.50.180.111", + "server": "cloud.io" + }, + { + "dns": "report.armo.cloud.", + "ipBlock": "16.170.0.0/15", + "name": "my-cloud", + "originalIP": "16.171.184.118", + "server": "cloud.io" + }, + { + "dns": "report.armo.cloud.", + "ipBlock": "16.170.0.0/15", + "name": "my-cloud", + "originalIP": "16.170.46.131", + "server": "cloud.io" + }, + { + "dns": "version-check.ks-services.co.", + "ipBlock": "35.186.253.219/32", + "name": "", + "originalIP": "35.186.253.219", + "server": "" + } + ], + "spec": { + "apiVersion": "networking.k8s.io/v1", + "kind": "NetworkPolicy", + "metadata": { + "annotations": { + "generated-by": "kubescape" + }, + "creationTimestamp": null, + "labels": { + "kubescape.io/workload-api-group": "apps", + "kubescape.io/workload-api-version": "v1", + "kubescape.io/workload-kind": "Deployment", + "kubescape.io/workload-name": "operator", + "kubescape.io/workload-namespace": "kubescape", + "kubescape.io/workload-resource-version": "5358810" + }, + "name": "deployment-operator", + "namespace": "kubescape" + }, + "spec": { + "ingress": [ + { + "from": [ + { + "ipBlock": { + "cidr": "10.244.0.67/32" + } + } + ], + "ports": [ + { + "port": 4002, + "protocol": "TCP" + } + ] + }, + { + "from": [ + { + "ipBlock": { + "cidr": "10.244.0.73/32" + } + } + ], + "ports": [ + { + "port": 8000, + "protocol": "TCP" + } + ] + }, + { + "from": [ + { + "podSelector": { + "matchLabels": { + "app": "kubescape-scheduler", + "app.kubernetes.io/name": "kubescape-scheduler", + "armo.tier": "kubescape-scan", + "batch.kubernetes.io/controller-uid": "c3f4e988-0cca-40e6-bd25-69872d41281a", + "batch.kubernetes.io/job-name": "kubescape-scheduler-28618366", + "controller-uid": "c3f4e988-0cca-40e6-bd25-69872d41281a", + "job-name": "kubescape-scheduler-28618366", + "kubescape.io/tier": "core" + } + } + } + ], + "ports": [ + { + "port": 4002, + "protocol": "TCP" + } + ] + } + ], + "egress": [ + { + "ports": [ + { + "port": 443, + "protocol": "TCP" + } + ], + "to": [ + { + "ipBlock": { + "cidr": "13.50.180.111/24" + } + }, + { + "ipBlock": { + "cidr": "16.170.0.0/15" + } + }, + { + "ipBlock": { + "cidr": "35.186.253.219/32" + } + } + ] + }, + { + "ports": [ + { + "port": 4317, + "protocol": "TCP" + } + ], + "to": [ + { + "podSelector": { + "matchLabels": { + "app": "otel-collector" + } + } + } + ] + }, + { + "ports": [ + { + "port": 53, + "protocol": "UDP" + } + ], + "to": [ + { + "namespaceSelector": { + "matchLabels": { + "kubernetes.io/metadata.name": "kube-system" + } + }, + "podSelector": { + "matchLabels": { + "k8s-app": "kube-dns" + } + } + } + ] + }, + { + "ports": [ + { + "port": 8001, + "protocol": "TCP" + } + ], + "to": [ + { + "podSelector": { + "matchLabels": { + "app": "gateway" + } + } + } + ] + }, + { + "ports": [ + { + "port": 8080, + "protocol": "TCP" + } + ], + "to": [ + { + "podSelector": { + "matchLabels": { + "app": "kubevuln" + } + } + } + ] + }, + { + "ports": [ + { + "port": 8080, + "protocol": "TCP" + } + ], + "to": [ + { + "podSelector": { + "matchLabels": { + "app": "kubescape" + } + } + } + ] + }, + { + "ports": [ + { + "port": 443, + "protocol": "TCP" + } + ], + "to": [ + { + "namespaceSelector": { + "matchLabels": { + "kubernetes.io/metadata.name": "default" + } + }, + "podSelector": { + "matchLabels": { + "component": "apiserver", + "provider": "kubernetes" + } + } + }, + { + "ipBlock": { + "cidr": "10.245.0.1/32" + } + } + ] + } + ], + "podSelector": { + "matchLabels": { + "app.kubernetes.io/instance": "kubescape", + "app.kubernetes.io/name": "operator", + "tier": "ks-control-plane" + } + }, + "policyTypes": [ + "Ingress", + "Egress" + ] + } + } +} diff --git a/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/testdata/np.new.json b/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/testdata/np.new.json new file mode 100644 index 000000000..6d097c5f1 --- /dev/null +++ b/pkg/apis/softwarecomposition/v1beta1/networkpolicy/v2/testdata/np.new.json @@ -0,0 +1,281 @@ +{ + "kind": "GeneratedNetworkPolicy", + "apiVersion": "spdx.softwarecomposition.kubescape.io/v1beta1", + "metadata": { + "name": "replicaset-operator-55df98fc6d", + "namespace": "kubescape", + "creationTimestamp": null, + "labels": { + "kubescape.io/workload-api-group": "apps", + "kubescape.io/workload-api-version": "v1", + "kubescape.io/workload-kind": "Deployment", + "kubescape.io/workload-name": "operator", + "kubescape.io/workload-namespace": "kubescape", + "kubescape.io/workload-resource-version": "5358810" + } + }, + "policyRef": [ + { + "ipBlock": "35.186.253.219/32", + "originalIP": "35.186.253.219", + "dns": "version-check.ks-services.co.", + "name": "", + "server": "" + }, + { + "ipBlock": "16.170.0.0/15", + "originalIP": "16.170.46.131", + "dns": "report.armo.cloud.", + "name": "my-cloud", + "server": "cloud.io" + }, + { + "ipBlock": "16.170.0.0/15", + "originalIP": "16.171.184.118", + "dns": "report.armo.cloud.", + "name": "my-cloud", + "server": "cloud.io" + }, + { + "ipBlock": "13.50.180.111/24", + "originalIP": "13.50.180.111", + "dns": "report.armo.cloud.", + "name": "my-cloud", + "server": "cloud.io" + } + ], + "spec": { + "kind": "NetworkPolicy", + "apiVersion": "networking.k8s.io/v1", + "metadata": { + "name": "replicaset-operator-55df98fc6d", + "namespace": "kubescape", + "creationTimestamp": null, + "labels": { + "kubescape.io/workload-api-group": "apps", + "kubescape.io/workload-api-version": "v1", + "kubescape.io/workload-kind": "Deployment", + "kubescape.io/workload-name": "operator", + "kubescape.io/workload-namespace": "kubescape", + "kubescape.io/workload-resource-version": "5358810" + }, + "annotations": { + "generated-by": "kubescape" + } + }, + "spec": { + "podSelector": { + "matchLabels": { + "app.kubernetes.io/instance": "kubescape", + "app.kubernetes.io/name": "operator", + "tier": "ks-control-plane" + } + }, + "ingress": [ + { + "ports": [ + { + "protocol": "TCP", + "port": 4002 + } + ], + "from": [ + { + "ipBlock": { + "cidr": "10.244.0.67/32" + } + } + ] + }, + { + "ports": [ + { + "protocol": "TCP", + "port": 8000 + } + ], + "from": [ + { + "ipBlock": { + "cidr": "10.244.0.73/32" + } + } + ] + }, + { + "ports": [ + { + "protocol": "TCP", + "port": 4002 + } + ], + "from": [ + { + "podSelector": { + "matchLabels": { + "app": "kubescape-scheduler", + "app.kubernetes.io/name": "kubescape-scheduler", + "armo.tier": "kubescape-scan", + "batch.kubernetes.io/controller-uid": "c3f4e988-0cca-40e6-bd25-69872d41281a", + "batch.kubernetes.io/job-name": "kubescape-scheduler-28618366", + "controller-uid": "c3f4e988-0cca-40e6-bd25-69872d41281a", + "job-name": "kubescape-scheduler-28618366", + "kubescape.io/tier": "core" + } + } + } + ] + } + ], + "egress": [ + { + "ports": [ + { + "protocol": "TCP", + "port": 443 + } + ], + "to": [ + { + "ipBlock": { + "cidr": "13.50.180.111/24" + } + }, + { + "ipBlock": { + "cidr": "16.170.0.0/15" + } + }, + { + "ipBlock": { + "cidr": "35.186.253.219/32" + } + } + ] + }, + { + "ports": [ + { + "protocol": "TCP", + "port": 4317 + } + ], + "to": [ + { + "podSelector": { + "matchLabels": { + "app": "otel-collector" + } + } + } + ] + }, + { + "ports": [ + { + "protocol": "TCP", + "port": 8080 + } + ], + "to": [ + { + "podSelector": { + "matchLabels": { + "app": "kubescape" + } + } + } + ] + }, + { + "ports": [ + { + "protocol": "TCP", + "port": 8001 + } + ], + "to": [ + { + "podSelector": { + "matchLabels": { + "app": "gateway" + } + } + } + ] + }, + { + "ports": [ + { + "protocol": "TCP", + "port": 8080 + } + ], + "to": [ + { + "podSelector": { + "matchLabels": { + "app": "kubevuln" + } + } + } + ] + }, + { + "ports": [ + { + "protocol": "UDP", + "port": 53 + } + ], + "to": [ + { + "podSelector": { + "matchLabels": { + "k8s-app": "kube-dns" + } + }, + "namespaceSelector": { + "matchLabels": { + "kubernetes.io/metadata.name": "kube-system" + } + } + } + ] + }, + { + "ports": [ + { + "protocol": "TCP", + "port": 443 + } + ], + "to": [ + { + "podSelector": { + "matchLabels": { + "component": "apiserver", + "provider": "kubernetes" + } + }, + "namespaceSelector": { + "matchLabels": { + "kubernetes.io/metadata.name": "default" + } + } + }, + { + "ipBlock": { + "cidr": "10.245.0.1/32" + } + } + ] + } + ], + "policyTypes": [ + "Ingress", + "Egress" + ] + } + } +} \ No newline at end of file diff --git a/pkg/registry/file/generatednetworkpolicy.go b/pkg/registry/file/generatednetworkpolicy.go index 757411b94..6be8bef6d 100644 --- a/pkg/registry/file/generatednetworkpolicy.go +++ b/pkg/registry/file/generatednetworkpolicy.go @@ -8,7 +8,7 @@ import ( "github.com/kubescape/go-logger" "github.com/kubescape/go-logger/helpers" "github.com/kubescape/storage/pkg/apis/softwarecomposition" - "github.com/kubescape/storage/pkg/apis/softwarecomposition/networkpolicy" + "github.com/kubescape/storage/pkg/apis/softwarecomposition/networkpolicy/v1" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"