Skip to content

Commit

Permalink
Merge pull request #1569 from Prateeknandle/allow
Browse files Browse the repository at this point in the history
fix(policymatcher) : fixed audit event alerts for allowed network rules
  • Loading branch information
daemon1024 authored Jan 9, 2024
2 parents 697e597 + e363753 commit e9b5406
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 74 deletions.
172 changes: 99 additions & 73 deletions KubeArmor/feeder/policyMatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ func GetProtocolFromName(proto string) string {
}
}

func fetchProtocol(resource string) string {
if strings.Contains(resource, "protocol=TCP") || (strings.Contains(resource, "SOCK_STREAM") && strings.Contains(resource, "protocol=0")) {
return "tcp"
} else if strings.Contains(resource, "protocol=UDP") || (strings.Contains(resource, "SOCK_DGRAM") && strings.Contains(resource, "protocol=0")) {
return "udp"
} else if strings.Contains(resource, "protocol=ICMP") {
return "icmp"
} else if strings.Contains(resource, "SOCK_RAW") {
return "raw"
}

return "unknown"
}

func getFileProcessUID(path string) string {
info, err := os.Stat(path)
if err == nil {
Expand All @@ -52,7 +66,7 @@ func getOperationAndCapabilityFromName(capName string) (op, capability string) {
switch strings.ToLower(capName) {
case "net_raw":
op = "Network"
capability = "SOCK_RAW"
capability = "raw" // we will remove this when we have proper handling of capabilities
default:
return "", "unknown"
}
Expand Down Expand Up @@ -185,7 +199,7 @@ func (fd *Feeder) newMatchPolicy(policyEnabled int, policyName, src string, mp i
match.Message = npt.Message

match.Operation = "Network"
match.Resource = GetProtocolFromName(npt.Protocol)
match.Resource = npt.Protocol
match.ResourceType = "Protocol"

if policyEnabled == tp.KubeArmorPolicyAudited && npt.Action == "Allow" {
Expand Down Expand Up @@ -923,7 +937,6 @@ func setLogFields(log *tp.Log, existAllowPolicy bool, defaultPosture string, vis
(*log).Type = "HostLog"
}


// handles host visibility
// return true if visibility enabled
// return false otherwise so that log is skipped
Expand Down Expand Up @@ -960,7 +973,10 @@ func (fd *Feeder) UpdateMatchedPolicy(log tp.Log) tp.Log {
}

secPolicies := fd.SecurityPolicies[key].Policies
for _, secPolicy := range secPolicies {
// for "Network" case below we use skip bool to skip the log when the log is matched with one of the allowed rules in secPolicies
// skip is set to true(in below cases, in Network) for the log event which is matched by the rules
skip := false
for rule, secPolicy := range secPolicies {
if secPolicy.Action == "Allow" || secPolicy.Action == "Audit (Allow)" {
if secPolicy.Operation == "Process" || secPolicy.Operation == "File" {
existFileAllowPolicy = true
Expand Down Expand Up @@ -1206,105 +1222,115 @@ func (fd *Feeder) UpdateMatchedPolicy(log tp.Log) tp.Log {
continue
}

// when one of the below rule is already matched for the log event, we will skip for further matches
if skip {
break // break, so that once source is matched for a log it doesn't look for other cases
}
// match sources
if (!secPolicy.IsFromSource) || (secPolicy.IsFromSource && (secPolicy.Source == log.ParentProcessName || secPolicy.Source == log.ProcessName)) {
skip := false

for _, matchProtocol := range strings.Split(secPolicy.Resource, ",") {
if skip {
break
}

// match resources
if strings.Contains(log.Resource, matchProtocol) {
if (secPolicy.Action == "Allow" || secPolicy.Action == "Audit (Allow)") && log.Result == "Passed" {
// allow policy or allow policy with audit mode
// matched source + matched resource + matched action + expected result -> going to be skipped
matchedFlags := false

log.Type = "MatchedPolicy"
protocol := fetchProtocol(log.Resource)
if protocol == secPolicy.Resource {
matchedFlags = true
}

log.PolicyName = secPolicy.PolicyName
log.Severity = secPolicy.Severity
if matchedFlags {
if (secPolicy.Action == "Allow" || secPolicy.Action == "Audit (Allow)") && log.Result == "Passed" {
// allow policy or allow policy with audit mode
// matched source + matched resource + matched action + expected result -> going to be skipped

if len(secPolicy.Tags) > 0 {
log.Tags = strings.Join(secPolicy.Tags[:], ",")
log.ATags = secPolicy.Tags
}
log.Type = "MatchedPolicy"

if len(secPolicy.Message) > 0 {
log.Message = secPolicy.Message
}
log.PolicyName = secPolicy.PolicyName
log.Severity = secPolicy.Severity

if log.PolicyEnabled == tp.KubeArmorPolicyAudited {
log.Enforcer = "eBPF Monitor"
} else {
log.Enforcer = fd.Enforcer
}
if len(secPolicy.Tags) > 0 {
log.Tags = strings.Join(secPolicy.Tags[:], ",")
log.ATags = secPolicy.Tags
}

log.Action = "Allow"
if len(secPolicy.Message) > 0 {
log.Message = secPolicy.Message
}

skip = true
continue
if log.PolicyEnabled == tp.KubeArmorPolicyAudited {
log.Enforcer = "eBPF Monitor"
} else {
log.Enforcer = fd.Enforcer
}

if secPolicy.Action == "Audit" && log.Result == "Passed" {
// audit policy
// matched source + matched resource + matched action + expected result -> alert (audit log)
log.Action = "Allow"

log.Type = "MatchedPolicy"
skip = true
continue
}

log.PolicyName = secPolicy.PolicyName
log.Severity = secPolicy.Severity
if secPolicy.Action == "Audit" && log.Result == "Passed" {
// audit policy
// matched source + matched resource + matched action + expected result -> alert (audit log)

if len(secPolicy.Tags) > 0 {
log.Tags = strings.Join(secPolicy.Tags[:], ",")
log.ATags = secPolicy.Tags
}
log.Type = "MatchedPolicy"

if len(secPolicy.Message) > 0 {
log.Message = secPolicy.Message
}
log.PolicyName = secPolicy.PolicyName
log.Severity = secPolicy.Severity

log.Enforcer = "eBPF Monitor"
log.Action = secPolicy.Action
if len(secPolicy.Tags) > 0 {
log.Tags = strings.Join(secPolicy.Tags[:], ",")
log.ATags = secPolicy.Tags
}

skip = true
continue
if len(secPolicy.Message) > 0 {
log.Message = secPolicy.Message
}

if (secPolicy.Action == "Block" && log.Result != "Passed") ||
(secPolicy.Action == "Audit (Block)" && log.Result == "Passed") {
// block policy or block policy with audit mode
// matched source + matched resource + matched action + expected result -> alert
log.Enforcer = "eBPF Monitor"
log.Action = secPolicy.Action

log.Type = "MatchedPolicy"
skip = true
continue
}

log.PolicyName = secPolicy.PolicyName
log.Severity = secPolicy.Severity
if (secPolicy.Action == "Block" && log.Result != "Passed") ||
(secPolicy.Action == "Audit (Block)" && log.Result == "Passed") {
// block policy or block policy with audit mode
// matched source + matched resource + matched action + expected result -> alert

if len(secPolicy.Tags) > 0 {
log.Tags = strings.Join(secPolicy.Tags[:], ",")
}
log.Type = "MatchedPolicy"

if len(secPolicy.Message) > 0 {
log.Message = secPolicy.Message
}
log.PolicyName = secPolicy.PolicyName
log.Severity = secPolicy.Severity

if log.PolicyEnabled == tp.KubeArmorPolicyAudited {
log.Enforcer = "eBPF Monitor"
} else {
log.Enforcer = fd.Enforcer
}
if len(secPolicy.Tags) > 0 {
log.Tags = strings.Join(secPolicy.Tags[:], ",")
}

log.Action = secPolicy.Action
if len(secPolicy.Message) > 0 {
log.Message = secPolicy.Message
}

skip = true
continue
if log.PolicyEnabled == tp.KubeArmorPolicyAudited {
log.Enforcer = "eBPF Monitor"
} else {
log.Enforcer = fd.Enforcer
}

log.Action = secPolicy.Action

skip = true
continue
}
}
// if protocol is unknown we skip the audit alert event
if protocol == "unknown" {
log.Type = "MatchedPolicy"
log.Action = "Allow"
continue
}

if skip {
// keep looking for a rule to be matched
// send audit alert only when all the rules are compared and none is matched
if !matchedFlags && rule < len(secPolicies)-1 {
continue
}

Expand Down
5 changes: 4 additions & 1 deletion tests/k8s_env/block/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"time"

"github.com/kubearmor/KubeArmor/tests/util"
. "github.com/kubearmor/KubeArmor/tests/util"
. "github.com/onsi/ginkgo/v2"

Check warning on line 12 in tests/k8s_env/block/block_test.go

View workflow job for this annotation

GitHub Actions / go-lint-tests

should not use dot imports
. "github.com/onsi/gomega"
Expand Down Expand Up @@ -86,8 +87,10 @@ var _ = Describe("Posture", func() {
})

It("can whitelist certain files accessed by a package while blocking all other sensitive content", func() {
err := util.AnnotateNS("wordpress-mysql", "kubearmor-network-posture", "block")
Expect(err).To(BeNil())
// Apply policy
err := K8sApplyFile("res/ksp-wordpress-allow-file.yaml")
err = K8sApplyFile("res/ksp-wordpress-allow-file.yaml")
Expect(err).To(BeNil())

// wait for policy creation, added due to flaky behaviour
Expand Down
18 changes: 18 additions & 0 deletions tests/k8s_env/smoke/res/ksp-wordpress-allow-tcp.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
name: ksp-wordpress-net-tcp-allow-curl
namespace: wordpress-mysql
spec:
severity: 8
selector:
matchLabels:
app: wordpress
network:
matchProtocols:
- protocol: tcp
fromSource:
- path: /usr/bin/curl
- path: /bin/bash
action:
Allow
42 changes: 42 additions & 0 deletions tests/k8s_env/smoke/smoke_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"time"

"github.com/kubearmor/KubeArmor/tests/util"
. "github.com/kubearmor/KubeArmor/tests/util"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
Expand Down Expand Up @@ -287,6 +288,47 @@ var _ = Describe("Smoke", func() {
Expect(alerts[0].PolicyName).To(Equal("ksp-wordpress-block-mount-file"))
Expect(alerts[0].Severity).To(Equal("5"))
})
It("will allow use of tcp network protocol by curl and bash", func() {
err := util.AnnotateNS("wordpress-mysql", "kubearmor-network-posture", "audit")
Expect(err).To(BeNil())
// Apply policy
err = K8sApplyFile("res/ksp-wordpress-allow-tcp.yaml")
Expect(err).To(BeNil())

// Start Kubearmor Logs
err = KarmorLogStart("policy", "wordpress-mysql", "Network", wp)
Expect(err).To(BeNil())

// wait for policy creation
time.Sleep(5 * time.Second)

sout, _, err := K8sExecInPod(wp, "wordpress-mysql",
[]string{"bash", "-c", "curl 142.250.193.46"})
Expect(err).To(BeNil())
fmt.Printf("OUTPUT: %s\n", sout)
// tcp action
Expect(sout).To(ContainSubstring("http://www.google.com/"))

// check alert
_, alerts, err := KarmorGetLogs(5*time.Second, 1)
fmt.Printf("OUTPUT: %s\n", alerts)
Expect(err).To(BeNil())
Expect(len(alerts)).To(Equal(0))

// tcp + udp + raw action
sout, _, err = K8sExecInPod(wp, "wordpress-mysql",
[]string{"bash", "-c", "curl google.com"})
Expect(err).To(BeNil())
fmt.Printf("OUTPUT: %s\n", sout)
Expect(sout).To(ContainSubstring("http://www.google.com/"))

// check alert
_, alerts, err = KarmorGetLogs(5*time.Second, 1)
Expect(err).To(BeNil())
Expect(len(alerts)).To(BeNumerically(">=", 1))
Expect(alerts[0].PolicyName).To(Equal("DefaultPosture"))
Expect(alerts[0].Result).To(Equal("Passed"))
})
})

})

0 comments on commit e9b5406

Please sign in to comment.