Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(core): handle sctp protocol and all protocols with raw socket and add protocol:all network rule #1892

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 59 additions & 19 deletions KubeArmor/BPF/enforcer.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,8 @@ static inline int match_net_rules(int type, int protocol, u32 eventID) {
return 0;

bpf_map_update_elem(&bufk, &one, z, BPF_ANY);
int p0;
int p1;
int p0_t, p1_t;
int p0_p, p1_p;
struct data_t *val = bpf_map_lookup_elem(inner, p);
bool fromSourceCheck = true;

Expand All @@ -329,41 +329,80 @@ static inline int match_net_rules(int type, int protocol, u32 eventID) {
if (src_offset == NULL)
fromSourceCheck = false;

void *ptr = &src_buf->buf[*src_offset];

// socket type check
if (type == SOCK_STREAM || type == SOCK_DGRAM || type == SOCK_RAW || type == SOCK_RDM || type == SOCK_SEQPACKET || type == SOCK_DCCP || type == SOCK_PACKET) {
p0_t = sock_type;
p1_t = type;
}

// protocol check
if (type == SOCK_STREAM && (protocol == IPPROTO_TCP || protocol == 0)) {
p0 = sock_proto;
p1 = IPPROTO_TCP;
p0_p = sock_proto;
p1_p = IPPROTO_TCP;
} else if (type == SOCK_DGRAM && (protocol == IPPROTO_UDP || protocol == 0)) {
p0 = sock_proto;
p1 = IPPROTO_UDP;
p0_p = sock_proto;
p1_p = IPPROTO_UDP;
} else if (protocol == IPPROTO_ICMP &&
(type == SOCK_DGRAM || type == SOCK_RAW)) {
p0 = sock_proto;
p1 = IPPROTO_ICMP;
} else if (type == SOCK_RAW && protocol == 0) {
p0 = sock_type;
p1 = SOCK_RAW;
p0_p = sock_proto;
p1_p = IPPROTO_ICMP;
} else if (protocol == IPPROTO_ICMPV6 &&
(type == SOCK_DGRAM || type == SOCK_RAW)) {
p0_p = sock_proto;
p1_p = IPPROTO_ICMPV6;
} else if ((type == SOCK_STREAM || type == SOCK_SEQPACKET) && (protocol == IPPROTO_SCTP || protocol == 0)) {
p0_p = sock_proto;
p1_p = IPPROTO_SCTP;
} else {
p0 = sock_proto;
p1 = protocol;
p0_p = sock_proto;
p1_p = protocol;
}

// socket type fromsource check
if (fromSourceCheck) {
void *ptr = &src_buf->buf[*src_offset];
bpf_probe_read_str(p->source, MAX_STRING_SIZE, ptr);
p->path[0] = p0;
p->path[1] = p1;
p->path[0] = p0_t;
p->path[1] = p1_t;
bpf_probe_read_str(store->source, MAX_STRING_SIZE, p->source);
val = bpf_map_lookup_elem(inner, p);
if (val) {
match = true;
goto decision;
}
}

// protocol fromsource check
if (fromSourceCheck) {
void *ptr = &src_buf->buf[*src_offset];
bpf_probe_read_str(p->source, MAX_STRING_SIZE, ptr);
p->path[0] = p0_p;
p->path[1] = p1_p;
bpf_probe_read_str(store->source, MAX_STRING_SIZE, p->source);
val = bpf_map_lookup_elem(inner, p);
if (val) {
match = true;
goto decision;
}
}
// check for rules without fromSource

// check for type rules without fromSource
bpf_map_update_elem(&bufk, &one, z, BPF_ANY);
p->path[0] = p0;
p->path[1] = p1;
p->path[0] = p0_t;
p->path[1] = p1_t;

val = bpf_map_lookup_elem(inner, p);

if (val) {
match = true;
goto decision;
}

// check for protocol rules without fromSource
bpf_map_update_elem(&bufk, &one, z, BPF_ANY);
p->path[0] = p0_p;
p->path[1] = p1_p;

val = bpf_map_lookup_elem(inner, p);

Expand Down Expand Up @@ -425,6 +464,7 @@ static inline int match_net_rules(int type, int protocol, u32 eventID) {

SEC("lsm/socket_create")
int BPF_PROG(enforce_net_create, int family, int type, int protocol) {
bpf_printk("type: %d protocol: %d", type, protocol);
return match_net_rules(type, protocol, _SOCKET_CREATE);
}

Expand Down
4 changes: 4 additions & 0 deletions KubeArmor/BPF/shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ char LICENSE[] SEC("license") = "Dual BSD/GPL";
#define BLOCK_POSTURE 141
#define CAPABLE_KEY 200

enum {
IPPROTO_ICMPV6 = 58
};

enum file_hook_type { dpath = 0, dfileread, dfilewrite };

enum deny_by_default {
Expand Down
17 changes: 13 additions & 4 deletions KubeArmor/enforcer/appArmorProfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,14 +231,21 @@ func (ae *AppArmorEnforcer) SetNetworkMatchProtocols(proto tp.NetworkProtocolTyp
//forcing the protocol to lowercase
proto.Protocol = strings.ToLower(proto.Protocol)

// handle icmpv6 protocol same as icmp
if proto.Protocol == "icmpv6" {
proto.Protocol = "icmp"
}

if !deny {
prof.Network = head
}
rule := RuleConfig{}
rule.Deny = deny
rule.Allow = !deny
if len(proto.FromSource) == 0 {
addRuletoMap(rule, proto.Protocol, prof.NetworkRules)
if proto.Protocol != "all" {
addRuletoMap(rule, proto.Protocol, prof.NetworkRules)
}
return
}

Expand All @@ -260,7 +267,9 @@ func (ae *AppArmorEnforcer) SetNetworkMatchProtocols(proto tp.NetworkProtocolTyp
prof.FromSource[source] = val
}
}
addRuletoMap(rule, proto.Protocol, prof.FromSource[source].NetworkRules)
if proto.Protocol != "all" {
addRuletoMap(rule, proto.Protocol, prof.FromSource[source].NetworkRules)
}
}
}

Expand Down Expand Up @@ -382,9 +391,9 @@ func (ae *AppArmorEnforcer) GenerateProfileBody(securityPolicies []tp.SecurityPo
if len(secPolicy.Spec.Network.MatchProtocols) > 0 {
for _, proto := range secPolicy.Spec.Network.MatchProtocols {
if proto.Action == "Allow" {
ae.SetNetworkMatchProtocols(proto, &profile, false, defaultPosture.NetworkAction != "block")
ae.SetNetworkMatchProtocols(proto, &profile, false, defaultPosture.NetworkAction != "block" || proto.Protocol == "all")
} else if proto.Action == "Block" {
ae.SetNetworkMatchProtocols(proto, &profile, true, true)
ae.SetNetworkMatchProtocols(proto, &profile, true, true && proto.Protocol != "all")
}
}
}
Expand Down
10 changes: 7 additions & 3 deletions KubeArmor/enforcer/bpflsm/enforcer.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ func NewBPFEnforcer(node tp.Node, pinpath string, logger *fd.Feeder, monitor *mo
PinPath: pinpath,
},
}); err != nil {
var ve *ebpf.VerifierError
if errors.As(err, &ve) {
// Using %+v will print the whole verifier error, not just the last
// few lines.
be.Logger.Errf("Verifier error: %+v", ve)
}
be.Logger.Errf("error loading BPF LSM objects: %v", err)
return be, err
}
Expand Down Expand Up @@ -351,9 +357,7 @@ func (be *BPFEnforcer) TraceEvents() {
sockProtocol = int32(event.Data.Path[1])
log.Operation = "Network"
if event.Data.Path[0] == 2 {
if event.Data.Path[1] == 3 {
log.Resource = fd.GetProtocolFromName("raw")
}
log.Resource = fd.GetProtocolFromType(int32(event.Data.Path[1]))
} else if event.Data.Path[0] == 3 {
log.Resource = fd.GetProtocolFromName(mon.GetProtocol(sockProtocol))
}
Expand Down
12 changes: 11 additions & 1 deletion KubeArmor/enforcer/bpflsm/rulesHandling.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,18 @@ var protocols = map[string]uint8{
"TCP": 6,
"UDP": 17,
"ICMPv6": 58,
"SCTP": 132,
}

// Socket Type Identifiers for Network Rules
var netType = map[string]uint8{
"RAW": 3,
"STREAM": 1,
"DGRAM": 2,
"RAW": 3,
"RDM": 4,
"SEQPACKET": 5,
"DCCP": 6,
"PACKET": 10,
}

// Array Keys for Network Rule Keys
Expand Down Expand Up @@ -251,6 +258,9 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec
}
}

// handle protocol: all|ALL rules
handleAllNetworkRule(&secPolicy.Spec.Network.MatchProtocols)

for _, net := range secPolicy.Spec.Network.MatchProtocols {
var val [2]uint8
var key = InnerKey{Path: [256]byte{}, Source: [256]byte{}}
Expand Down
90 changes: 90 additions & 0 deletions KubeArmor/enforcer/bpflsm/rulesHelper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 Authors of KubeArmor

package bpflsm

import (
"strings"

tp "github.com/kubearmor/KubeArmor/KubeArmor/types"
)

func handleAllNetworkRule(protocols *[]tp.NetworkProtocolType) {
allProtocols := []tp.NetworkProtocolType{}

allWithNoFromSourceAllow := false
allWithNoFromSourceBlock := false

sourcesBlock := map[string]string{}
sourcesAllow := map[string]string{}

for _, net := range *protocols {
if strings.ToUpper(net.Protocol) == "ALL" {
if len(net.FromSource) == 0 {
if net.Action == "Allow" && !allWithNoFromSourceAllow {
for r := range netType {
allProtocols = append(allProtocols, tp.NetworkProtocolType{
Protocol: r,
Action: net.Action,
})
}
allWithNoFromSourceAllow = true
} else if net.Action == "Block" && !allWithNoFromSourceBlock {
for r := range netType {
allProtocols = append(allProtocols, tp.NetworkProtocolType{
Protocol: r,
Action: net.Action,
})
}
allWithNoFromSourceBlock = true
}
} else {
for _, src := range net.FromSource {
if _, ok := sourcesAllow[src.Path]; !ok && net.Action == "Allow" {
sourcesAllow[src.Path] = net.Action
}
if _, ok := sourcesBlock[src.Path]; !ok && net.Action == "Block" {
sourcesBlock[src.Path] = net.Action
}
}
}
}
}

// add all with fromsource rules

if len(sourcesAllow) > 0 {
sources := []tp.MatchSourceType{}
for src := range sourcesAllow {
sources = append(sources, tp.MatchSourceType{
Path: src,
})
}
for r := range netType {
allProtocols = append(allProtocols, tp.NetworkProtocolType{
Protocol: r,
Action: "Allow",
FromSource: sources,
})
}
}

if len(sourcesBlock) > 0 {
sources := []tp.MatchSourceType{}
for src := range sourcesBlock {
sources = append(sources, tp.MatchSourceType{
Path: src,
})
}
for r := range netType {
allProtocols = append(allProtocols, tp.NetworkProtocolType{
Protocol: r,
Action: "Block",
FromSource: sources,
})
}
}

*protocols = append(*protocols, allProtocols...)

}
50 changes: 44 additions & 6 deletions KubeArmor/feeder/policyMatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,33 @@ func GetProtocolFromName(proto string) string {
return "protocol=UDP,type=SOCK_DGRAM"
case "icmp":
return "protocol=ICMP,type=SOCK_RAW"
case "raw":
case "icmpv6":
return "protocol=ICMPv6,type=SOCK_RAW"
case "sctp":
return "protocol=SCTP,type=SOCK_STREAM|SOCK_SEQPACKET"
default:
return proto
}
}

func GetProtocolFromType(proto int32) string {
switch proto {
case 1:
return "type=SOCK_STREAM"
case 2:
return "type=SOCK_DGRAM"
case 3:
return "type=SOCK_RAW"
case 4:
return "type=SOCK_RDM"
case 5:
return "type=SOCK_SEQPACKET"
case 6:
return "type=SOCK_DCCP"
case 10:
return "type=SOCK_PACKET"
default:
return "unknown"
return string(proto)
}
}

Expand All @@ -44,9 +67,24 @@ func fetchProtocol(resource string) string {
return "icmp"
} else if strings.Contains(resource, "SOCK_RAW") {
return "raw"
} else if strings.Contains(resource, "protocol=ICMPv6") {
return "icmpv6"
} else if strings.Contains(resource, "protocol=SCTP") {
return "sctp"
} else if strings.Contains(resource, "SOCK_STREAM") {
return "stream"
} else if strings.Contains(resource, "SOCK_DGRAM") {
return "dgram"
} else if strings.Contains(resource, "SOCK_RDM") {
return "rdm"
} else if strings.Contains(resource, "SOCK_SEQPACKET") {
return "seqpacket"
} else if strings.Contains(resource, "SOCK_DCCP") {
return "dccp"
} else if strings.Contains(resource, "SOCK_PACKET") {
return "packet"
}

return "unknown"
return resource
}

func getFileProcessUID(path string) string {
Expand Down Expand Up @@ -204,7 +242,7 @@ func (fd *Feeder) newMatchPolicy(policyEnabled int, policyName, src string, mp i
match.Message = npt.Message

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

// TODO: Handle cases where AppArmor network enforcement is not present
Expand Down Expand Up @@ -1303,7 +1341,7 @@ func (fd *Feeder) UpdateMatchedPolicy(log tp.Log) tp.Log {
matchedFlags := false

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

Expand Down
Loading
Loading