diff --git a/KubeArmor/BPF/enforcer.bpf.c b/KubeArmor/BPF/enforcer.bpf.c index 23f71ed9ff..383ccab78e 100644 --- a/KubeArmor/BPF/enforcer.bpf.c +++ b/KubeArmor/BPF/enforcer.bpf.c @@ -3,10 +3,12 @@ /* Copyright 2023 Authors of KubeArmor */ #include "shared.h" +#include "syscalls.h" SEC("lsm/bprm_check_security") int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) { struct task_struct *t = (struct task_struct *)bpf_get_current_task(); + event *task_info; bool match = false; @@ -98,10 +100,6 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) { if ((dirval->processmask & RULE_DIR) && (dirval->processmask & RULE_EXEC)) { match = true; - bpf_printk("dir match %s with recursive %d and hint %d ", pk, - (dirval->processmask & RULE_RECURSIVE), - (dirval->processmask & RULE_HINT)); - bpf_printk("and from source %s\n", pk->source); if ((dirval->processmask & RULE_RECURSIVE) && (~dirval->processmask & RULE_HINT)) { // true directory match and not a hint suggests @@ -166,9 +164,6 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) { if ((dirval->processmask & RULE_DIR) && (dirval->processmask & RULE_EXEC)) { match = true; - bpf_printk("dir match %s with recursive %d and hint %d\n", pk, - (dirval->processmask & RULE_RECURSIVE), - (dirval->processmask & RULE_HINT)); if ((dirval->processmask & RULE_RECURSIVE) && (~dirval->processmask & RULE_HINT)) { // true directory match and not a hint suggests @@ -203,39 +198,61 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) { } decision: + task_info = bpf_ringbuf_reserve(&events, sizeof(event), 0); + if (!task_info) { + return 0; + } + +// Clearing arrays to avoid garbage values + __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, store->path); + bpf_probe_read_str(&task_info->data.source, MAX_STRING_SIZE, + store->source); task_info->event_id = _SECURITY_BPRM_CHECK; + task_info->retval = -EPERM; if (match) { if (val && (val->processmask & RULE_OWNER)) { if (!is_owner(bprm->file)) { - bpf_printk("denying proc %s due to not owner\n", store); + bpf_ringbuf_submit(task_info, 0); return -EPERM; } else { - bpf_printk("allowing proc %s for owner\n", store); + bpf_ringbuf_discard(task_info, 0); return ret; } } if (val && (val->processmask & RULE_DENY)) { - bpf_printk("denying proc %s due to in blacklist\n", store->path); + bpf_ringbuf_submit(task_info, 0); return -EPERM; } - } + } bpf_map_update_elem(&bufk, &two, z, BPF_ANY); pk->path[0] = dproc; struct data_t *allow = bpf_map_lookup_elem(inner, pk); if (allow) { + if (!match) { - bpf_printk("denying proc %s due to not in allowlist, source -> %s\n", - store->path, store->source); + bpf_ringbuf_submit(task_info, 0); return -EPERM; } + // Do not remove this else block + else { + bpf_ringbuf_discard(task_info, 0); + return ret; + } } + bpf_ringbuf_discard(task_info, 0); return ret; } -static inline int match_net_rules(int type, int protocol, char *string) { +static inline int match_net_rules(int type, int protocol, u32 eventID) { + event *task_info; + struct task_struct *t = (struct task_struct *)bpf_get_current_task(); bool match = false; @@ -330,55 +347,66 @@ static inline int match_net_rules(int type, int protocol, char *string) { decision: + task_info = bpf_ringbuf_reserve(&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, ptr); + + task_info->event_id = eventID; + + task_info->retval = -EPERM; + bpf_map_update_elem(&bufk, &one, z, BPF_ANY); p->path[0] = dnet; struct data_t *allow = bpf_map_lookup_elem(inner, p); if (allow) { if (!match) { - bpf_printk("denying sock %s - type %d, protocol %d due " - "to not in allowlist\n", - string, type, protocol); - if (p->source[0] != '\0') { - bpf_printk("denying from source from %s", store->source); - } + bpf_ringbuf_submit(task_info, 0); return -EPERM; } } else { if (match) { if (val && (val->processmask & RULE_DENY)) { - bpf_printk("denying sock %s - type %d, protocol %d due to in " - "blacklist\n", - string, type, protocol); + bpf_ringbuf_submit(task_info, 0); return -EPERM; } } } + + bpf_ringbuf_discard(task_info, 0); return 0; } SEC("lsm/socket_create") int BPF_PROG(enforce_net_create, int family, int type, int protocol) { - return match_net_rules(type, protocol, "create"); + return match_net_rules(type, protocol, _SOCKET_CREATE); } -#define LSM_NET(name, string) \ +#define LSM_NET(name, ID) \ int BPF_PROG(name, struct socket *sock) { \ int type = sock->type; \ int protocol = sock->sk->sk_protocol; \ - return match_net_rules(type, protocol, string); \ + return match_net_rules(type, protocol, ID); \ } SEC("lsm/socket_connect") -LSM_NET(enforce_net_connect, "connect"); +LSM_NET(enforce_net_connect, _SOCKET_CONNECT); SEC("lsm/socket_accept") -LSM_NET(enforce_net_accept, "accept"); +LSM_NET(enforce_net_accept, _SOCKET_ACCEPT); SEC("lsm/file_open") int BPF_PROG(enforce_file, struct file *file) { // check if ret code available struct path f_path = BPF_CORE_READ(file, f_path); - return match_and_enforce_path_hooks(&f_path, dfileread); + return match_and_enforce_path_hooks(&f_path, dfileread, _FILE_OPEN); } SEC("lsm/file_permission") @@ -390,5 +418,5 @@ 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); + return match_and_enforce_path_hooks(&f_path, dfilewrite, _FILE_PERMISSION); } \ No newline at end of file diff --git a/KubeArmor/BPF/enforcer_path.bpf.c b/KubeArmor/BPF/enforcer_path.bpf.c index 18ee3cf33b..ec619f078e 100644 --- a/KubeArmor/BPF/enforcer_path.bpf.c +++ b/KubeArmor/BPF/enforcer_path.bpf.c @@ -3,21 +3,22 @@ /* Copyright 2023 Authors of KubeArmor */ #include "shared.h" +#include "syscalls.h" -#define PATH_SEC_CALL(NAME) \ +#define PATH_SEC_CALL(NAME , ID) \ SEC("lsm/path_" #NAME) \ int BPF_PROG(enforce_##NAME, struct path *dir, struct dentry *dentry) { \ struct path f_path; \ f_path.dentry = dentry; \ f_path.mnt = BPF_CORE_READ(dir, mnt); \ - return match_and_enforce_path_hooks(&f_path, dpath); \ + return match_and_enforce_path_hooks(&f_path, dpath, ID); \ } -PATH_SEC_CALL(mknod) -PATH_SEC_CALL(rmdir) -PATH_SEC_CALL(unlink) -PATH_SEC_CALL(symlink) -PATH_SEC_CALL(mkdir) +PATH_SEC_CALL(mknod , _FILE_MKNOD) +PATH_SEC_CALL(rmdir , _FILE_RMDIR) +PATH_SEC_CALL(unlink , _FILE_UNLINK) +PATH_SEC_CALL(symlink , _FILE_SYMLINK) +PATH_SEC_CALL(mkdir , _FILE_MKDIR) SEC("lsm/path_link") int BPF_PROG(enforce_link_src, struct dentry *old_dentry, struct path *dir, @@ -25,7 +26,7 @@ int BPF_PROG(enforce_link_src, struct dentry *old_dentry, struct path *dir, struct path f_path; f_path.dentry = old_dentry; f_path.mnt = BPF_CORE_READ(dir, mnt); - return match_and_enforce_path_hooks(&f_path, dpath); + return match_and_enforce_path_hooks(&f_path, dpath, _FILE_LINK ); } SEC("lsm/path_link") @@ -34,7 +35,7 @@ int BPF_PROG(enforce_link_dst, struct dentry *old_dentry, struct path *dir, struct path f_path; f_path.dentry = new_dentry; f_path.mnt = BPF_CORE_READ(dir, mnt); - return match_and_enforce_path_hooks(&f_path, dpath); + return match_and_enforce_path_hooks(&f_path, dpath, _FILE_LINK); } SEC("lsm/path_rename") @@ -43,7 +44,7 @@ int BPF_PROG(enforce_rename_old, struct path *old_dir, struct path f_path; f_path.dentry = old_dentry; f_path.mnt = BPF_CORE_READ(old_dir, mnt); - return match_and_enforce_path_hooks(&f_path, dpath); + return match_and_enforce_path_hooks(&f_path, dpath , _FILE_RENAME); } SEC("lsm/path_rename") @@ -53,12 +54,12 @@ int BPF_PROG(enforce_rename_new, struct path *old_dir, struct path f_path; f_path.dentry = new_dentry; f_path.mnt = BPF_CORE_READ(new_dir, mnt); - return match_and_enforce_path_hooks(&f_path, dpath); + return match_and_enforce_path_hooks(&f_path, dpath ,_FILE_RENAME); } SEC("lsm/path_chmod") int BPF_PROG(enforce_chmod, struct path *p) { - return match_and_enforce_path_hooks(p, dpath); + return match_and_enforce_path_hooks(p, dpath , _FILE_CHMOD); } // SEC("lsm/path_chown") @@ -68,5 +69,5 @@ int BPF_PROG(enforce_chmod, struct path *p) { SEC("lsm/path_truncate") int BPF_PROG(enforce_truncate, struct path *p) { - return match_and_enforce_path_hooks(p, dpath); + return match_and_enforce_path_hooks(p, dpath, _FILE_TRUNCATE); } diff --git a/KubeArmor/BPF/shared.h b/KubeArmor/BPF/shared.h index 8ef28841df..096f9614da 100644 --- a/KubeArmor/BPF/shared.h +++ b/KubeArmor/BPF/shared.h @@ -17,6 +17,7 @@ char LICENSE[] SEC("license") = "Dual BSD/GPL"; #define MAX_STRING_SIZE 256 #define MAX_BUFFERS 1 #define PATH_BUFFER 0 +#define TASK_COMM_LEN 80 enum file_hook_type { dpath = 0, dfileread, dfilewrite }; @@ -59,7 +60,7 @@ struct { __uint(max_entries, MAX_BUFFERS); } bufs_off SEC(".maps"); -struct { +struct { __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); __type(key, u32); __type(value, bufs_k); @@ -71,6 +72,33 @@ struct outer_key { u32 mnt_ns; }; +typedef struct { + u64 ts; + + u32 pid_id; + u32 mnt_id; + + u32 host_ppid; + u32 host_pid; + + u32 ppid; + u32 pid; + u32 uid; + + u32 event_id; + s64 retval; + + u8 comm[TASK_COMM_LEN]; + + bufs_k data; +} event; + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 1 << 24); + __uint(pinning, LIBBPF_PIN_BY_NAME); +} events SEC(".maps"); + #define RULE_EXEC 1 << 0 #define RULE_WRITE 1 << 1 #define RULE_READ 1 << 2 @@ -194,6 +222,26 @@ static __always_inline u32 get_task_mnt_ns_id(struct task_struct *task) { return BPF_CORE_READ(task, nsproxy, mnt_ns, ns).inum; } +static __always_inline u32 get_task_pid_vnr(struct task_struct *task) { + struct pid *pid = BPF_CORE_READ(task, thread_pid); + unsigned int level = BPF_CORE_READ(pid, level); + return BPF_CORE_READ(pid, numbers[level].nr); +} + +static __always_inline u32 get_task_ns_ppid(struct task_struct *task) { + struct task_struct *real_parent = BPF_CORE_READ(task, real_parent); + return get_task_pid_vnr(real_parent); +} + +static __always_inline u32 get_task_ns_tgid(struct task_struct *task) { + struct task_struct *group_leader = BPF_CORE_READ(task, group_leader); + return get_task_pid_vnr(group_leader); +} + +static __always_inline u32 get_task_ppid(struct task_struct *task) { + return BPF_CORE_READ(task, parent, pid); +} + static struct file *get_task_file(struct task_struct *task) { return BPF_CORE_READ(task, mm, exe_file); } @@ -208,6 +256,40 @@ static inline void get_outer_key(struct outer_key *pokey, } } +// == Context Management == // + +static __always_inline u32 init_context(event *event_data) { + struct task_struct *task = (struct task_struct *)bpf_get_current_task(); + + event_data->ts = bpf_ktime_get_ns(); + + event_data->host_ppid = get_task_ppid(task); + event_data->host_pid = bpf_get_current_pid_tgid() >> 32; + + u32 pid = get_task_ns_tgid(task); + if (event_data->host_pid == pid) { // host + event_data->pid_id = 0; + event_data->mnt_id = 0; + + event_data->ppid = get_task_ppid(task); + event_data->pid = bpf_get_current_pid_tgid() >> 32; + } else { // container + event_data->pid_id = get_task_pid_ns_id(task); + event_data->mnt_id = get_task_mnt_ns_id(task); + + event_data->ppid = get_task_ns_ppid(task); + event_data->pid = pid; + } + + event_data->uid = bpf_get_current_uid_gid(); + +// Clearing array to avoid garbage values + __builtin_memset(event_data->comm, 0, sizeof(event_data->comm)); + bpf_get_current_comm(&event_data->comm, sizeof(event_data->comm)); + + return 0; +} + static bool is_owner(struct file *file_p) { kuid_t owner = BPF_CORE_READ(file_p, f_inode, i_uid); unsigned int z = bpf_get_current_uid_gid(); @@ -224,9 +306,11 @@ static bool is_owner_path(struct dentry *dent) { return true; } -static inline int match_and_enforce_path_hooks(struct path *f_path, u32 id) { +static inline int match_and_enforce_path_hooks(struct path *f_path, u32 id , u32 eventID) { struct task_struct *t = (struct task_struct *)bpf_get_current_task(); + event *task_info; + bool match = false; struct outer_key okey; @@ -325,10 +409,6 @@ static inline int match_and_enforce_path_hooks(struct path *f_path, u32 id) { if (dirval) { if ((dirval->filemask & RULE_DIR) && (dirval->filemask & RULE_READ)) { match = true; - bpf_printk("dir match %s with recursive %d and hint %d ", pk, - (dirval->filemask & RULE_RECURSIVE), - (dirval->filemask & RULE_HINT)); - bpf_printk("and from source %s\n", pk->source); if ((dirval->filemask & RULE_RECURSIVE)) { /* true directory match and */ /* not a hint suggests */ @@ -390,9 +470,6 @@ static inline int match_and_enforce_path_hooks(struct path *f_path, u32 id) { } if ((dirval->filemask & RULE_DIR) && (dirval->filemask & RULE_READ)) { match = true; - bpf_printk("dir match %s with recursive %d and hint %d ", pk, - (dirval->filemask & RULE_RECURSIVE), - (dirval->filemask & RULE_HINT)); if (!(dirval->filemask & RULE_RECURSIVE)) { continue; /* continue the loop to see if we have more nested dirs */ } @@ -418,18 +495,34 @@ static inline int match_and_enforce_path_hooks(struct path *f_path, u32 id) { decision: + task_info = bpf_ringbuf_reserve(&events, sizeof(event), 0); + if (!task_info) { + return 0; + } + + init_context(task_info); + // Clearing arrays to avoid garbage values + __builtin_memset(task_info->data.path, 0, sizeof(task_info->data.path)); + __builtin_memset(task_info->data.source, 0, sizeof(task_info->data.source)); + + bpf_probe_read_str(&task_info->data.path, MAX_STRING_SIZE, store->path); + bpf_probe_read_str(&task_info->data.source, MAX_STRING_SIZE, store->source); + + task_info->event_id = eventID; + task_info->retval = -EPERM; + if (id == dpath) { // Path Hooks if (match) { if (val && (val->filemask & RULE_OWNER)) { if (!is_owner_path(f_path->dentry)) { - bpf_printk("denying path %s due to not owner\n", store); + bpf_ringbuf_submit(task_info, 0); return -EPERM; } - bpf_printk("allowing path %s for owner\n", store); + bpf_ringbuf_discard(task_info, 0); return 0; } if (val && (val->filemask & RULE_DENY)) { - bpf_printk("denying path %s due to in blacklist\n", store->path); + bpf_ringbuf_submit(task_info, 0); return -EPERM; } } @@ -440,8 +533,7 @@ static inline int match_and_enforce_path_hooks(struct path *f_path, u32 id) { if (allow) { if (!match) { - bpf_printk("denying path %s due to not in allowlist, source -> %s\n", - store->path, store->source); + bpf_ringbuf_submit(task_info, 0); return -EPERM; } } @@ -450,18 +542,19 @@ static inline int match_and_enforce_path_hooks(struct path *f_path, u32 id) { if (match) { if (val && (val->filemask & RULE_OWNER)) { if (!is_owner_path(f_path->dentry)) { - bpf_printk("denying file %s due to not owner\n", store); + bpf_ringbuf_submit(task_info, 0); return -EPERM; } - bpf_printk("allowing file %s for owner\n", store); + bpf_ringbuf_discard(task_info, 0); return 0; } if (val && (val->filemask & RULE_READ) && !(val->filemask & RULE_WRITE)) { + bpf_ringbuf_discard(task_info, 0); // Read Only Policy, Decision making will be done in lsm/file_permission return 0; } if (val && (val->filemask & RULE_DENY)) { - bpf_printk("denying file %s due to in blacklist\n", store->path); + bpf_ringbuf_submit(task_info, 0); return -EPERM; } } @@ -471,14 +564,13 @@ static inline int match_and_enforce_path_hooks(struct path *f_path, u32 id) { struct data_t *allow = bpf_map_lookup_elem(inner, pk); if (allow && !match) { - bpf_printk("denying file %s due to not in allowlist, source -> %s\n", - store->path, store->source); + bpf_ringbuf_submit(task_info, 0); return -EPERM; } } else if (id == dfilewrite) { // fule write if (match) { if (val && (val->filemask & RULE_DENY)) { - bpf_printk("denying file %s due to in blacklist\n", store->path); + bpf_ringbuf_submit(task_info, 0); return -EPERM; } } @@ -488,11 +580,11 @@ static inline int match_and_enforce_path_hooks(struct path *f_path, u32 id) { struct data_t *allow = bpf_map_lookup_elem(inner, pk); if (allow && !match) { - bpf_printk("denying file %s due to not in allowlist, source -> %s\n", - store->path, store->source); + bpf_ringbuf_submit(task_info, 0); return -EPERM; } } + bpf_ringbuf_discard(task_info, 0); return 0; } diff --git a/KubeArmor/BPF/syscalls.h b/KubeArmor/BPF/syscalls.h new file mode 100644 index 0000000000..806080d479 --- /dev/null +++ b/KubeArmor/BPF/syscalls.h @@ -0,0 +1,33 @@ +// +build ignore +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright 2023 Authors of KubeArmor */ + +#ifndef __SYSCALLS_H +#define __SYSCALLS_H +enum +{ + // file + _FILE_OPEN = 450, + _FILE_PERMISSION = 451, + _FILE_MKNOD = 452, + _FILE_UNLINK = 453, + _FILE_MKDIR = 454, + _FILE_RMDIR= 455, + _FILE_SYMLINK = 456, + _FILE_LINK = 457, + _FILE_RENAME = 8458, + _FILE_CHMOD = 459, + _FILE_TRUNCATE = 460, + + + // network + _SOCKET_CREATE = 461, + _SOCKET_CONNECT = 462, + _SOCKET_ACCEPT = 463, + + //process + _SECURITY_BPRM_CHECK = 352, + + +}; +#endif /* __SYSCALLS_H */ \ No newline at end of file diff --git a/KubeArmor/config/config.go b/KubeArmor/config/config.go index a02b2e2820..89bd63fc41 100644 --- a/KubeArmor/config/config.go +++ b/KubeArmor/config/config.go @@ -41,11 +41,12 @@ type KubearmorConfig struct { HostDefaultNetworkPosture string // Default Enforcement Action in Global Network Context HostDefaultCapabilitiesPosture string // Default Enforcement Action in Global Capabilities Context - CoverageTest bool // Enable/Disable Coverage Test - + CoverageTest bool // Enable/Disable Coverage Test ConfigUntrackedNs []string // untracked namespaces LsmOrder []string // LSM order BPFFsPath string // path to the BPF filesystem + EnforcerAlerts bool // policy enforcer + } // GlobalCfg Global configuration for Kubearmor @@ -77,6 +78,7 @@ const ( ConfigUntrackedNs string = "untrackedNs" LsmOrder string = "lsm" BPFFsPath string = "bpfFsPath" + EnforcerAlerts string = "enforcerAlerts" ) func readCmdLineParams() { @@ -112,6 +114,7 @@ func readCmdLineParams() { lsmOrder := flag.String(LsmOrder, "bpf,apparmor,selinux", "lsm preference order to use, available lsms [bpf, apparmor, selinux]") bpfFsPath := flag.String(BPFFsPath, "/sys/fs/bpf", "Path to the BPF filesystem to use for storing maps") + enforcerAlerts := flag.Bool(EnforcerAlerts, true, "ebpf alerts") flags := []string{} flag.VisitAll(func(f *flag.Flag) { @@ -153,6 +156,8 @@ func readCmdLineParams() { viper.SetDefault(LsmOrder, *lsmOrder) viper.SetDefault(BPFFsPath, *bpfFsPath) + + viper.SetDefault(EnforcerAlerts, *enforcerAlerts) } // LoadConfig Load configuration @@ -183,7 +188,6 @@ func LoadConfig() error { GlobalCfg.GRPC = viper.GetString(ConfigGRPC) GlobalCfg.LogPath = viper.GetString(ConfigLogPath) - GlobalCfg.SELinuxProfileDir = viper.GetString(ConfigSELinuxProfileDir) GlobalCfg.CRISocket = os.Getenv("CRI_SOCKET") if GlobalCfg.CRISocket == "" { @@ -232,6 +236,7 @@ func LoadConfig() error { GlobalCfg.LsmOrder = strings.Split(viper.GetString(LsmOrder), ",") GlobalCfg.BPFFsPath = viper.GetString(BPFFsPath) + GlobalCfg.EnforcerAlerts = viper.GetBool(EnforcerAlerts) kg.Printf("Final Configuration [%+v]", GlobalCfg) diff --git a/KubeArmor/core/kubeArmor.go b/KubeArmor/core/kubeArmor.go index 5e1bc2dd43..624affe219 100644 --- a/KubeArmor/core/kubeArmor.go +++ b/KubeArmor/core/kubeArmor.go @@ -263,7 +263,7 @@ func (dm *KubeArmorDaemon) CloseSystemMonitor() bool { // InitRuntimeEnforcer Function func (dm *KubeArmorDaemon) InitRuntimeEnforcer(pinpath string) bool { - dm.RuntimeEnforcer = efc.NewRuntimeEnforcer(dm.Node, pinpath, dm.Logger) + dm.RuntimeEnforcer = efc.NewRuntimeEnforcer(dm.Node, pinpath, dm.Logger, dm.SystemMonitor) return dm.RuntimeEnforcer != nil } diff --git a/KubeArmor/enforcer/bpflsm/enforcer.go b/KubeArmor/enforcer/bpflsm/enforcer.go index 2c4870aadb..d5bd230031 100644 --- a/KubeArmor/enforcer/bpflsm/enforcer.go +++ b/KubeArmor/enforcer/bpflsm/enforcer.go @@ -5,16 +5,21 @@ package bpflsm import ( + "bytes" + "encoding/binary" "errors" + "log" "sync" "github.com/cilium/ebpf" "github.com/cilium/ebpf/link" + "github.com/cilium/ebpf/ringbuf" "github.com/cilium/ebpf/rlimit" - common "github.com/kubearmor/KubeArmor/KubeArmor/common" + "github.com/kubearmor/KubeArmor/KubeArmor/common" cfg "github.com/kubearmor/KubeArmor/KubeArmor/config" fd "github.com/kubearmor/KubeArmor/KubeArmor/feeder" + mon "github.com/kubearmor/KubeArmor/KubeArmor/monitor" tp "github.com/kubearmor/KubeArmor/KubeArmor/types" ) @@ -32,6 +37,10 @@ type BPFEnforcer struct { InnerMapSpec *ebpf.MapSpec BPFContainerMap *ebpf.Map + // events + Events *ringbuf.Reader + EventsChannel chan []byte + // ContainerID -> NsKey + rules ContainerMap map[string]ContainerKV ContainerMapLock *sync.RWMutex @@ -40,14 +49,17 @@ type BPFEnforcer struct { objPath enforcer_pathObjects Probes map[string]link.Link + + Monitor *mon.SystemMonitor } // NewBPFEnforcer instantiates a objects for setting up BPF LSM Enforcement -func NewBPFEnforcer(node tp.Node, pinpath string, logger *fd.Feeder) (*BPFEnforcer, error) { +func NewBPFEnforcer(node tp.Node, pinpath string, logger *fd.Feeder, monitor *mon.SystemMonitor) (*BPFEnforcer, error) { be := &BPFEnforcer{} be.Logger = logger + be.Monitor = monitor var err error @@ -131,7 +143,7 @@ func NewBPFEnforcer(node tp.Node, pinpath string, logger *fd.Feeder) (*BPFEnforc /* Path Hooks - Create, Link, Unlink, Symlink, Rename, MkDir, RmDir, Chowm, Chmod, Truncate + Create, Link, Unlink, Symlink, Rename, MkDir, RmDir, Chown, Chmod, Truncate These will only work if the system has `CONFIG_SECURITY_PATH=y` @@ -207,6 +219,15 @@ func NewBPFEnforcer(node tp.Node, pinpath string, logger *fd.Feeder) (*BPFEnforc } } + be.Events, err = ringbuf.NewReader(be.obj.Events) + if err != nil { + be.Logger.Errf("opening ringbuf reader: %s", err) + return be, err + } + be.EventsChannel = make(chan []byte, mon.SyscallChannelSize) + + go be.TraceEvents() + if cfg.GlobalCfg.HostPolicy { be.AddHostToMap() } @@ -214,6 +235,121 @@ func NewBPFEnforcer(node tp.Node, pinpath string, logger *fd.Feeder) (*BPFEnforc return be, nil } +type eventBPF struct { + Ts uint64 + + PidID uint32 + MntID uint32 + + HostPPID uint32 + HostPID uint32 + + PPID uint32 + PID uint32 + UID uint32 + + EventID int32 + Retval int64 + + Comm [80]byte + + Data InnerKey +} + +// TraceEvents traces events generated by bpflsm enforcer +func (be *BPFEnforcer) TraceEvents() { + + if be.Events == nil { + be.Logger.Err("ringbuf reader is nil, exiting trace events") + } + be.Logger.Print("Starting TraceEvents from BPF LSM Enforcer") + go func() { + for { + + record, err := be.Events.Read() + if err != nil { + if errors.Is(err, ringbuf.ErrClosed) { + // This should only happen when we call DestroyMonitor while terminating the process. + // Adding a Warn just in case it happens at runtime, to help debug + be.Logger.Warnf("Ring Buffer closed, exiting TraceEvents %s", err.Error()) + return + } + be.Logger.Warnf("Ringbuf error reading %s", err.Error()) + continue + } + + be.EventsChannel <- record.RawSample + + } + }() + + for { + + dataRaw := <-be.EventsChannel + + var event eventBPF + + if err := binary.Read(bytes.NewBuffer(dataRaw), binary.LittleEndian, &event); err != nil { + log.Printf("parsing ringbuf event: %s", err) + continue + } + + containerID := "" + + if event.PidID != 0 && event.MntID != 0 { + containerID = be.Monitor.LookupContainerID(event.PidID, event.MntID, event.HostPPID, event.HostPID) + } + + log := be.Monitor.BuildLogBase(event.EventID, mon.ContextCombined{ + ContainerID: containerID, + ContextSys: mon.SyscallContext{ + PID: event.PID, + PPID: event.PPID, + UID: event.UID, + + HostPID: event.HostPID, + HostPPID: event.HostPPID, + }, + }) + + switch event.EventID { + + case mon.FileOpen, mon.FilePermission, mon.FileMknod, mon.FileMkdir, mon.FileRmdir, mon.FileUnlink, mon.FileSymlink, mon.FileLink, mon.FileRename, mon.FileChmod, mon.FileTruncate: + log.Operation = "File" + log.Resource = string(bytes.Trim(event.Data.Path[:], "\x00")) + log.Enforcer = "BPFLSM" + log.Result = "Permission denied" + log.Data = "lsm=" + mon.GetSyscallName(int32(event.EventID)) + + case mon.SocketCreate, mon.SocketConnect, mon.SocketAccept: + var sockProtocol int32 + sockProtocol = int32(event.Data.Path[1]) + log.Operation = "Network" + log.Enforcer = "BPFLSM" + log.Result = "Permission denied" + if event.Data.Path[0] == 2 { + if event.Data.Path[1] == 3 { + log.Resource = fd.GetProtocolFromName("raw") + } + } else if event.Data.Path[0] == 3 { + log.Resource = fd.GetProtocolFromName(mon.GetProtocol(sockProtocol)) + } + log.Data = "lsm=" + mon.GetSyscallName(int32(event.EventID)) + " " + log.Resource + + case mon.SecurityBprmCheck: + log.Operation = "Process" + log.Source = string(bytes.Trim(event.Data.Source[:], "\x00")) + log.Resource = string(bytes.Trim(event.Data.Path[:], "\x00")) + log.Enforcer = "BPFLSM" + log.Result = "Permission denied" + log.Data = "lsm=" + mon.GetSyscallName(int32(event.EventID)) + } + + be.Logger.PushLog(log) + + } +} + // UpdateSecurityPolicies loops through containers present in the input endpoint and updates rules for each container func (be *BPFEnforcer) UpdateSecurityPolicies(endPoint tp.EndPoint) { // skip if BPFEnforcer is not active @@ -277,6 +413,11 @@ func (be *BPFEnforcer) DestroyBPFEnforcer() error { } } + if err := be.Events.Close(); err != nil { + be.Logger.Err(err.Error()) + errBPFCleanUp = true + } + if errBPFCleanUp { return errors.New("error cleaning up BPF LSM Enforcer Objects") } diff --git a/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.go b/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.go index 1f4775d751..fbf6777932 100644 --- a/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.go +++ b/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.go @@ -1,6 +1,5 @@ // Code generated by bpf2go; DO NOT EDIT. //go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64 -// +build arm64be armbe mips mips64 mips64p32 ppc64 s390 s390x sparc sparc64 package bpflsm @@ -76,6 +75,7 @@ type enforcerMapSpecs struct { Bufk *ebpf.MapSpec `ebpf:"bufk"` Bufs *ebpf.MapSpec `ebpf:"bufs"` BufsOff *ebpf.MapSpec `ebpf:"bufs_off"` + Events *ebpf.MapSpec `ebpf:"events"` KubearmorContainers *ebpf.MapSpec `ebpf:"kubearmor_containers"` } @@ -101,6 +101,7 @@ type enforcerMaps struct { Bufk *ebpf.Map `ebpf:"bufk"` Bufs *ebpf.Map `ebpf:"bufs"` BufsOff *ebpf.Map `ebpf:"bufs_off"` + Events *ebpf.Map `ebpf:"events"` KubearmorContainers *ebpf.Map `ebpf:"kubearmor_containers"` } @@ -109,6 +110,7 @@ func (m *enforcerMaps) Close() error { m.Bufk, m.Bufs, m.BufsOff, + m.Events, m.KubearmorContainers, ) } diff --git a/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.o b/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.o index e1f0aec736..a0566aba17 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 547c59f430..6da3a2e7b9 100644 --- a/KubeArmor/enforcer/bpflsm/enforcer_bpfel.go +++ b/KubeArmor/enforcer/bpflsm/enforcer_bpfel.go @@ -1,6 +1,5 @@ // Code generated by bpf2go; DO NOT EDIT. -//go:build 386 || amd64 || amd64p32 || arm || arm64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64 -// +build 386 amd64 amd64p32 arm arm64 mips64le mips64p32le mipsle ppc64le riscv64 +//go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64 package bpflsm @@ -76,6 +75,7 @@ type enforcerMapSpecs struct { Bufk *ebpf.MapSpec `ebpf:"bufk"` Bufs *ebpf.MapSpec `ebpf:"bufs"` BufsOff *ebpf.MapSpec `ebpf:"bufs_off"` + Events *ebpf.MapSpec `ebpf:"events"` KubearmorContainers *ebpf.MapSpec `ebpf:"kubearmor_containers"` } @@ -101,6 +101,7 @@ type enforcerMaps struct { Bufk *ebpf.Map `ebpf:"bufk"` Bufs *ebpf.Map `ebpf:"bufs"` BufsOff *ebpf.Map `ebpf:"bufs_off"` + Events *ebpf.Map `ebpf:"events"` KubearmorContainers *ebpf.Map `ebpf:"kubearmor_containers"` } @@ -109,6 +110,7 @@ func (m *enforcerMaps) Close() error { m.Bufk, m.Bufs, m.BufsOff, + m.Events, m.KubearmorContainers, ) } diff --git a/KubeArmor/enforcer/bpflsm/enforcer_bpfel.o b/KubeArmor/enforcer/bpflsm/enforcer_bpfel.o index fe697df0d9..688500deab 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.go b/KubeArmor/enforcer/bpflsm/enforcer_path_bpfeb.go index 5e27f1a0aa..c4fc0e24d0 100644 --- a/KubeArmor/enforcer/bpflsm/enforcer_path_bpfeb.go +++ b/KubeArmor/enforcer/bpflsm/enforcer_path_bpfeb.go @@ -1,6 +1,5 @@ // Code generated by bpf2go; DO NOT EDIT. //go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64 -// +build arm64be armbe mips mips64 mips64p32 ppc64 s390 s390x sparc sparc64 package bpflsm @@ -81,6 +80,7 @@ type enforcer_pathMapSpecs struct { Bufk *ebpf.MapSpec `ebpf:"bufk"` Bufs *ebpf.MapSpec `ebpf:"bufs"` BufsOff *ebpf.MapSpec `ebpf:"bufs_off"` + Events *ebpf.MapSpec `ebpf:"events"` KubearmorContainers *ebpf.MapSpec `ebpf:"kubearmor_containers"` } @@ -106,6 +106,7 @@ type enforcer_pathMaps struct { Bufk *ebpf.Map `ebpf:"bufk"` Bufs *ebpf.Map `ebpf:"bufs"` BufsOff *ebpf.Map `ebpf:"bufs_off"` + Events *ebpf.Map `ebpf:"events"` KubearmorContainers *ebpf.Map `ebpf:"kubearmor_containers"` } @@ -114,6 +115,7 @@ func (m *enforcer_pathMaps) Close() error { m.Bufk, m.Bufs, m.BufsOff, + m.Events, m.KubearmorContainers, ) } diff --git a/KubeArmor/enforcer/bpflsm/enforcer_path_bpfeb.o b/KubeArmor/enforcer/bpflsm/enforcer_path_bpfeb.o index 86221219d4..84d63be719 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.go b/KubeArmor/enforcer/bpflsm/enforcer_path_bpfel.go index 941b5bac20..53baabda20 100644 --- a/KubeArmor/enforcer/bpflsm/enforcer_path_bpfel.go +++ b/KubeArmor/enforcer/bpflsm/enforcer_path_bpfel.go @@ -1,6 +1,5 @@ // Code generated by bpf2go; DO NOT EDIT. -//go:build 386 || amd64 || amd64p32 || arm || arm64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64 -// +build 386 amd64 amd64p32 arm arm64 mips64le mips64p32le mipsle ppc64le riscv64 +//go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64 package bpflsm @@ -81,6 +80,7 @@ type enforcer_pathMapSpecs struct { Bufk *ebpf.MapSpec `ebpf:"bufk"` Bufs *ebpf.MapSpec `ebpf:"bufs"` BufsOff *ebpf.MapSpec `ebpf:"bufs_off"` + Events *ebpf.MapSpec `ebpf:"events"` KubearmorContainers *ebpf.MapSpec `ebpf:"kubearmor_containers"` } @@ -106,6 +106,7 @@ type enforcer_pathMaps struct { Bufk *ebpf.Map `ebpf:"bufk"` Bufs *ebpf.Map `ebpf:"bufs"` BufsOff *ebpf.Map `ebpf:"bufs_off"` + Events *ebpf.Map `ebpf:"events"` KubearmorContainers *ebpf.Map `ebpf:"kubearmor_containers"` } @@ -114,6 +115,7 @@ func (m *enforcer_pathMaps) Close() error { m.Bufk, m.Bufs, m.BufsOff, + m.Events, m.KubearmorContainers, ) } diff --git a/KubeArmor/enforcer/bpflsm/enforcer_path_bpfel.o b/KubeArmor/enforcer/bpflsm/enforcer_path_bpfel.o index 0457500759..da9f2382aa 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/runtimeEnforcer.go b/KubeArmor/enforcer/runtimeEnforcer.go index 6fdb9ddbdb..06a3b71c9b 100644 --- a/KubeArmor/enforcer/runtimeEnforcer.go +++ b/KubeArmor/enforcer/runtimeEnforcer.go @@ -16,6 +16,7 @@ import ( cfg "github.com/kubearmor/KubeArmor/KubeArmor/config" be "github.com/kubearmor/KubeArmor/KubeArmor/enforcer/bpflsm" fd "github.com/kubearmor/KubeArmor/KubeArmor/feeder" + mon "github.com/kubearmor/KubeArmor/KubeArmor/monitor" tp "github.com/kubearmor/KubeArmor/KubeArmor/types" ) @@ -38,7 +39,7 @@ type RuntimeEnforcer struct { } // selectLsm Function -func selectLsm(re *RuntimeEnforcer, lsmOrder, availablelsms, supportedlsm []string, node tp.Node, pinpath string, logger *fd.Feeder) *RuntimeEnforcer { +func selectLsm(re *RuntimeEnforcer, lsmOrder, availablelsms, supportedlsm []string, node tp.Node, pinpath string, logger *fd.Feeder, monitor *mon.SystemMonitor) *RuntimeEnforcer { var err error var lsm string @@ -100,7 +101,7 @@ apparmor: goto lsmselection bpf: - re.bpfEnforcer, err = be.NewBPFEnforcer(node, pinpath, logger) + re.bpfEnforcer, err = be.NewBPFEnforcer(node, pinpath, logger, monitor) if re.bpfEnforcer != nil { if err != nil { re.Logger.Print("Error Initialising BPF-LSM Enforcer, Cleaning Up") @@ -123,7 +124,7 @@ nil: } // NewRuntimeEnforcer Function -func NewRuntimeEnforcer(node tp.Node, pinpath string, logger *fd.Feeder) *RuntimeEnforcer { +func NewRuntimeEnforcer(node tp.Node, pinpath string, logger *fd.Feeder, monitor *mon.SystemMonitor) *RuntimeEnforcer { availablelsms := []string{"bpf", "selinux", "apparmor"} re := &RuntimeEnforcer{} re.Logger = logger @@ -165,7 +166,7 @@ probeBPFLSM: re.Logger.Printf("Supported LSMs: %s", strings.Join(lsms, ",")) - return selectLsm(re, cfg.GlobalCfg.LsmOrder, availablelsms, lsms, node, pinpath, logger) + return selectLsm(re, cfg.GlobalCfg.LsmOrder, availablelsms, lsms, node, pinpath, logger, monitor) } // RegisterContainer registers container identifiers to BPFEnforcer Map diff --git a/KubeArmor/feeder/feeder.go b/KubeArmor/feeder/feeder.go index 1842828288..534d53b41a 100644 --- a/KubeArmor/feeder/feeder.go +++ b/KubeArmor/feeder/feeder.go @@ -35,6 +35,7 @@ import ( // Running flag var Running bool +// QueueSize const QueueSize = 1000 func init() { @@ -571,7 +572,19 @@ func (fd *Feeder) PushMessage(level, message string) { // PushLog Function func (fd *Feeder) PushLog(log tp.Log) { - log = fd.UpdateMatchedPolicy(log) + + if cfg.GlobalCfg.EnforcerAlerts && fd.Enforcer == "BPFLSM" && log.Enforcer != "BPFLSM" { + log = fd.UpdateMatchedPolicy(log) + if (log.Type == "MatchedPolicy" || log.Type == "MatchedHostPolicy") && !strings.Contains(log.Action, "Audit") { + if log.Type == "MatchedPolicy" { + log.Type = "ContainerLog" + } else if log.Type == "MatchedHostPolicy" { + log.Type = "HostLog" + } + } + } else { + log = fd.UpdateMatchedPolicy(log) + } if log.Source == "" { return diff --git a/KubeArmor/feeder/policyMatcher.go b/KubeArmor/feeder/policyMatcher.go index a6bfd335c3..a6f12df03e 100644 --- a/KubeArmor/feeder/policyMatcher.go +++ b/KubeArmor/feeder/policyMatcher.go @@ -19,8 +19,8 @@ import ( // == Security Policies == // // ======================= // -// getProtocolFromName Function -func getProtocolFromName(proto string) string { +// GetProtocolFromName() Function +func GetProtocolFromName(proto string) string { switch strings.ToLower(proto) { case "tcp": return "protocol=TCP,type=SOCK_STREAM" @@ -185,7 +185,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 = GetProtocolFromName(npt.Protocol) match.ResourceType = "Protocol" if policyEnabled == tp.KubeArmorPolicyAudited && npt.Action == "Allow" { diff --git a/KubeArmor/monitor/logUpdate.go b/KubeArmor/monitor/logUpdate.go index bec548eb01..ec5ff33404 100644 --- a/KubeArmor/monitor/logUpdate.go +++ b/KubeArmor/monitor/logUpdate.go @@ -148,7 +148,7 @@ func (mon *SystemMonitor) UpdateLogs() { log.Operation = "File" log.Resource = fileName - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + " flags=" + fileOpenFlags + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) + " flags=" + fileOpenFlags case SysOpenAt: if len(msg.ContextArgs) != 3 { @@ -171,7 +171,7 @@ func (mon *SystemMonitor) UpdateLogs() { log.Operation = "File" log.Resource = fileName - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + " fd=" + fd + " flags=" + fileOpenFlags + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) + " fd=" + fd + " flags=" + fileOpenFlags case SysUnlink: if len(msg.ContextArgs) != 2 { @@ -185,7 +185,7 @@ func (mon *SystemMonitor) UpdateLogs() { log.Operation = "File" log.Resource = fileName - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) case SysUnlinkAt: if len(msg.ContextArgs) != 3 { @@ -204,7 +204,7 @@ func (mon *SystemMonitor) UpdateLogs() { log.Operation = "File" log.Resource = fileName - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + " flags=" + fileUnlinkAtFlags + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) + " flags=" + fileUnlinkAtFlags case SysRmdir: if len(msg.ContextArgs) != 1 { @@ -218,7 +218,7 @@ func (mon *SystemMonitor) UpdateLogs() { log.Operation = "File" log.Resource = fileName - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) case SysChown: if len(msg.ContextArgs) != 3 { @@ -240,7 +240,7 @@ func (mon *SystemMonitor) UpdateLogs() { log.Operation = "File" log.Resource = fileName - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + " userid=" + strconv.Itoa(uid) + " group=" + strconv.Itoa(guid) + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) + " userid=" + strconv.Itoa(uid) + " group=" + strconv.Itoa(guid) case SysFChownAt: if len(msg.ContextArgs) != 5 { @@ -269,7 +269,7 @@ func (mon *SystemMonitor) UpdateLogs() { log.Operation = "File" log.Resource = fileName - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + " userid=" + strconv.Itoa(uid) + " group=" + strconv.Itoa(guid) + " mode=" + strconv.Itoa(mode) + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) + " userid=" + strconv.Itoa(uid) + " group=" + strconv.Itoa(guid) + " mode=" + strconv.Itoa(mode) case SysSetuid, SysSetgid: if len(msg.ContextArgs) != 1 { @@ -281,7 +281,7 @@ func (mon *SystemMonitor) UpdateLogs() { uid = int(val) } log.Operation = "Syscall" - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + " userid=" + strconv.Itoa(uid) + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) + " userid=" + strconv.Itoa(uid) case SysMount: if len(msg.ContextArgs) != 5 { @@ -307,7 +307,7 @@ func (mon *SystemMonitor) UpdateLogs() { } log.Operation = "Syscall" - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + " source=" + source + " target=" + target + " filesystem=" + fstype + " mountflag=" + strconv.Itoa(flags) + " data=" + data + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) + " source=" + source + " target=" + target + " filesystem=" + fstype + " mountflag=" + strconv.Itoa(flags) + " data=" + data case SysUmount: if len(msg.ContextArgs) != 2 { @@ -324,7 +324,7 @@ func (mon *SystemMonitor) UpdateLogs() { } log.Operation = "Syscall" - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + " target=" + target + " flag=" + strconv.Itoa(flags) + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) + " target=" + target + " flag=" + strconv.Itoa(flags) case SysClose: if len(msg.ContextArgs) != 1 { @@ -339,7 +339,7 @@ func (mon *SystemMonitor) UpdateLogs() { log.Operation = "File" log.Resource = "" - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + " fd=" + fd + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) + " fd=" + fd case SysPtrace: if len(msg.ContextArgs) != 3 { @@ -364,7 +364,7 @@ func (mon *SystemMonitor) UpdateLogs() { log.Resource = binary log.Operation = "Process" - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + " request=" + request + " pid=" + pid + " process=" + binary + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) + " request=" + request + " pid=" + pid + " process=" + binary case SysSocket: // domain, type, proto if len(msg.ContextArgs) != 3 { @@ -386,8 +386,8 @@ func (mon *SystemMonitor) UpdateLogs() { } log.Operation = "Network" - log.Resource = "domain=" + sockDomain + " type=" + sockType + " protocol=" + getProtocol(sockProtocol) - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + log.Resource = "domain=" + sockDomain + " type=" + sockType + " protocol=" + GetProtocol(sockProtocol) + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) case TCPConnect, TCPConnectv6, TCPAccept, TCPAcceptv6: if len(msg.ContextArgs) != 2 { @@ -438,7 +438,7 @@ func (mon *SystemMonitor) UpdateLogs() { } } - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + " fd=" + fd + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) + " fd=" + fd case SysAccept: // fd, sockaddr if len(msg.ContextArgs) != 2 { @@ -457,7 +457,7 @@ func (mon *SystemMonitor) UpdateLogs() { log.Operation = "Network" log.Resource = "" - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + " fd=" + fd + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) + " fd=" + fd for k, v := range sockAddr { if log.Resource == "" { @@ -493,7 +493,7 @@ func (mon *SystemMonitor) UpdateLogs() { } } - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + " fd=" + fd + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) + " fd=" + fd case SysListen: // fd if len(msg.ContextArgs) != 2 { @@ -508,7 +508,7 @@ func (mon *SystemMonitor) UpdateLogs() { log.Operation = "Network" log.Resource = "" - log.Data = "syscall=" + getSyscallName(int32(msg.ContextSys.EventID)) + " fd=" + fd + log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) + " fd=" + fd default: continue diff --git a/KubeArmor/monitor/syscallParser.go b/KubeArmor/monitor/syscallParser.go index c8b76894b7..151ad92d48 100644 --- a/KubeArmor/monitor/syscallParser.go +++ b/KubeArmor/monitor/syscallParser.go @@ -522,8 +522,8 @@ var socketTypes = map[uint32]string{ 10: "SOCK_PACKET", } -// getSocketType Function -func getSocketType(st uint32) string { +// GetSocketType Function +func GetSocketType(st uint32) string { // readSocketType prints the `type` bitmask argument of the `socket` syscall // http://man7.org/linux/man-pages/man2/socket.2.html // https://elixir.bootlin.com/linux/v5.5.3/source/arch/mips/include/asm/socket.h @@ -553,7 +553,7 @@ var protocols = map[int32]string{ } // getProtocol Function -func getProtocol(proto int32) string { +func GetProtocol(proto int32) string { var res string if protoName, ok := protocols[proto]; ok { @@ -622,8 +622,8 @@ func getCapabilityName(cap int32) string { return res } -// getSyscallName Function -func getSyscallName(sc int32) string { +// GetSyscallName Function +func GetSyscallName(sc int32) string { // source: /usr/include/x86_64-linux-gnu/asm/unistd_64.h var res string @@ -844,7 +844,7 @@ func readArgFromBuff(dataBuff io.Reader) (interface{}, error) { if err != nil { return nil, fmt.Errorf("error reading syscall type: %v", err) } - res = getSyscallName(sc) + res = GetSyscallName(sc) case sockAddrT: sockaddr, err := readSockaddrFromBuff(dataBuff) if err != nil { @@ -898,7 +898,7 @@ func readArgFromBuff(dataBuff io.Reader) (interface{}, error) { if err != nil { return nil, err } - res = getSocketType(t) + res = GetSocketType(t) default: return nil, fmt.Errorf("error unknown argument type %v", at) } diff --git a/KubeArmor/monitor/syscalls_amd64.go b/KubeArmor/monitor/syscalls_amd64.go index d4ef686f84..5a9273230e 100644 --- a/KubeArmor/monitor/syscalls_amd64.go +++ b/KubeArmor/monitor/syscalls_amd64.go @@ -41,6 +41,24 @@ const ( TCPAccept = 401 TCPConnectv6 = 402 TCPAcceptv6 = 403 + + FileOpen = 450 + FilePermission = 451 + FileMknod = 452 + FileUnlink = 453 + FileMkdir = 454 + + FileRmdir = 455 + FileSymlink = 456 + + FileLink = 457 + FileRename = 458 + FileChmod = 459 + FileTruncate = 460 + + SocketCreate = 461 + SocketConnect = 462 + SocketAccept = 463 ) var syscalls = map[int32]string{ @@ -380,4 +398,18 @@ var syscalls = map[int32]string{ 351: "DO_EXIT", 352: "SECURITY_BPRM_CHECK", + 450: "FILE_OPEN", + 451: "FILE_PERMISSION", + 452: "FILE_MKNOD", + 453: "FILE_UNLINK", + 454: "FILE_MKDIR", + 455: "FILE_RMDIR", + 456: "FILE_SYMLINK", + 457: "FILE_LINK", + 458: "FILE_RENAME", + 459: "FILE_CHMOD", + 460: "FILE_TRUNCATE", + 461: "SOCKET_CREATE", + 462: "SOCKET_CONNECT", + 463: "SOCKET_ACCEPT", } diff --git a/KubeArmor/monitor/syscalls_arm64.go b/KubeArmor/monitor/syscalls_arm64.go index 36a434e937..536ced6669 100644 --- a/KubeArmor/monitor/syscalls_arm64.go +++ b/KubeArmor/monitor/syscalls_arm64.go @@ -44,6 +44,24 @@ const ( TCPAccept = 401 TCPConnectv6 = 402 TCPAcceptv6 = 403 + + FileOpen = 450 + FilePermission = 451 + FileMknod = 452 + FileUnlink = 453 + FileMkdir = 454 + + FileRmdir = 455 + FileSymlink = 456 + + FileLink = 457 + FileRename = 458 + FileChmod = 459 + FileTruncate = 460 + + SocketCreate = 461 + SocketConnect = 462 + SocketAccept = 463 ) var syscalls = map[int32]string{ @@ -340,4 +358,20 @@ var syscalls = map[int32]string{ 289: "SYS_PKEY_ALLOC", 290: "SYS_PKEY_FREE", 291: "SYS_STATX", + + 352: "SECURITY_BPRM_CHECK", + 450: "FILE_OPEN", + 451: "FILE_PERMISSION", + 452: "FILE_MKNOD", + 453: "FILE_UNLINK", + 454: "FILE_MKDIR", + 455: "FILE_RMDIR", + 456: "FILE_SYMLINK", + 457: "FILE_LINK", + 458: "FILE_RENAME", + 459: "FILE_CHMOD", + 460: "FILE_TRUNCATE", + 461: "SOCKET_CREATE", + 462: "SOCKET_CONNECT", + 463: "SOCKET_ACCEPT", } diff --git a/KubeArmor/monitor/systemMonitor.go b/KubeArmor/monitor/systemMonitor.go index 26ca398544..cd7a1b73f1 100644 --- a/KubeArmor/monitor/systemMonitor.go +++ b/KubeArmor/monitor/systemMonitor.go @@ -718,7 +718,7 @@ func (mon *SystemMonitor) TraceSyscall() { } log.Operation = "Process" - log.Data = "syscall=" + getSyscallName(int32(ctx.EventID)) + log.Data = "syscall=" + GetSyscallName(int32(ctx.EventID)) // store the log in the map mon.execLogMapLock.Lock() @@ -809,7 +809,7 @@ func (mon *SystemMonitor) TraceSyscall() { } log.Operation = "Process" - log.Data = "syscall=" + getSyscallName(int32(ctx.EventID)) + " fd=" + fd + " flag=" + procExecFlag + log.Data = "syscall=" + GetSyscallName(int32(ctx.EventID)) + " fd=" + fd + " flag=" + procExecFlag // store the log in the map mon.execLogMapLock.Lock() diff --git a/KubeArmor/utils/bpflsmprobe/probe_bpfeb.o b/KubeArmor/utils/bpflsmprobe/probe_bpfeb.o index 86bb521eaf..60ce6aaccc 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 6fb81f0c50..3c586ab079 100644 Binary files a/KubeArmor/utils/bpflsmprobe/probe_bpfel.o and b/KubeArmor/utils/bpflsmprobe/probe_bpfel.o differ diff --git a/tests/smoke/res/wordpress-mysql-deployment.yaml b/tests/smoke/res/wordpress-mysql-deployment.yaml index a241acffff..7dea05439e 100644 --- a/tests/smoke/res/wordpress-mysql-deployment.yaml +++ b/tests/smoke/res/wordpress-mysql-deployment.yaml @@ -47,6 +47,10 @@ spec: value: mysql - name: WORDPRESS_DB_PASSWORD value: root-password + securityContext: + allowPrivilegeEscalation: true + capabilities: + add: ["NET_RAW"] ports: - name: wordpress containerPort: 80 diff --git a/tests/syscalls/syscalls_test.go b/tests/syscalls/syscalls_test.go index c89f2f56b7..8b8e2cdfbe 100644 --- a/tests/syscalls/syscalls_test.go +++ b/tests/syscalls/syscalls_test.go @@ -5,6 +5,7 @@ package syscalls import ( "fmt" + "strings" "time" "github.com/kubearmor/KubeArmor/protobuf" @@ -481,6 +482,9 @@ var _ = Describe("Syscalls", func() { }) It("mount will be blocked by default for a pod", func() { + if strings.Contains(K8sRuntimeEnforcer(), "bpf") { + Skip("Skipping due to alerts being generated when something is blocked by kubearmor") + } // Start KubeArmor Logs err := KarmorLogStart("policy", "syscalls", "Syscall", ubuntu) Expect(err).To(BeNil()) @@ -507,6 +511,9 @@ var _ = Describe("Syscalls", func() { }) It("umount will be blocked by default for a pod as the capability not added", func() { + if strings.Contains(K8sRuntimeEnforcer(), "bpf") { + Skip("Skipping due to alerts being generated when something is blocked by kubearmor") + } // Start KubeArmor Logs err := KarmorLogStart("policy", "syscalls", "Syscall", ubuntu) Expect(err).To(BeNil())