diff --git a/KubeArmor/BPF/enforcer.bpf.c b/KubeArmor/BPF/enforcer.bpf.c index 811c25d546..055a4c19d8 100644 --- a/KubeArmor/BPF/enforcer.bpf.c +++ b/KubeArmor/BPF/enforcer.bpf.c @@ -15,6 +15,8 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) { struct outer_key okey; get_outer_key(&okey, t); + bpf_printk("process mnt id %llu pid %llu ", okey.mnt_ns , okey.pid_ns); + u32 *inner = bpf_map_lookup_elem(&kubearmor_containers, &okey); if (!inner) { @@ -346,6 +348,7 @@ static inline int match_net_rules(int type, int protocol, u32 eventID) { val = bpf_map_lookup_elem(inner, p); } + // doubt here bpf_map_update_elem(&bufk, &one, z, BPF_ANY); p->path[0] = p0; @@ -437,4 +440,141 @@ int BPF_PROG(enforce_file_perm, struct file *file, int mask) { struct path f_path = BPF_CORE_READ(file, f_path); return match_and_enforce_path_hooks(&f_path, dfilewrite, _FILE_PERMISSION); +} +SEC("lsm/capable") +int BPF_PROG(enforce_cap, const struct cred *cred, struct user_namespace *ns ,int cap, int ret){ + + event *task_info; + + struct task_struct *t = (struct task_struct *)bpf_get_current_task(); + + bool match = false; + + struct outer_key okey; + get_outer_key(&okey, t); + //struct cred *cred = (struct cred *)bpf_task_cred(t); + + bpf_printk(" capable mnt id %llu pid %llu ", okey.mnt_ns , okey.pid_ns); + bpf_printk( "capab %d ", cap); + // bpf_printk("UID: %llu, EUID: %llu, capabilities: %llx\\n", + // t->cred->uid.val, t->cred->euid.val, t->cred->cap_inheritable.cap[0]); + + u32 *inner = bpf_map_lookup_elem(&kubearmor_containers, &okey); + + if (!inner) { + return 0; + } + + u32 zero = 0; + bufs_k *z = bpf_map_lookup_elem(&bufk, &zero); + if (z == NULL) + return 0; + + u32 one = 1; + bufs_k *p = bpf_map_lookup_elem(&bufk, &one); + if (p == NULL) + return 0; + + u32 two = 2; + bufs_k *store = bpf_map_lookup_elem(&bufk, &two); + if (store == NULL) + return 0; + + bpf_map_update_elem(&bufk, &one, z, BPF_ANY); + int p0; + int p1; + struct data_t *val = bpf_map_lookup_elem(inner, p); + bool fromSourceCheck = true; + + struct file *file_p = get_task_file(t); + if (file_p == NULL) + fromSourceCheck = false; + bufs_t *src_buf = get_buf(PATH_BUFFER); + if (src_buf == NULL) + fromSourceCheck = false; + struct path f_src = BPF_CORE_READ(file_p, f_path); + if (!prepend_path(&f_src, src_buf)) + fromSourceCheck = false; + + u32 *src_offset = get_buf_off(PATH_BUFFER); + if (src_offset == NULL) + fromSourceCheck = false; + + void *ptr = &src_buf->buf[*src_offset]; + + if (fromSourceCheck) { + bpf_probe_read_str(p->source, MAX_STRING_SIZE, ptr); + p0 = 200; + p1 = cap; + p->path[0] = p0 ; + p->path[1] = p1 ; + val = bpf_map_lookup_elem(inner, p); + + if (val) { + match = true; + goto decision; + } + + val = bpf_map_lookup_elem(inner, p); + + + } + // doubt here + bpf_map_update_elem(&bufk, &one, z, BPF_ANY); + + p->path[0] = p0; + p->path[1] = p1; + + val = bpf_map_lookup_elem(inner, p); + + if (val) { + match = true; + goto decision; + } + decision: + + task_info = bpf_ringbuf_reserve(&kubearmor_events, sizeof(event), 0); + if (!task_info) { + return 0; + } + // Clearing arrays to avoid garbage values to be parsed + __builtin_memset(task_info->data.path, 0, sizeof(task_info->data.path)); + __builtin_memset(task_info->data.source, 0, sizeof(task_info->data.source)); + + init_context(task_info); + bpf_probe_read_str(&task_info->data.path, MAX_STRING_SIZE, p->path); + bpf_probe_read_str(&task_info->data.source, MAX_STRING_SIZE, p->source); + + task_info->event_id = _CAPABLE; + + task_info->retval = -EPERM; + + bpf_map_update_elem(&bufk, &one, z, BPF_ANY); + p->path[0] = dcap; + struct data_t *allow = bpf_map_lookup_elem(inner, p); + + if (allow) { + if (!match) { + if(allow->processmask == BLOCK_POSTURE) { + bpf_ringbuf_submit(task_info, BPF_RB_FORCE_WAKEUP); + return -EPERM; + } else { + task_info->retval = 0; + bpf_ringbuf_submit(task_info, BPF_RB_FORCE_WAKEUP); + return 0; + } + } + } else { + if (match) { + if (val && (val->processmask & RULE_DENY)) { + bpf_ringbuf_submit(task_info, BPF_RB_FORCE_WAKEUP); + return -EPERM; + } + } + } + bpf_ringbuf_discard(task_info, BPF_RB_NO_WAKEUP); + + +return 0; + } \ No newline at end of file diff --git a/KubeArmor/BPF/shared.h b/KubeArmor/BPF/shared.h index 7a92c6d3ac..4bf7c3a9d5 100644 --- a/KubeArmor/BPF/shared.h +++ b/KubeArmor/BPF/shared.h @@ -27,7 +27,8 @@ enum file_hook_type { dpath = 0, dfileread, dfilewrite }; enum deny_by_default { dproc = 101, dfile, - dnet + dnet, + dcap }; // check if the list is whitelist/blacklist enum network_check_type { sock_type = 2, @@ -119,6 +120,9 @@ struct data_t { u8 processmask; u8 filemask; }; +// struct data_cap{ +// u64 capability ; +// }; struct outer_hash { __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS); diff --git a/KubeArmor/BPF/syscalls.h b/KubeArmor/BPF/syscalls.h index 806080d479..bae88561b4 100644 --- a/KubeArmor/BPF/syscalls.h +++ b/KubeArmor/BPF/syscalls.h @@ -28,6 +28,9 @@ enum //process _SECURITY_BPRM_CHECK = 352, + // capabilities + _CAPABLE = 464, + }; #endif /* __SYSCALLS_H */ \ No newline at end of file diff --git a/KubeArmor/enforcer/bpflsm/enforcer.go b/KubeArmor/enforcer/bpflsm/enforcer.go index ef7c91a779..44ea898e25 100644 --- a/KubeArmor/enforcer/bpflsm/enforcer.go +++ b/KubeArmor/enforcer/bpflsm/enforcer.go @@ -139,6 +139,11 @@ func NewBPFEnforcer(node tp.Node, pinpath string, logger *fd.Feeder, monitor *mo be.Logger.Errf("opening lsm %s: %s", be.obj.EnforceNetAccept.String(), err) return be, err } + be.Probes[be.obj.EnforceCap.String()], err = link.AttachLSM(link.LSMOptions{Program: be.obj.EnforceCap}) + if err != nil { + be.Logger.Errf("opening lsm %s: %s", be.obj.EnforceCap.String(), err) + return be, err + } /* Path Hooks @@ -338,6 +343,11 @@ func (be *BPFEnforcer) TraceEvents() { log.Source = string(bytes.Trim(event.Data.Source[:], "\x00")) log.Resource = string(bytes.Trim(event.Data.Path[:], "\x00")) log.Data = "lsm=" + mon.GetSyscallName(int32(event.EventID)) + + case mon.Capable: + log.Operation = "Capabilities" + log.Resource = "capability : " + mon.Capabilities[int32(event.Data.Path[1])] + log.Data = "lsm=" + mon.GetSyscallName(int32(event.EventID)) + " " + log.Resource } if event.Retval >= 0 { log.Result = "Passed" diff --git a/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.go b/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.go index 0deafdf43f..2567087499 100644 --- a/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.go +++ b/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.go @@ -60,6 +60,7 @@ type enforcerSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type enforcerProgramSpecs struct { + EnforceCap *ebpf.ProgramSpec `ebpf:"enforce_cap"` EnforceFile *ebpf.ProgramSpec `ebpf:"enforce_file"` EnforceFilePerm *ebpf.ProgramSpec `ebpf:"enforce_file_perm"` EnforceNetAccept *ebpf.ProgramSpec `ebpf:"enforce_net_accept"` @@ -119,6 +120,7 @@ func (m *enforcerMaps) Close() error { // // It can be passed to loadEnforcerObjects or ebpf.CollectionSpec.LoadAndAssign. type enforcerPrograms struct { + EnforceCap *ebpf.Program `ebpf:"enforce_cap"` EnforceFile *ebpf.Program `ebpf:"enforce_file"` EnforceFilePerm *ebpf.Program `ebpf:"enforce_file_perm"` EnforceNetAccept *ebpf.Program `ebpf:"enforce_net_accept"` @@ -129,6 +131,7 @@ type enforcerPrograms struct { func (p *enforcerPrograms) Close() error { return _EnforcerClose( + p.EnforceCap, p.EnforceFile, p.EnforceFilePerm, p.EnforceNetAccept, diff --git a/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.o b/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.o index 6ed17e11bf..97e3489a9e 100644 Binary files a/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.o and b/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.o differ diff --git a/KubeArmor/enforcer/bpflsm/enforcer_bpfel.go b/KubeArmor/enforcer/bpflsm/enforcer_bpfel.go index d197dfacfd..c20bbee4b6 100644 --- a/KubeArmor/enforcer/bpflsm/enforcer_bpfel.go +++ b/KubeArmor/enforcer/bpflsm/enforcer_bpfel.go @@ -60,6 +60,7 @@ type enforcerSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type enforcerProgramSpecs struct { + EnforceCap *ebpf.ProgramSpec `ebpf:"enforce_cap"` EnforceFile *ebpf.ProgramSpec `ebpf:"enforce_file"` EnforceFilePerm *ebpf.ProgramSpec `ebpf:"enforce_file_perm"` EnforceNetAccept *ebpf.ProgramSpec `ebpf:"enforce_net_accept"` @@ -119,6 +120,7 @@ func (m *enforcerMaps) Close() error { // // It can be passed to loadEnforcerObjects or ebpf.CollectionSpec.LoadAndAssign. type enforcerPrograms struct { + EnforceCap *ebpf.Program `ebpf:"enforce_cap"` EnforceFile *ebpf.Program `ebpf:"enforce_file"` EnforceFilePerm *ebpf.Program `ebpf:"enforce_file_perm"` EnforceNetAccept *ebpf.Program `ebpf:"enforce_net_accept"` @@ -129,6 +131,7 @@ type enforcerPrograms struct { func (p *enforcerPrograms) Close() error { return _EnforcerClose( + p.EnforceCap, p.EnforceFile, p.EnforceFilePerm, p.EnforceNetAccept, diff --git a/KubeArmor/enforcer/bpflsm/enforcer_bpfel.o b/KubeArmor/enforcer/bpflsm/enforcer_bpfel.o index 4da1dbe758..c042a91c7c 100644 Binary files a/KubeArmor/enforcer/bpflsm/enforcer_bpfel.o and b/KubeArmor/enforcer/bpflsm/enforcer_bpfel.o differ diff --git a/KubeArmor/enforcer/bpflsm/enforcer_path_bpfeb.o b/KubeArmor/enforcer/bpflsm/enforcer_path_bpfeb.o index cf1f1fa115..bb6d05f3b7 100644 Binary files a/KubeArmor/enforcer/bpflsm/enforcer_path_bpfeb.o and b/KubeArmor/enforcer/bpflsm/enforcer_path_bpfeb.o differ diff --git a/KubeArmor/enforcer/bpflsm/enforcer_path_bpfel.o b/KubeArmor/enforcer/bpflsm/enforcer_path_bpfel.o index 7376c6b49b..91863cbe9b 100644 Binary files a/KubeArmor/enforcer/bpflsm/enforcer_path_bpfel.o and b/KubeArmor/enforcer/bpflsm/enforcer_path_bpfel.o differ diff --git a/KubeArmor/enforcer/bpflsm/rulesHandling.go b/KubeArmor/enforcer/bpflsm/rulesHandling.go index ae87314491..821c88e5f8 100644 --- a/KubeArmor/enforcer/bpflsm/rulesHandling.go +++ b/KubeArmor/enforcer/bpflsm/rulesHandling.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/cilium/ebpf" + mon "github.com/kubearmor/KubeArmor/KubeArmor/monitor" tp "github.com/kubearmor/KubeArmor/KubeArmor/types" ) @@ -26,9 +27,10 @@ const ( // Data Index for rules const ( - PROCESS = 0 - FILE = 1 - NETWORK = 0 + PROCESS = 0 + FILE = 1 + NETWORK = 0 + CAPABILITIES = 0 ) // values for posture and retval @@ -42,6 +44,7 @@ var ( PROCWHITELIST = InnerKey{Path: [256]byte{101}} FILEWHITELIST = InnerKey{Path: [256]byte{102}} NETWHITELIST = InnerKey{Path: [256]byte{103}} + CAPWHITELIST = InnerKey{Path: [256]byte{104}} ) // Protocol Identifiers for Network Rules @@ -69,9 +72,11 @@ type RuleList struct { ProcessRuleList map[InnerKey][2]uint8 FileRuleList map[InnerKey][2]uint8 NetworkRuleList map[InnerKey][2]uint8 + CapabilitiesRuleList map[InnerKey][2]uint8 ProcWhiteListPosture bool FileWhiteListPosture bool NetWhiteListPosture bool + CapWhiteListPosture bool } // Init prepares the RuleList object @@ -84,6 +89,9 @@ func (r *RuleList) Init() { r.NetworkRuleList = make(map[InnerKey][2]uint8) r.NetWhiteListPosture = false + + r.CapabilitiesRuleList = make(map[InnerKey][2]uint8) + r.CapWhiteListPosture = false } // UpdateContainerRules updates individual container map with new rules and resolves conflicting rules @@ -272,6 +280,39 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec } } } + for _, capab := range secPolicy.Spec.Capabilities.MatchCapabilities { + var val [2]uint8 + var key = InnerKey{Path: [256]byte{}, Source: [256]byte{}} + + key.Path[0] = 200 + key.Path[1] = mon.CapToCode[strings.ToUpper(capab.Capability)] + + if len(capab.FromSource) == 0 { + if capab.Action == "Allow" { + newrules.CapWhiteListPosture = true + newrules.CapabilitiesRuleList[key] = val + + } else if capab.Action == "Block" { + val[CAPABILITIES] = val[CAPABILITIES] | DENY + newrules.CapabilitiesRuleList[key] = val + } + } else { + for _, src := range capab.FromSource { + var source [256]byte + copy(source[:], []byte(src.Path)) + key.Source = source + if capab.Action == "Allow" { + newrules.CapWhiteListPosture = true + newrules.CapabilitiesRuleList[key] = val + + } else if capab.Action == "Block" { + val[CAPABILITIES] = val[CAPABILITIES] | DENY + newrules.CapabilitiesRuleList[key] = val + } + + } + } + } } fuseProcAndFileRules(newrules.ProcessRuleList, newrules.FileRuleList) @@ -286,11 +327,11 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec return } - if be.ContainerMap[id].Map == nil && !(len(newrules.FileRuleList) == 0 && len(newrules.ProcessRuleList) == 0 && len(newrules.NetworkRuleList) == 0) { + if be.ContainerMap[id].Map == nil && !(len(newrules.FileRuleList) == 0 && len(newrules.ProcessRuleList) == 0 && len(newrules.NetworkRuleList) == 0 && len(newrules.CapabilitiesRuleList) == 0) { // We create the inner map only when we have policies specific to that be.Logger.Printf("Creating inner map for %s", id) be.CreateContainerInnerMap(id) - } else if len(newrules.FileRuleList) == 0 && len(newrules.ProcessRuleList) == 0 && len(newrules.NetworkRuleList) == 0 { + } else if len(newrules.FileRuleList) == 0 && len(newrules.ProcessRuleList) == 0 && len(newrules.NetworkRuleList) == 0 && len(newrules.CapabilitiesRuleList) == 0 { // All Policies removed for the container be.Logger.Printf("Deleting inner map for %s", id) be.DeleteContainerInnerMap(id) @@ -301,12 +342,14 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec be.resolveConflicts(newrules.ProcWhiteListPosture, be.ContainerMap[id].Rules.ProcWhiteListPosture, newrules.ProcessRuleList, be.ContainerMap[id].Rules.ProcessRuleList, be.ContainerMap[id].Map) be.resolveConflicts(newrules.FileWhiteListPosture, be.ContainerMap[id].Rules.FileWhiteListPosture, newrules.FileRuleList, be.ContainerMap[id].Rules.FileRuleList, be.ContainerMap[id].Map) be.resolveConflicts(newrules.NetWhiteListPosture, be.ContainerMap[id].Rules.NetWhiteListPosture, newrules.NetworkRuleList, be.ContainerMap[id].Rules.NetworkRuleList, be.ContainerMap[id].Map) + be.resolveConflicts(newrules.CapWhiteListPosture, be.ContainerMap[id].Rules.CapWhiteListPosture, newrules.CapabilitiesRuleList, be.ContainerMap[id].Rules.CapabilitiesRuleList, be.ContainerMap[id].Map) // Update Posture if list, ok := be.ContainerMap[id]; ok { list.Rules.ProcWhiteListPosture = newrules.ProcWhiteListPosture list.Rules.FileWhiteListPosture = newrules.FileWhiteListPosture list.Rules.NetWhiteListPosture = newrules.NetWhiteListPosture + list.Rules.CapWhiteListPosture = newrules.CapWhiteListPosture be.ContainerMap[id] = list } @@ -383,6 +426,29 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec be.Logger.Errf("error adding rule to map for container %s: %s", id, err) } } + if newrules.CapWhiteListPosture { + if defaultPosture.CapabilitiesAction == "block" { + if err := be.ContainerMap[id].Map.Put(CAPWHITELIST, [2]uint8{BlockPosture}); err != nil { + be.Logger.Errf("error adding network key rule to map for container %s: %s", id, err) + } + } else { + if err := be.ContainerMap[id].Map.Put(CAPWHITELIST, [2]uint8{AuditPosture}); err != nil { + be.Logger.Errf("error adding network key rule to map for container %s: %s", id, err) + } + } + } else { + if err := be.ContainerMap[id].Map.Delete(CAPWHITELIST); err != nil { + if !errors.Is(err, os.ErrNotExist) { + be.Logger.Err(err.Error()) + } + } + } + for key, val := range newrules.CapabilitiesRuleList { + be.ContainerMap[id].Rules.CapabilitiesRuleList[key] = val + if err := be.ContainerMap[id].Map.Put(key, val); err != nil { + be.Logger.Errf("error adding rule to map for container %s: %s", id, err) + } + } } func fuseProcAndFileRules(procList, fileList map[InnerKey][2]uint8) { diff --git a/KubeArmor/feeder/policyMatcher.go b/KubeArmor/feeder/policyMatcher.go index fe952f4bad..7734e91571 100644 --- a/KubeArmor/feeder/policyMatcher.go +++ b/KubeArmor/feeder/policyMatcher.go @@ -4,6 +4,7 @@ package feeder import ( + "fmt" "os" "path/filepath" "regexp" @@ -47,18 +48,18 @@ func getFileProcessUID(path string) string { return "" } -// getOperationAndCapabilityFromName Function -func getOperationAndCapabilityFromName(capName string) (op, capability string) { - switch strings.ToLower(capName) { - case "net_raw": - op = "Network" - capability = "SOCK_RAW" - default: - return "", "unknown" - } +// // getOperationAndCapabilityFromName Function +// func getOperationAndCapabilityFromName(capName string) (op, capability string) { +// switch strings.ToLower(capName) { +// case "net_raw": +// op = "Network" +// capability = "SOCK_RAW" +// default: +// return "", "unknown" +// } - return op, capability -} +// return op, capability +// } // newMatchPolicy Function func (fd *Feeder) newMatchPolicy(policyEnabled int, policyName, src string, mp interface{}) tp.MatchPolicy { @@ -201,11 +202,8 @@ func (fd *Feeder) newMatchPolicy(policyEnabled int, policyName, src string, mp i match.Severity = strconv.Itoa(cct.Severity) match.Tags = cct.Tags match.Message = cct.Message - - op, cap := getOperationAndCapabilityFromName(cct.Capability) - - match.Operation = op - match.Resource = cap + match.Operation = "Capabilities" + match.Resource = strings.ToUpper(cct.Capability) match.ResourceType = "Capability" if policyEnabled == tp.KubeArmorPolicyAudited && cct.Action == "Allow" { @@ -923,7 +921,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 @@ -974,6 +971,7 @@ func (fd *Feeder) UpdateMatchedPolicy(log tp.Log) tp.Log { continue } } + // fmt.Printf(" name : %s ,operation : %s , resource %s , source %s ", secPolicy.PolicyName, secPolicy.Operation, secPolicy.Resource, secPolicy.Source) firstLogResource := strings.Split(log.Resource, " ")[0] firstLogResourceDir := getDirectoryPart(firstLogResource) @@ -1377,6 +1375,188 @@ func (fd *Feeder) UpdateMatchedPolicy(log tp.Log) tp.Log { log.Enforcer = "eBPF Monitor" log.Action = "Audit" } + + case "Capabilities": + fmt.Println("policy :", secPolicy) + fmt.Println("log :", log) + if secPolicy.Operation != log.Operation { + continue + } + // match sources + if (!secPolicy.IsFromSource) || (secPolicy.IsFromSource && (secPolicy.Source == log.ParentProcessName || secPolicy.Source == log.ProcessName)) { + skip := false + + for _, matchCapability := range strings.Split(secPolicy.Resource, ",") { + if skip { + break + } + + // match resources + //fmt.Println(matchCapability) + fmt.Println(log.Resource, matchCapability) + if strings.Contains(log.Resource, matchCapability) { + + 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 + + log.Type = "MatchedPolicy" + + log.PolicyName = secPolicy.PolicyName + log.Severity = secPolicy.Severity + + if len(secPolicy.Tags) > 0 { + log.Tags = strings.Join(secPolicy.Tags[:], ",") + log.ATags = secPolicy.Tags + } + + if len(secPolicy.Message) > 0 { + log.Message = secPolicy.Message + } + + if log.PolicyEnabled == tp.KubeArmorPolicyAudited { + log.Enforcer = "eBPF Monitor" + } else { + log.Enforcer = fd.Enforcer + } + + log.Action = "Allow" + + skip = true + continue + } + + if secPolicy.Action == "Audit" && log.Result == "Passed" { + // audit policy + // matched source + matched resource + matched action + expected result -> alert (audit log) + + log.Type = "MatchedPolicy" + + log.PolicyName = secPolicy.PolicyName + log.Severity = secPolicy.Severity + + if len(secPolicy.Tags) > 0 { + log.Tags = strings.Join(secPolicy.Tags[:], ",") + log.ATags = secPolicy.Tags + } + + if len(secPolicy.Message) > 0 { + log.Message = secPolicy.Message + } + + log.Enforcer = "eBPF Monitor" + log.Action = secPolicy.Action + + skip = true + continue + } + + 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.Type = "MatchedPolicy" + + log.PolicyName = secPolicy.PolicyName + log.Severity = secPolicy.Severity + + if len(secPolicy.Tags) > 0 { + log.Tags = strings.Join(secPolicy.Tags[:], ",") + } + + if len(secPolicy.Message) > 0 { + log.Message = secPolicy.Message + } + + if log.PolicyEnabled == tp.KubeArmorPolicyAudited { + log.Enforcer = "eBPF Monitor" + } else { + log.Enforcer = fd.Enforcer + } + + log.Action = secPolicy.Action + + skip = true + continue + } + } + } + + if skip { + continue + } + + if secPolicy.Action == "Allow" && log.Result != "Passed" { + // matched source + !(matched resource) + action = allow + result = blocked -> allow policy violation + + log.Type = "MatchedPolicy" + + log.PolicyName = "DefaultPosture" + + log.Severity = "" + log.Tags = "" + log.Message = "" + + log.Enforcer = "eBPF Monitor" + log.Action = "Block" + + continue + } + + if secPolicy.Action == "Audit (Allow)" && log.Result == "Passed" { + // matched source + !(matched resource) + action = audit (allow) + result = passed -> allow policy violation (audit mode) + + log.Type = "MatchedPolicy" + + log.PolicyName = "DefaultPosture" + + log.Severity = "" + log.Tags = "" + log.Message = "" + + log.Enforcer = "eBPF Monitor" + + if fd.DefaultPostures[log.NamespaceName].CapabilitiesAction == "block" { + log.Action = "Audit (Block)" + } else { // fd.DefaultPostures[log.NamespaceName].CapabilitiesAction == "audit" + log.Action = "Audit" + } + + continue + } + } + + if fd.DefaultPostures[log.NamespaceName].CapabilitiesAction == "block" && secPolicy.Action == "Audit (Allow)" && log.Result == "Passed" { + // defaultPosture = block + audit mode + + log.Type = "MatchedPolicy" + + log.PolicyName = "DefaultPosture" + + log.Severity = "" + log.Tags = "" + log.Message = "" + + log.Enforcer = "eBPF Monitor" + log.Action = "Audit (Block)" + } + + if fd.DefaultPostures[log.NamespaceName].CapabilitiesAction == "audit" && (secPolicy.Action == "Allow" || secPolicy.Action == "Audit (Allow)") && log.Result == "Passed" { + // defaultPosture = audit + + log.Type = "MatchedPolicy" + + log.PolicyName = "DefaultPosture" + + log.Severity = "" + log.Tags = "" + log.Message = "" + + log.Enforcer = "eBPF Monitor" + log.Action = "Audit" + } + case "Syscall": if secPolicy.Operation != log.Operation { continue diff --git a/KubeArmor/monitor/syscallParser.go b/KubeArmor/monitor/syscallParser.go index 151ad92d48..4826a77871 100644 --- a/KubeArmor/monitor/syscallParser.go +++ b/KubeArmor/monitor/syscallParser.go @@ -16,6 +16,8 @@ import ( "net" "strconv" "strings" + + "golang.org/x/sys/unix" ) // ===================== // @@ -565,7 +567,8 @@ func GetProtocol(proto int32) string { return res } -var capabilities = map[int32]string{ +// Capabilities code to name +var Capabilities = map[int32]string{ 0: "CAP_CHOWN", 1: "CAP_DAC_OVERRIDE", 2: "CAP_DAC_READ_SEARCH", @@ -605,6 +608,48 @@ var capabilities = map[int32]string{ 36: "CAP_BLOCK_SUSPEND", 37: "CAP_AUDIT_READ", } +var CapToCode = map[string]uint8{ + "AUDIT_CONTROL": unix.CAP_AUDIT_CONTROL, + "AUDIT_READ": unix.CAP_AUDIT_READ, + "AUDIT_WRITE": unix.CAP_AUDIT_WRITE, + "DAC_READ_SEARCH": unix.CAP_DAC_READ_SEARCH, + "DAC_OVERRIDE": unix.CAP_DAC_OVERRIDE, + "LINUX_IMMUTABLE": unix.CAP_LINUX_IMMUTABLE, + "NET_BROADCAST": unix.CAP_NET_BROADCAST, + "NET_ADMIN": unix.CAP_NET_ADMIN, + "NET_BIND_SERVICE": unix.CAP_NET_BIND_SERVICE, + "NET_RAW": unix.CAP_NET_RAW, + "IPC_LOCK": unix.CAP_IPC_LOCK, + "IPC_OWNER": unix.CAP_IPC_OWNER, + "SYS_MODULE": unix.CAP_SYS_MODULE, + "SYS_RAWIO": unix.CAP_SYS_RAWIO, + "SYS_PTRACE": unix.CAP_SYS_PTRACE, + "SYS_PACCT": unix.CAP_SYS_PACCT, + "SYS_ADMIN": unix.CAP_SYS_ADMIN, + "SYS_BOOT": unix.CAP_SYS_BOOT, + "SYS_NICE": unix.CAP_SYS_NICE, + "SYS_RESOURCE": unix.CAP_SYS_RESOURCE, + "SYS_TIME": unix.CAP_SYS_TIME, + "SYS_TTY_CONFIG": unix.CAP_SYS_TTY_CONFIG, + "SYS_CHROOT": unix.CAP_SYS_CHROOT, + "SYSLOG": unix.CAP_SYSLOG, + "LEASE": unix.CAP_LEASE, + "MAC_OVERRIDE": unix.CAP_MAC_OVERRIDE, + "MAC_ADMIN": unix.CAP_MAC_ADMIN, + "WAKE_ALARM": unix.CAP_WAKE_ALARM, + "BLOCK_SUSPEND": unix.CAP_BLOCK_SUSPEND, + "CHOWN": unix.CAP_CHOWN, + "FOWNER": unix.CAP_FOWNER, + "FSETID": unix.CAP_FSETID, + "KILL": unix.CAP_KILL, + "SETGID": unix.CAP_SETGID, + "SETUID": unix.CAP_SETUID, + "SETPCAP": unix.CAP_SETPCAP, + "PERFMON": unix.CAP_PERFMON, + "MKNOD": unix.CAP_MKNOD, + "SETFCAP": unix.CAP_SETFCAP, + "BPF": unix.CAP_BPF, +} // getCapabilityName Function func getCapabilityName(cap int32) string { @@ -613,7 +658,7 @@ func getCapabilityName(cap int32) string { var res string - if capName, ok := capabilities[cap]; ok { + if capName, ok := Capabilities[cap]; ok { res = capName } else { res = strconv.Itoa(int(cap)) diff --git a/KubeArmor/monitor/syscalls_amd64.go b/KubeArmor/monitor/syscalls_amd64.go index 5a9273230e..7e1202fc18 100644 --- a/KubeArmor/monitor/syscalls_amd64.go +++ b/KubeArmor/monitor/syscalls_amd64.go @@ -59,6 +59,8 @@ const ( SocketCreate = 461 SocketConnect = 462 SocketAccept = 463 + + Capable = 464 ) var syscalls = map[int32]string{ @@ -412,4 +414,5 @@ var syscalls = map[int32]string{ 461: "SOCKET_CREATE", 462: "SOCKET_CONNECT", 463: "SOCKET_ACCEPT", + 464: "CAPABLE", } diff --git a/KubeArmor/monitor/syscalls_arm64.go b/KubeArmor/monitor/syscalls_arm64.go index 536ced6669..2066041232 100644 --- a/KubeArmor/monitor/syscalls_arm64.go +++ b/KubeArmor/monitor/syscalls_arm64.go @@ -62,6 +62,8 @@ const ( SocketCreate = 461 SocketConnect = 462 SocketAccept = 463 + + Capable = 464 ) var syscalls = map[int32]string{ @@ -374,4 +376,5 @@ var syscalls = map[int32]string{ 461: "SOCKET_CREATE", 462: "SOCKET_CONNECT", 463: "SOCKET_ACCEPT", + 464: "CAPAB", } diff --git a/KubeArmor/utils/bpflsmprobe/probe_bpfeb.o b/KubeArmor/utils/bpflsmprobe/probe_bpfeb.o index 48e797fd5e..0775008831 100644 Binary files a/KubeArmor/utils/bpflsmprobe/probe_bpfeb.o and b/KubeArmor/utils/bpflsmprobe/probe_bpfeb.o differ diff --git a/KubeArmor/utils/bpflsmprobe/probe_bpfel.o b/KubeArmor/utils/bpflsmprobe/probe_bpfel.o index d5d022eccd..4446f3ada9 100644 Binary files a/KubeArmor/utils/bpflsmprobe/probe_bpfel.o and b/KubeArmor/utils/bpflsmprobe/probe_bpfel.o differ diff --git a/tests/k8s_env/ksp/ksp_test.go b/tests/k8s_env/ksp/ksp_test.go index 30ba670f0f..27805c977e 100644 --- a/tests/k8s_env/ksp/ksp_test.go +++ b/tests/k8s_env/ksp/ksp_test.go @@ -132,9 +132,9 @@ var _ = Describe("Ksp", func() { It("it can block all network traffic on net-raw protocol", func() { // multiubuntu_test_03, github_test_10 - if strings.Contains(K8sRuntimeEnforcer(), "bpf") { - Skip("Skipping due to policy not supported by bpflsm enforcer") - } + // if strings.Contains(K8sRuntimeEnforcer(), "bpf") { + // Skip("Skipping due to policy not supported by bpflsm enforcer") + // } // Apply Policy err := K8sApplyFile("multiubuntu/ksp-ubuntu-1-block-net-raw-cap.yaml") @@ -154,7 +154,6 @@ var _ = Describe("Ksp", func() { PolicyName: "ksp-ubuntu-1-block-net-raw-cap", Severity: "1", Action: "Block", - Result: "Operation not permitted", } res, err := KarmorGetTargetAlert(5*time.Second, &expect)