Skip to content

Commit

Permalink
Discover k8s generic net policy from kubearmor logs (#564)
Browse files Browse the repository at this point in the history
Signed-off-by: Eswar Rajan Subramanian <eswar@accuknox.com>
  • Loading branch information
seswarrajan authored Oct 17, 2022
1 parent 542f5f6 commit dcfcf6c
Show file tree
Hide file tree
Showing 11 changed files with 367 additions and 177 deletions.
25 changes: 25 additions & 0 deletions src/cluster/clusterResourceHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package cluster

import (
"errors"
"sort"
"strings"

"github.com/accuknox/auto-policy-discovery/src/config"
"github.com/accuknox/auto-policy-discovery/src/types"
Expand Down Expand Up @@ -62,3 +64,26 @@ func GetAllClusterResources(cluster string) ([]string, []types.Service, []types.
return namespaces, services, endpoints, pods, nil
}
}

// ExtractPodSvcInfoFromIP -- Extract respective podname/ns/labels from pod/svc ip
func ExtractPodSvcInfoFromIP(ip, clustername string) (string, string, string) {
podSvcName := ip

_, services, _, pods, err := GetAllClusterResources(clustername)
if err != nil {
return podSvcName, "", ""
}

for _, pod := range pods {
if pod.PodIP == ip {
return "pod/" + pod.PodName, strings.Join(sort.StringSlice(pod.Labels), ","), pod.Namespace
}
}
for _, svc := range services {
if svc.ClusterIP == ip {
return "svc/" + svc.ServiceName, strings.Join(svc.Labels, ","), svc.Namespace
}
}

return podSvcName, "", ""
}
8 changes: 4 additions & 4 deletions src/conf/local-file.yaml
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
application:
name: knoxautopolicy
network:
operation-mode: 1 # 1: cronjob | 2: one-time-job
operation-trigger: 100
operation-mode: 1 # 1: cronjob | 2: one-time-job
operation-trigger: 5
cron-job-time-interval: "0h0m10s" # format: XhYmZs
network-log-limit: 10000
network-log-from: "hubble" # db|hubble|feed-consumer
network-log-from: "kubearmor" # db|hubble|feed-consumer|kubearmor
#network-log-file: "/home/rahul/feeds.json" # file path
network-policy-to: "db" # db, file
network-policy-dir: "./"
namespace-filter:
- "!kube-system"
system:
operation-mode: 1 # 1: cronjob | 2: one-time-job
operation-mode: 1 # 1: cronjob | 2: one-time-job
operation-trigger: 5
cron-job-time-interval: "0h0m10s" # format: XhYmZs
system-log-from: "kubearmor" # db|kubearmor|feed-consumer
Expand Down
54 changes: 43 additions & 11 deletions src/networkpolicy/helperFunctions.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,16 @@ func getNetworkLogs() []types.KnoxNetworkLog {
if err := logFile.Close(); err != nil {
log.Error().Msg(err.Error())
}
} else if NetworkLogFrom == "kubearmor" {
// =============== //
// == Kubearmor == //
// =============== //
log.Info().Msg("Get network log from Kubearmor")

kaNwLogs := plugin.GetNetworkLogsFromKubeArmor()

// convert kubearmor log/alert to KnoxNetworkLog
networkLogs = plugin.ConvertKubeArmorNetLogToKnoxNetLog(kaNwLogs)
} else {
log.Error().Msgf("Network log source not correct: %s", NetworkLogFrom)
return nil
Expand Down Expand Up @@ -764,22 +774,44 @@ func WriteNetworkPoliciesToFile(cluster, namespace string) {
libs.WriteCiliumPolicyToYamlFile(namespace, ciliumPolicies)
}

func GetNetPolicy(cluster, namespace string) *wpb.WorkerResponse {
latestPolicies := libs.GetNetworkPolicies(CfgDB, cluster, namespace, "latest", "", "")
log.Info().Msgf("No. of latestPolicies - %d", len(latestPolicies))
ciliumPolicies := plugin.ConvertKnoxPoliciesToCiliumPolicies(latestPolicies)
func GetNetPolicy(cluster, namespace, policyType string) *wpb.WorkerResponse {

var response wpb.WorkerResponse
for i := range ciliumPolicies {
ciliumpolicy := wpb.CiliumPolicy{}

val, err := json.Marshal(&ciliumPolicies[i])
if err != nil {
log.Error().Msg(err.Error())
if strings.Contains(policyType, "cilium") {
latestPolicies := libs.GetNetworkPolicies(CfgDB, cluster, namespace, "latest", "", "")
log.Info().Msgf("No. of latestPolicies - %d", len(latestPolicies))
ciliumPolicies := plugin.ConvertKnoxPoliciesToCiliumPolicies(latestPolicies)

for i := range ciliumPolicies {
ciliumpolicy := wpb.Policy{}

val, err := json.Marshal(&ciliumPolicies[i])
if err != nil {
log.Error().Msg(err.Error())
continue
}
ciliumpolicy.Data = val

response.Ciliumpolicy = append(response.Ciliumpolicy, &ciliumpolicy)
}
ciliumpolicy.Data = val
response.K8SNetworkpolicy = nil
} else if strings.Contains(policyType, "generic") {
policies := plugin.ConvertKnoxNetPolicyToK8sNetworkPolicy(cluster, namespace)

for i := range policies {
genericNetPol := wpb.Policy{}

response.Ciliumpolicy = append(response.Ciliumpolicy, &ciliumpolicy)
val, err := json.Marshal(&policies[i])
if err != nil {
log.Error().Msg(err.Error())
continue
}
genericNetPol.Data = val

response.K8SNetworkpolicy = append(response.K8SNetworkpolicy, &genericNetPol)
}
response.Ciliumpolicy = nil
}
response.Res = "OK"
response.Kubearmorpolicy = nil
Expand Down
25 changes: 1 addition & 24 deletions src/observability/kubearmor.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package observability
import (
"encoding/json"
"errors"
"sort"
"strings"
"time"

Expand Down Expand Up @@ -149,28 +148,6 @@ func ProcessKubearmorAlert(kubearmorAlert *pb.Log) {
SystemLogsMutex.Unlock()
}

func extractPodSvcInfoFromIP(ip, clustername string) (string, string, string) {
podSvcName := ip

_, services, _, pods, err := cluster.GetAllClusterResources(clustername)
if err != nil {
return podSvcName, "", ""
}

for _, pod := range pods {
if pod.PodIP == ip {
return "pod/" + pod.PodName, strings.Join(sort.StringSlice(pod.Labels), ","), pod.Namespace
}
}
for _, svc := range services {
if svc.ClusterIP == ip {
return "svc/" + svc.ServiceName, strings.Join(svc.Labels, ","), svc.Namespace
}
}

return podSvcName, "", ""
}

func fetchSysServerConnDetail(log types.KubeArmorLog) (types.SysObsNwData, error) {
conn := types.SysObsNwData{}
err := errors.New("not a valid incoming/outgoing connection")
Expand All @@ -195,7 +172,7 @@ func fetchSysServerConnDetail(log types.KubeArmorLog) (types.SysObsNwData, error
resslice := strings.Split(log.Resource, " ")
for _, locres := range resslice {
if strings.Contains(locres, "remoteip") {
conn.PodSvcIP, conn.Labels, conn.Namespace = extractPodSvcInfoFromIP(strings.Split(locres, "=")[1], log.ClusterName)
conn.PodSvcIP, conn.Labels, conn.Namespace = cluster.ExtractPodSvcInfoFromIP(strings.Split(locres, "=")[1], log.ClusterName)
}
if strings.Contains(locres, "port") {
conn.ServerPort = strings.Split(locres, "=")[1]
Expand Down
109 changes: 109 additions & 0 deletions src/plugin/k8sNetwork.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package plugin

import (
"strconv"

"github.com/accuknox/auto-policy-discovery/src/config"
"github.com/accuknox/auto-policy-discovery/src/libs"
"github.com/accuknox/auto-policy-discovery/src/types"
v1 "k8s.io/api/core/v1"
nv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)

func ConvertKnoxNetPolicyToK8sNetworkPolicy(clustername, namespace string) []nv1.NetworkPolicy {

knoxNetPolicies := libs.GetNetworkPolicies(config.CurrentCfg.ConfigDB, clustername, namespace, "latest", "", "")
log.Info().Msgf("No. of knox network policies - %d", len(knoxNetPolicies))

if len(knoxNetPolicies) <= 0 {
return nil
}

res := []nv1.NetworkPolicy{}

for _, knp := range knoxNetPolicies {
k8NetPol := nv1.NetworkPolicy{}

k8NetPol.APIVersion = types.K8sNwPolicyAPIVersion
k8NetPol.Kind = types.K8sNwPolicyKind
k8NetPol.Name = knp.Metadata["name"]
k8NetPol.Namespace = knp.Metadata["namespace"]
k8NetPol.ClusterName = knp.Metadata["cluster_name"]

if len(knp.Spec.Egress) > 0 {

for _, eg := range knp.Spec.Egress {
var egressRule nv1.NetworkPolicyEgressRule
var protocol v1.Protocol

if eg.ToPorts[0].Protocol == string(v1.ProtocolTCP) {
protocol = v1.ProtocolTCP
} else if eg.ToPorts[0].Protocol == string(v1.ProtocolUDP) {
protocol = v1.ProtocolUDP
}
portVal, _ := strconv.ParseInt(eg.ToPorts[0].Port, 10, 32)

port := nv1.NetworkPolicyPort{
Port: &intstr.IntOrString{
Type: intstr.Int,
IntVal: int32(portVal),
},
Protocol: &protocol,
}

to := nv1.NetworkPolicyPeer{
PodSelector: &metav1.LabelSelector{
MatchLabels: eg.MatchLabels,
},
}

egressRule.Ports = append(egressRule.Ports, port)
egressRule.To = append(egressRule.To, to)

k8NetPol.Spec.Egress = append(k8NetPol.Spec.Egress, egressRule)
}

k8NetPol.Spec.PolicyTypes = append(k8NetPol.Spec.PolicyTypes, nv1.PolicyType(nv1.PolicyTypeEgress))
}

if len(knp.Spec.Ingress) > 0 {
for _, ing := range knp.Spec.Ingress {
var ingressRule nv1.NetworkPolicyIngressRule
var protocol v1.Protocol

if ing.ToPorts[0].Protocol == string(v1.ProtocolTCP) {
protocol = v1.ProtocolTCP
} else if ing.ToPorts[0].Protocol == string(v1.ProtocolUDP) {
protocol = v1.ProtocolUDP
}
portVal, _ := strconv.ParseInt(ing.ToPorts[0].Port, 10, 32)

port := nv1.NetworkPolicyPort{
Port: &intstr.IntOrString{
Type: intstr.Int,
IntVal: int32(portVal),
},
Protocol: &protocol,
}

from := nv1.NetworkPolicyPeer{
PodSelector: &metav1.LabelSelector{
MatchLabels: ing.MatchLabels,
},
}

ingressRule.Ports = append(ingressRule.Ports, port)
ingressRule.From = append(ingressRule.From, from)

k8NetPol.Spec.Ingress = append(k8NetPol.Spec.Ingress, ingressRule)
}
k8NetPol.Spec.PolicyTypes = append(k8NetPol.Spec.PolicyTypes, nv1.PolicyType(nv1.PolicyTypeIngress))
}

res = append(res, k8NetPol)
}

return res
}
Loading

0 comments on commit dcfcf6c

Please sign in to comment.