diff --git a/KubeArmor/BPF/common.h b/KubeArmor/BPF/common.h new file mode 100644 index 0000000000..312200fd9b --- /dev/null +++ b/KubeArmor/BPF/common.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright 2024 Authors of KubeArmor */ +/* This module contains the common structures shared by lsm and system monitor*/ +# include "throttling.h" +#ifndef __COMMON_H +#define __COMMON_H +#define MAX_ENTRIES 10240 +#define MAX_ARGUMENT_SIZE 256 +#define MAX_STR_ARR_ELEM 20 + +// arguments matching + +// values stored for argument map +struct argVal{ + char argsArray[80]; +}; +struct cmd_args_key { + u64 tgid ; + u64 ind; +}; + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, MAX_ENTRIES); + __type(key, struct cmd_args_key); + __type(value, struct argVal); + __uint(pinning, LIBBPF_PIN_BY_NAME); +} args_store SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, 1); // Adjust max_entries based on expected usage + __type(key, u32); + __type(value, struct argVal); // Store the args in this struct +} cmd_args_buf SEC(".maps"); + +#endif /* __COMMON_H */ \ No newline at end of file diff --git a/KubeArmor/BPF/enforcer.bpf.c b/KubeArmor/BPF/enforcer.bpf.c index 8b8344b8fe..cdff41632c 100644 --- a/KubeArmor/BPF/enforcer.bpf.c +++ b/KubeArmor/BPF/enforcer.bpf.c @@ -4,6 +4,7 @@ #include "shared.h" #include "syscalls.h" +#include "common.h" SEC("lsm/bprm_check_security") int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) { @@ -11,11 +12,13 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) { event *task_info; int retval = ret; - bool match = false; - + // no of arguments + unsigned int num_of_args = BPF_CORE_READ(bprm , argc); + bool argmatch = false; + + bool match = false; struct outer_key okey; get_outer_key(&okey, t); - u32 *inner = bpf_map_lookup_elem(&kubearmor_containers, &okey); if (!inner) { @@ -38,7 +41,7 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) { bufs_k *pk = bpf_map_lookup_elem(&bufk, &two); if (pk == NULL) return 0; - + bpf_map_update_elem(&bufk, &two, z, BPF_ANY); // Extract full path from file structure provided by LSM Hook bufs_t *path_buf = get_buf(PATH_BUFFER); if (path_buf == NULL) @@ -82,7 +85,9 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) { if (src_ptr == NULL) fromSourceCheck = false; + if (fromSourceCheck) { + bpf_probe_read_str(store->source, MAX_STRING_SIZE, src_ptr); val = bpf_map_lookup_elem(inner, store); @@ -90,50 +95,49 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) { match = true; goto decision; } - -#pragma unroll - for (int i = 0; i < 64; i++) { - if (store->path[i] == '\0') - break; - - if (store->path[i] == '/') { - bpf_map_update_elem(&bufk, &two, z, BPF_ANY); - - match = false; - - bpf_probe_read_str(pk->path, i + 2, store->path); - // Check Subdir with From Source - bpf_probe_read_str(pk->source, MAX_STRING_SIZE, store->source); - dirval = bpf_map_lookup_elem(inner, pk); - if (dirval) { - if ((dirval->processmask & RULE_DIR) && - (dirval->processmask & RULE_EXEC)) { - match = true; - if ((dirval->processmask & RULE_RECURSIVE) && - (~dirval->processmask & - RULE_HINT)) { // true directory match and not a hint suggests - // there are no possibility of child dir - val = dirval; - goto decision; - } else if (dirval->processmask & - RULE_RECURSIVE) { // It's a directory match but also a - // hint, it's possible that a - // subdirectory exists that can also - // match so we continue the loop to look - // for a true match in subdirectories - recursivebuthint = true; - val = dirval; + #pragma unroll + for (int i = 0; i < 64; i++) { + if (store->path[i] == '\0') + break; + + if (store->path[i] == '/') { + bpf_map_update_elem(&bufk, &two, z, BPF_ANY); + + match = false; + + bpf_probe_read_str(pk->path, i + 2, store->path); + // Check Subdir with From Source + bpf_probe_read_str(pk->source, MAX_STRING_SIZE, store->source); + dirval = bpf_map_lookup_elem(inner, pk); + if (dirval) { + if ((dirval->processmask & RULE_DIR) && + (dirval->processmask & RULE_EXEC)) { + match = true; + if ((dirval->processmask & RULE_RECURSIVE) && + (~dirval->processmask & + RULE_HINT)) { // true directory match and not a hint suggests + // there are no possibility of child dir + val = dirval; + + goto decision; + } else if (dirval->processmask & + RULE_RECURSIVE) { // It's a directory match but also a + // hint, it's possible that a + // subdirectory exists that can also + // match so we continue the loop to look + // for a true match in subdirectories + recursivebuthint = true; + val = dirval; + } else { + continue; // We continue the loop to see if we have more nested + // directories and set match to false + } + } } else { - continue; // We continue the loop to see if we have more nested - // directories and set match to false + break; } } - } else { - break; } - } - } - if (recursivebuthint) { match = true; goto decision; @@ -164,6 +168,7 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) { val = bpf_map_lookup_elem(inner, pk); if (val && (val->processmask & RULE_EXEC)) { + match = true; goto decision; } @@ -208,7 +213,6 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) { } } } - if (recursivebuthint) { match = true; goto decision; @@ -219,11 +223,21 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) { } } -decision: - +decision: if (match) { + if (val && (val->processmask & RULE_ARGSET)){ + argmatch = matchArguments( num_of_args , &okey , store , pk); + if(argmatch){ + // if arguments matches allow the process to be executed + return 0; + } + } + if (val && (val->processmask & RULE_OWNER)) { if (!is_owner(bprm->file)) { + if((val->processmask & RULE_ARGSET) && argmatch){ + return 0; + } retval = -EPERM; } else { // Owner Only Rule Match, No need to enforce @@ -234,7 +248,6 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) { retval = -EPERM; } } - if (retval == -EPERM) { goto ringbuf; } diff --git a/KubeArmor/BPF/shared.h b/KubeArmor/BPF/shared.h index 2dbd0d4b8b..90323a064e 100644 --- a/KubeArmor/BPF/shared.h +++ b/KubeArmor/BPF/shared.h @@ -11,6 +11,7 @@ #include #include #include "throttling.h" +#include "common.h" char LICENSE[] SEC("license") = "Dual BSD/GPL"; #define EPERM 13 @@ -47,6 +48,7 @@ typedef struct bufkey { char source[MAX_STRING_SIZE]; } bufs_k; + #undef container_of #define container_of(ptr, type, member) \ ({ \ @@ -74,6 +76,33 @@ struct { __uint(max_entries, 3); } bufk SEC(".maps"); +typedef struct argskey{ + struct outer_key okey; + bufs_k store; + char arg[MAX_STRING_SIZE]; +} arg_bufs_k; + +//-- Maps and structs for argument matching--// +// argument matching + +// Key for argument map => okey+bufkey+argname + +struct { + __uint(type,BPF_MAP_TYPE_PERCPU_ARRAY); + __type(key, u32); + __type(value, arg_bufs_k); + __uint(max_entries, 1); +} args_bufk SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 100); + __type(key, arg_bufs_k); // Composite key of okey+bufkey+argname + __type(value, u8); // Value is a u8 integer + __uint(pinning, LIBBPF_PIN_BY_NAME); +} kubearmor_arguments SEC(".maps"); + +//--------------------------------------------// + typedef struct { u64 ts; @@ -109,14 +138,15 @@ struct { #define RULE_RECURSIVE 1 << 5 #define RULE_HINT 1 << 6 #define RULE_DENY 1 << 7 +#define RULE_ARGSET 1 << 8 #define MASK_WRITE 0x00000002 #define MASK_READ 0x00000004 #define MASK_APPEND 0x00000008 struct data_t { - u8 processmask; - u8 filemask; + u16 processmask; + u16 filemask; }; enum @@ -716,6 +746,56 @@ static inline int match_and_enforce_path_hooks(struct path *f_path, u32 id, bpf_ringbuf_submit(task_info, 0); return retval; } +static inline bool matchArguments( unsigned int num_of_args , struct outer_key *okey , bufs_k *store , bufs_k *pk ) { + + bool argmatch = false; + unsigned int *x ; + + unsigned int argKey; + struct argVal *argval ; + + u32 arg_k = 0; + arg_bufs_k *a_key = bpf_map_lookup_elem(&args_bufk, &arg_k); + if (a_key == NULL) + return 0; + + // clearing to avoid processing garbage values + __builtin_memset(&a_key->okey, 0, sizeof(a_key->okey)); + __builtin_memset(&a_key->store, 0, sizeof(a_key->store)); + + bpf_probe_read(&a_key->okey.mnt_ns, sizeof(okey->mnt_ns) , &okey->mnt_ns); + bpf_probe_read(&a_key->okey.pid_ns, sizeof(okey->pid_ns) , &okey->pid_ns); + bpf_probe_read_str(&a_key->store.path, sizeof(store->path) , &store->path); + + struct cmd_args_key cmd_args_buf_k; + cmd_args_buf_k.tgid = bpf_get_current_pid_tgid(); + + if (pk->path[0] == '\0') { + // pk->path[0] will be null for fromSource rules + bpf_probe_read_str(&a_key->store.source, sizeof(store->source) , store->source); + } + + for( u8 i = 0 ; i< num_of_args && i < 16; i++ ){ + cmd_args_buf_k.ind = i; + argval = bpf_map_lookup_elem(&args_store , &cmd_args_buf_k); + if(argval){ + __builtin_memset(a_key->arg, 0, sizeof(a_key->arg)); + bpf_probe_read_str(&a_key->arg, sizeof(a_key->arg), &argval->argsArray); + x = bpf_map_lookup_elem(&kubearmor_arguments ,a_key); + if (x){ + argmatch = true; + } + else { + argmatch = false; + if (i != 0) { + break; + } + } + } + } + + return argmatch; +} /* How do we check what to deny or not? diff --git a/KubeArmor/BPF/system_monitor.c b/KubeArmor/BPF/system_monitor.c index fe239f29e5..1ae15a928a 100644 --- a/KubeArmor/BPF/system_monitor.c +++ b/KubeArmor/BPF/system_monitor.c @@ -48,6 +48,7 @@ #include #include "syscalls.h" #include "throttling.h" +#include "common.h" #ifdef RHEL_RELEASE_CODE @@ -260,6 +261,8 @@ typedef struct __attribute__((__packed__)) sys_context BPF_LRU_HASH(pid_ns_map, u32, u32); + + #ifdef BTF_SUPPORTED #define GET_FIELD_ADDR(field) __builtin_preserve_access_index(&field) @@ -1142,6 +1145,37 @@ static __always_inline bool should_drop_alerts_per_container(sys_context_t *cont #endif return false; } + static __always_inline void save_cmd_args_to_buffer(const char __user *const __user *ptr){ + + + struct cmd_args_key key; + key.tgid = bpf_get_current_pid_tgid(); + u32 arg_k = 0; + struct argVal *args_buf = bpf_map_lookup_elem(&cmd_args_buf, &arg_k); + if (args_buf == NULL){ + return ; + } + // add number of args here // pragmaunroll + for ( u8 i = 0; i < 16; i++) + { + key.ind = i; + const char *const *curr_ptr = (void *)&ptr[i] ; + const char *argp = NULL; + bpf_probe_read(&argp, sizeof(argp), curr_ptr); + if (argp) + { + __builtin_memset(&args_buf->argsArray, 0, sizeof(args_buf->argsArray)); + bpf_probe_read_str(&args_buf->argsArray, sizeof(args_buf->argsArray), argp); + // bpf_printk("argp = %s argsBuf = %s" , argp , args_buf->argsArray); + bpf_map_update_elem(&args_store, &key, args_buf, BPF_ANY); + } + else { + break; + } + } + + + } SEC("kprobe/security_path_mknod") int kprobe__security_path_mknod(struct pt_regs *ctx) @@ -1274,6 +1308,10 @@ int kprobe__execve(struct pt_regs *ctx) char *filename = (char *)READ_KERN(PT_REGS_PARM1(ctx2)); unsigned long argv = READ_KERN(PT_REGS_PARM2(ctx2)); #endif + + if(get_kubearmor_config(_ENFORCER_BPFLSM)){ + save_cmd_args_to_buffer((const char *const *)argv); + } init_context(&context); diff --git a/KubeArmor/BPF/throttling.h b/KubeArmor/BPF/throttling.h index 5af306c938..fa808a26fd 100644 --- a/KubeArmor/BPF/throttling.h +++ b/KubeArmor/BPF/throttling.h @@ -4,17 +4,17 @@ #ifndef __THROTTLING_H #define __THROTTLING_H +struct outer_key { + u32 pid_ns; + u32 mnt_ns; +}; +// Throttling struct alert_throttle_state { u64 first_event_timestamp; u64 event_count; u64 throttle; }; -struct outer_key { - u32 pid_ns; - u32 mnt_ns; -}; - struct alert { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 256); diff --git a/KubeArmor/core/dockerHandler.go b/KubeArmor/core/dockerHandler.go index 35f3caab30..0d3ceba1e4 100644 --- a/KubeArmor/core/dockerHandler.go +++ b/KubeArmor/core/dockerHandler.go @@ -285,6 +285,7 @@ func (dm *KubeArmorDaemon) GetAlreadyDeployedDockerContainers() { dm.ContainersLock.Lock() if _, ok := dm.Containers[container.ContainerID]; !ok { dm.Containers[container.ContainerID] = container + fmt.Println("container id ", container.ContainerID) dm.ContainersLock.Unlock() // create/update endpoint in non-k8s mode diff --git a/KubeArmor/core/kubeArmor.go b/KubeArmor/core/kubeArmor.go index e86991de70..ab8e1bfdfe 100644 --- a/KubeArmor/core/kubeArmor.go +++ b/KubeArmor/core/kubeArmor.go @@ -157,13 +157,6 @@ func NewKubeArmorDaemon() *KubeArmorDaemon { func (dm *KubeArmorDaemon) DestroyKubeArmorDaemon() { close(StopChan) - if dm.RuntimeEnforcer != nil { - // close runtime enforcer - if dm.CloseRuntimeEnforcer() { - dm.Logger.Print("Stopped KubeArmor Enforcer") - } - } - if dm.SystemMonitor != nil { // close system monitor if dm.CloseSystemMonitor() { @@ -171,6 +164,13 @@ func (dm *KubeArmorDaemon) DestroyKubeArmorDaemon() { } } + if dm.RuntimeEnforcer != nil { + // close runtime enforcer + if dm.CloseRuntimeEnforcer() { + dm.Logger.Print("Stopped KubeArmor Enforcer") + } + } + if dm.KVMAgent != nil { // close kvm agent if dm.CloseKVMAgent() { diff --git a/KubeArmor/core/kubeUpdate.go b/KubeArmor/core/kubeUpdate.go index 18def91059..76ed91a457 100755 --- a/KubeArmor/core/kubeUpdate.go +++ b/KubeArmor/core/kubeUpdate.go @@ -1098,6 +1098,10 @@ func (dm *KubeArmorDaemon) UpdateSecurityPolicy(action string, secPolicyType str // update security policies dm.Logger.UpdateSecurityPolicies("UPDATED", endPoint) + if action == "DELETED" { + fmt.Println("policies are being delete in kubeupdate updatesecuritypoliccy") + } + if dm.RuntimeEnforcer != nil { if endPoint.PolicyEnabled == tp.KubeArmorPolicyEnabled { // enforce security policies diff --git a/KubeArmor/enforcer/bpflsm/enforcer.go b/KubeArmor/enforcer/bpflsm/enforcer.go index 7c3fbc1c40..b78e0d6004 100644 --- a/KubeArmor/enforcer/bpflsm/enforcer.go +++ b/KubeArmor/enforcer/bpflsm/enforcer.go @@ -38,6 +38,7 @@ type BPFEnforcer struct { // InnerMapSpec *ebpf.MapSpec BPFContainerMap *ebpf.Map BPFContainerThrottleMap *ebpf.Map + BPFArgumentsMap *ebpf.Map // events Events *ringbuf.Reader @@ -77,7 +78,7 @@ func NewBPFEnforcer(node tp.Node, pinpath string, logger *fd.Feeder, monitor *mo be.InnerMapSpec = &ebpf.MapSpec{ Type: ebpf.Hash, KeySize: 512, - ValueSize: 2, + ValueSize: 4, MaxEntries: 256, } @@ -96,6 +97,20 @@ func NewBPFEnforcer(node tp.Node, pinpath string, logger *fd.Feeder, monitor *mo be.Logger.Errf("error creating kubearmor_containers map: %s", err) return be, err } + be.BPFArgumentsMap, err = ebpf.NewMapWithOptions(&ebpf.MapSpec{ + Type: ebpf.Hash, + KeySize: 776, + ValueSize: 1, + MaxEntries: 100, + Name: "kubearmor_arguments", + Pinning: ebpf.PinByName, + }, ebpf.MapOptions{ + PinPath: pinpath, + }) + if err != nil { + be.Logger.Errf("error creating kubearmor_arguments_map: %s", err) + return be, err + } be.BPFContainerThrottleMap, err = ebpf.NewMapWithOptions(&ebpf.MapSpec{ Type: ebpf.Hash, @@ -445,7 +460,6 @@ func (be *BPFEnforcer) DestroyBPFEnforcer() error { } } - be.ContainerMapLock.Lock() if be.BPFContainerMap != nil { @@ -469,6 +483,16 @@ func (be *BPFEnforcer) DestroyBPFEnforcer() error { errBPFCleanUp = errors.Join(errBPFCleanUp, err) } } + if be.BPFArgumentsMap != nil { + if err := be.BPFArgumentsMap.Unpin(); err != nil { + be.Logger.Err(err.Error()) + errBPFCleanUp = errors.Join(errBPFCleanUp, err) + } + if err := be.BPFArgumentsMap.Close(); err != nil { + be.Logger.Err(err.Error()) + errBPFCleanUp = errors.Join(errBPFCleanUp, err) + } + } be.ContainerMapLock.Unlock() @@ -486,6 +510,16 @@ func (be *BPFEnforcer) DestroyBPFEnforcer() error { errBPFCleanUp = errors.Join(errBPFCleanUp, err) } } + if be.obj.enforcerMaps.ArgsStore != nil { + if err := be.obj.enforcerMaps.ArgsStore.Unpin(); err != nil { + be.Logger.Err(err.Error()) + errBPFCleanUp = errors.Join(errBPFCleanUp, err) + } + if err := be.obj.enforcerMaps.ArgsStore.Close(); err != nil { + be.Logger.Err(err.Error()) + errBPFCleanUp = errors.Join(errBPFCleanUp, err) + } + } be = nil return errBPFCleanUp diff --git a/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.go b/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.go index e9821c3bf6..2a9ed849c0 100644 --- a/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.go +++ b/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.go @@ -12,6 +12,17 @@ import ( "github.com/cilium/ebpf" ) +type enforcerArgBufsK struct { + Okey struct { + PidNs uint32 + MntNs uint32 + } + Store enforcerBufsK + Arg [256]int8 +} + +type enforcerArgVal struct{ ArgsArray [80]int8 } + type enforcerBufsK struct { Path [256]int8 Source [256]int8 @@ -19,6 +30,11 @@ type enforcerBufsK struct { type enforcerBufsT struct{ Buf [32768]int8 } +type enforcerCmdArgsKey struct { + Tgid uint64 + Ind uint64 +} + // loadEnforcer returns the embedded CollectionSpec for enforcer. func loadEnforcer() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_EnforcerBytes) @@ -73,10 +89,14 @@ type enforcerProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type enforcerMapSpecs struct { + ArgsBufk *ebpf.MapSpec `ebpf:"args_bufk"` + ArgsStore *ebpf.MapSpec `ebpf:"args_store"` Bufk *ebpf.MapSpec `ebpf:"bufk"` Bufs *ebpf.MapSpec `ebpf:"bufs"` BufsOff *ebpf.MapSpec `ebpf:"bufs_off"` + CmdArgsBuf *ebpf.MapSpec `ebpf:"cmd_args_buf"` KubearmorAlertThrottle *ebpf.MapSpec `ebpf:"kubearmor_alert_throttle"` + KubearmorArguments *ebpf.MapSpec `ebpf:"kubearmor_arguments"` KubearmorConfig *ebpf.MapSpec `ebpf:"kubearmor_config"` KubearmorContainers *ebpf.MapSpec `ebpf:"kubearmor_containers"` KubearmorEvents *ebpf.MapSpec `ebpf:"kubearmor_events"` @@ -101,10 +121,14 @@ func (o *enforcerObjects) Close() error { // // It can be passed to loadEnforcerObjects or ebpf.CollectionSpec.LoadAndAssign. type enforcerMaps struct { + ArgsBufk *ebpf.Map `ebpf:"args_bufk"` + ArgsStore *ebpf.Map `ebpf:"args_store"` Bufk *ebpf.Map `ebpf:"bufk"` Bufs *ebpf.Map `ebpf:"bufs"` BufsOff *ebpf.Map `ebpf:"bufs_off"` + CmdArgsBuf *ebpf.Map `ebpf:"cmd_args_buf"` KubearmorAlertThrottle *ebpf.Map `ebpf:"kubearmor_alert_throttle"` + KubearmorArguments *ebpf.Map `ebpf:"kubearmor_arguments"` KubearmorConfig *ebpf.Map `ebpf:"kubearmor_config"` KubearmorContainers *ebpf.Map `ebpf:"kubearmor_containers"` KubearmorEvents *ebpf.Map `ebpf:"kubearmor_events"` @@ -112,10 +136,14 @@ type enforcerMaps struct { func (m *enforcerMaps) Close() error { return _EnforcerClose( + m.ArgsBufk, + m.ArgsStore, m.Bufk, m.Bufs, m.BufsOff, + m.CmdArgsBuf, m.KubearmorAlertThrottle, + m.KubearmorArguments, m.KubearmorConfig, m.KubearmorContainers, m.KubearmorEvents, diff --git a/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.o b/KubeArmor/enforcer/bpflsm/enforcer_bpfeb.o index 7d2cead750..de5d5036af 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 b453aa6407..50168e75f9 100644 --- a/KubeArmor/enforcer/bpflsm/enforcer_bpfel.go +++ b/KubeArmor/enforcer/bpflsm/enforcer_bpfel.go @@ -12,6 +12,17 @@ import ( "github.com/cilium/ebpf" ) +type enforcerArgBufsK struct { + Okey struct { + PidNs uint32 + MntNs uint32 + } + Store enforcerBufsK + Arg [256]int8 +} + +type enforcerArgVal struct{ ArgsArray [80]int8 } + type enforcerBufsK struct { Path [256]int8 Source [256]int8 @@ -19,6 +30,11 @@ type enforcerBufsK struct { type enforcerBufsT struct{ Buf [32768]int8 } +type enforcerCmdArgsKey struct { + Tgid uint64 + Ind uint64 +} + // loadEnforcer returns the embedded CollectionSpec for enforcer. func loadEnforcer() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_EnforcerBytes) @@ -73,10 +89,14 @@ type enforcerProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type enforcerMapSpecs struct { + ArgsBufk *ebpf.MapSpec `ebpf:"args_bufk"` + ArgsStore *ebpf.MapSpec `ebpf:"args_store"` Bufk *ebpf.MapSpec `ebpf:"bufk"` Bufs *ebpf.MapSpec `ebpf:"bufs"` BufsOff *ebpf.MapSpec `ebpf:"bufs_off"` + CmdArgsBuf *ebpf.MapSpec `ebpf:"cmd_args_buf"` KubearmorAlertThrottle *ebpf.MapSpec `ebpf:"kubearmor_alert_throttle"` + KubearmorArguments *ebpf.MapSpec `ebpf:"kubearmor_arguments"` KubearmorConfig *ebpf.MapSpec `ebpf:"kubearmor_config"` KubearmorContainers *ebpf.MapSpec `ebpf:"kubearmor_containers"` KubearmorEvents *ebpf.MapSpec `ebpf:"kubearmor_events"` @@ -101,10 +121,14 @@ func (o *enforcerObjects) Close() error { // // It can be passed to loadEnforcerObjects or ebpf.CollectionSpec.LoadAndAssign. type enforcerMaps struct { + ArgsBufk *ebpf.Map `ebpf:"args_bufk"` + ArgsStore *ebpf.Map `ebpf:"args_store"` Bufk *ebpf.Map `ebpf:"bufk"` Bufs *ebpf.Map `ebpf:"bufs"` BufsOff *ebpf.Map `ebpf:"bufs_off"` + CmdArgsBuf *ebpf.Map `ebpf:"cmd_args_buf"` KubearmorAlertThrottle *ebpf.Map `ebpf:"kubearmor_alert_throttle"` + KubearmorArguments *ebpf.Map `ebpf:"kubearmor_arguments"` KubearmorConfig *ebpf.Map `ebpf:"kubearmor_config"` KubearmorContainers *ebpf.Map `ebpf:"kubearmor_containers"` KubearmorEvents *ebpf.Map `ebpf:"kubearmor_events"` @@ -112,10 +136,14 @@ type enforcerMaps struct { func (m *enforcerMaps) Close() error { return _EnforcerClose( + m.ArgsBufk, + m.ArgsStore, m.Bufk, m.Bufs, m.BufsOff, + m.CmdArgsBuf, m.KubearmorAlertThrottle, + m.KubearmorArguments, m.KubearmorConfig, m.KubearmorContainers, m.KubearmorEvents, diff --git a/KubeArmor/enforcer/bpflsm/enforcer_bpfel.o b/KubeArmor/enforcer/bpflsm/enforcer_bpfel.o index af225eb2d5..8c18772774 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 313edfa427..d5a39524fd 100644 --- a/KubeArmor/enforcer/bpflsm/enforcer_path_bpfeb.go +++ b/KubeArmor/enforcer/bpflsm/enforcer_path_bpfeb.go @@ -12,6 +12,17 @@ import ( "github.com/cilium/ebpf" ) +type enforcer_pathArgBufsK struct { + Okey struct { + PidNs uint32 + MntNs uint32 + } + Store enforcer_pathBufsK + Arg [256]int8 +} + +type enforcer_pathArgVal struct{ ArgsArray [80]int8 } + type enforcer_pathBufsK struct { Path [256]int8 Source [256]int8 @@ -19,6 +30,11 @@ type enforcer_pathBufsK struct { type enforcer_pathBufsT struct{ Buf [32768]int8 } +type enforcer_pathCmdArgsKey struct { + Tgid uint64 + Ind uint64 +} + // loadEnforcer_path returns the embedded CollectionSpec for enforcer_path. func loadEnforcer_path() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_Enforcer_pathBytes) @@ -77,10 +93,14 @@ type enforcer_pathProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type enforcer_pathMapSpecs struct { + ArgsBufk *ebpf.MapSpec `ebpf:"args_bufk"` + ArgsStore *ebpf.MapSpec `ebpf:"args_store"` Bufk *ebpf.MapSpec `ebpf:"bufk"` Bufs *ebpf.MapSpec `ebpf:"bufs"` BufsOff *ebpf.MapSpec `ebpf:"bufs_off"` + CmdArgsBuf *ebpf.MapSpec `ebpf:"cmd_args_buf"` KubearmorAlertThrottle *ebpf.MapSpec `ebpf:"kubearmor_alert_throttle"` + KubearmorArguments *ebpf.MapSpec `ebpf:"kubearmor_arguments"` KubearmorConfig *ebpf.MapSpec `ebpf:"kubearmor_config"` KubearmorContainers *ebpf.MapSpec `ebpf:"kubearmor_containers"` KubearmorEvents *ebpf.MapSpec `ebpf:"kubearmor_events"` @@ -105,10 +125,14 @@ func (o *enforcer_pathObjects) Close() error { // // It can be passed to loadEnforcer_pathObjects or ebpf.CollectionSpec.LoadAndAssign. type enforcer_pathMaps struct { + ArgsBufk *ebpf.Map `ebpf:"args_bufk"` + ArgsStore *ebpf.Map `ebpf:"args_store"` Bufk *ebpf.Map `ebpf:"bufk"` Bufs *ebpf.Map `ebpf:"bufs"` BufsOff *ebpf.Map `ebpf:"bufs_off"` + CmdArgsBuf *ebpf.Map `ebpf:"cmd_args_buf"` KubearmorAlertThrottle *ebpf.Map `ebpf:"kubearmor_alert_throttle"` + KubearmorArguments *ebpf.Map `ebpf:"kubearmor_arguments"` KubearmorConfig *ebpf.Map `ebpf:"kubearmor_config"` KubearmorContainers *ebpf.Map `ebpf:"kubearmor_containers"` KubearmorEvents *ebpf.Map `ebpf:"kubearmor_events"` @@ -116,10 +140,14 @@ type enforcer_pathMaps struct { func (m *enforcer_pathMaps) Close() error { return _Enforcer_pathClose( + m.ArgsBufk, + m.ArgsStore, m.Bufk, m.Bufs, m.BufsOff, + m.CmdArgsBuf, m.KubearmorAlertThrottle, + m.KubearmorArguments, m.KubearmorConfig, m.KubearmorContainers, m.KubearmorEvents, diff --git a/KubeArmor/enforcer/bpflsm/enforcer_path_bpfeb.o b/KubeArmor/enforcer/bpflsm/enforcer_path_bpfeb.o index 80ff9b63b1..215d4007f1 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 a1e9861c0c..fe3f14d361 100644 --- a/KubeArmor/enforcer/bpflsm/enforcer_path_bpfel.go +++ b/KubeArmor/enforcer/bpflsm/enforcer_path_bpfel.go @@ -12,6 +12,17 @@ import ( "github.com/cilium/ebpf" ) +type enforcer_pathArgBufsK struct { + Okey struct { + PidNs uint32 + MntNs uint32 + } + Store enforcer_pathBufsK + Arg [256]int8 +} + +type enforcer_pathArgVal struct{ ArgsArray [80]int8 } + type enforcer_pathBufsK struct { Path [256]int8 Source [256]int8 @@ -19,6 +30,11 @@ type enforcer_pathBufsK struct { type enforcer_pathBufsT struct{ Buf [32768]int8 } +type enforcer_pathCmdArgsKey struct { + Tgid uint64 + Ind uint64 +} + // loadEnforcer_path returns the embedded CollectionSpec for enforcer_path. func loadEnforcer_path() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_Enforcer_pathBytes) @@ -77,10 +93,14 @@ type enforcer_pathProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type enforcer_pathMapSpecs struct { + ArgsBufk *ebpf.MapSpec `ebpf:"args_bufk"` + ArgsStore *ebpf.MapSpec `ebpf:"args_store"` Bufk *ebpf.MapSpec `ebpf:"bufk"` Bufs *ebpf.MapSpec `ebpf:"bufs"` BufsOff *ebpf.MapSpec `ebpf:"bufs_off"` + CmdArgsBuf *ebpf.MapSpec `ebpf:"cmd_args_buf"` KubearmorAlertThrottle *ebpf.MapSpec `ebpf:"kubearmor_alert_throttle"` + KubearmorArguments *ebpf.MapSpec `ebpf:"kubearmor_arguments"` KubearmorConfig *ebpf.MapSpec `ebpf:"kubearmor_config"` KubearmorContainers *ebpf.MapSpec `ebpf:"kubearmor_containers"` KubearmorEvents *ebpf.MapSpec `ebpf:"kubearmor_events"` @@ -105,10 +125,14 @@ func (o *enforcer_pathObjects) Close() error { // // It can be passed to loadEnforcer_pathObjects or ebpf.CollectionSpec.LoadAndAssign. type enforcer_pathMaps struct { + ArgsBufk *ebpf.Map `ebpf:"args_bufk"` + ArgsStore *ebpf.Map `ebpf:"args_store"` Bufk *ebpf.Map `ebpf:"bufk"` Bufs *ebpf.Map `ebpf:"bufs"` BufsOff *ebpf.Map `ebpf:"bufs_off"` + CmdArgsBuf *ebpf.Map `ebpf:"cmd_args_buf"` KubearmorAlertThrottle *ebpf.Map `ebpf:"kubearmor_alert_throttle"` + KubearmorArguments *ebpf.Map `ebpf:"kubearmor_arguments"` KubearmorConfig *ebpf.Map `ebpf:"kubearmor_config"` KubearmorContainers *ebpf.Map `ebpf:"kubearmor_containers"` KubearmorEvents *ebpf.Map `ebpf:"kubearmor_events"` @@ -116,10 +140,14 @@ type enforcer_pathMaps struct { func (m *enforcer_pathMaps) Close() error { return _Enforcer_pathClose( + m.ArgsBufk, + m.ArgsStore, m.Bufk, m.Bufs, m.BufsOff, + m.CmdArgsBuf, m.KubearmorAlertThrottle, + m.KubearmorArguments, m.KubearmorConfig, m.KubearmorContainers, m.KubearmorEvents, diff --git a/KubeArmor/enforcer/bpflsm/enforcer_path_bpfel.o b/KubeArmor/enforcer/bpflsm/enforcer_path_bpfel.o index 0045eeffd0..2ecc3bbba9 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/mapHelpers.go b/KubeArmor/enforcer/bpflsm/mapHelpers.go index bdb7734f6f..466eb3133e 100644 --- a/KubeArmor/enforcer/bpflsm/mapHelpers.go +++ b/KubeArmor/enforcer/bpflsm/mapHelpers.go @@ -15,6 +15,10 @@ type ContainerKV struct { Key NsKey Map *ebpf.Map Rules RuleList + + // -----------------// + Arg_Key ArgumentsKey + Arg_Map *ebpf.Map } // NsKey Structure acts as an Identifier for containers @@ -28,6 +32,16 @@ type InnerKey struct { Path [256]byte Source [256]byte } +type ArgumentsKey struct { + NsKey + InnerKey + Argument [256]byte +} + +type ArgListKey struct { + NsKey + InnerKey +} // AddContainerIDToMap adds container metadata to Outer eBPF container Map for initialising enforcement tracking and initiates an InnerMap to store the container specific rules func (be *BPFEnforcer) AddContainerIDToMap(containerID string, pidns, mntns uint32) { @@ -83,6 +97,23 @@ func (be *BPFEnforcer) DeleteContainerInnerMap(containerID string) { be.Logger.Errf("error deleting container %s from outer map in kubearmor_alert_throttle map: %s", containerID, err.Error()) } } + + for key, val := range be.ContainerMap[containerID].Rules.ArgumentsList { + + for _, arg := range val { + var bpfArgKey ArgumentsKey + bpfArgKey.InnerKey = key.InnerKey + bpfArgKey.NsKey = key.NsKey + copy(bpfArgKey.Argument[:], []byte(arg)) + if err := be.BPFArgumentsMap.Delete(bpfArgKey); err != nil { + if !errors.Is(err, os.ErrNotExist) { + be.Logger.Errf("error deleting argument from container %s in kubearmor arguments map: %s", containerID, err.Error()) + } + + } + + } + } if err := be.ContainerMap[containerID].Map.Close(); err != nil { be.Logger.Errf("error closing container map for %s: %s", containerID, err) } @@ -90,6 +121,7 @@ func (be *BPFEnforcer) DeleteContainerInnerMap(containerID string) { val.Map = nil val.Rules.Init() be.ContainerMap[containerID] = val + } } diff --git a/KubeArmor/enforcer/bpflsm/rulesHandling.go b/KubeArmor/enforcer/bpflsm/rulesHandling.go index dfe92924c2..c45601e515 100644 --- a/KubeArmor/enforcer/bpflsm/rulesHandling.go +++ b/KubeArmor/enforcer/bpflsm/rulesHandling.go @@ -15,14 +15,15 @@ import ( // Bit Flags for Map Rule Mask const ( - EXEC uint8 = 1 << 0 - WRITE uint8 = 1 << 1 - READ uint8 = 1 << 2 - OWNER uint8 = 1 << 3 - DIR uint8 = 1 << 4 - RECURSIVE uint8 = 1 << 5 - HINT uint8 = 1 << 6 - DENY uint8 = 1 << 7 + EXEC uint16 = 1 << 0 + WRITE uint16 = 1 << 1 + READ uint16 = 1 << 2 + OWNER uint16 = 1 << 3 + DIR uint16 = 1 << 4 + RECURSIVE uint16 = 1 << 5 + HINT uint16 = 1 << 6 + DENY uint16 = 1 << 7 + ARGSET uint16 = 1 << 8 ) // Data Index for rules @@ -48,7 +49,7 @@ var ( ) // Protocol Identifiers for Network Rules -var protocols = map[string]uint8{ +var protocols = map[string]uint16{ "ICMP": 1, "TCP": 6, "UDP": 17, @@ -56,15 +57,15 @@ var protocols = map[string]uint8{ } // Socket Type Identifiers for Network Rules -var netType = map[string]uint8{ +var netType = map[string]uint16{ "RAW": 3, } // Array Keys for Network Rule Keys const ( - FAMILY uint8 = 1 - TYPE uint8 = 2 - PROTOCOL uint8 = 3 + FAMILY uint16 = 1 + TYPE uint16 = 2 + PROTOCOL uint16 = 3 ) // Key for mapping capabilities in bpf maps @@ -72,29 +73,32 @@ const capableKey = 200 // RuleList Structure contains all the data required to set rules for a particular container type RuleList struct { - ProcessRuleList map[InnerKey][2]uint8 - FileRuleList map[InnerKey][2]uint8 - NetworkRuleList map[InnerKey][2]uint8 - CapabilitiesRuleList map[InnerKey][2]uint8 + ProcessRuleList map[InnerKey][2]uint16 + FileRuleList map[InnerKey][2]uint16 + NetworkRuleList map[InnerKey][2]uint16 + CapabilitiesRuleList map[InnerKey][2]uint16 ProcWhiteListPosture bool FileWhiteListPosture bool NetWhiteListPosture bool CapWhiteListPosture bool + ArgumentsList map[ArgListKey][]string } // Init prepares the RuleList object func (r *RuleList) Init() { - r.ProcessRuleList = make(map[InnerKey][2]uint8) + r.ProcessRuleList = make(map[InnerKey][2]uint16) r.ProcWhiteListPosture = false - r.FileRuleList = make(map[InnerKey][2]uint8) + r.FileRuleList = make(map[InnerKey][2]uint16) r.FileWhiteListPosture = false - r.NetworkRuleList = make(map[InnerKey][2]uint8) + r.NetworkRuleList = make(map[InnerKey][2]uint16) r.NetWhiteListPosture = false - r.CapabilitiesRuleList = make(map[InnerKey][2]uint8) + r.CapabilitiesRuleList = make(map[InnerKey][2]uint16) r.CapWhiteListPosture = false + + r.ArgumentsList = make(map[ArgListKey][]string) } // UpdateContainerRules updates individual container map with new rules and resolves conflicting rules @@ -108,13 +112,17 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec for _, secPolicy := range securityPolicies { for _, path := range secPolicy.Spec.Process.MatchPaths { - var val [2]uint8 + var argKey ArgListKey + var key InnerKey + var val [2]uint16 val[PROCESS] = val[PROCESS] | EXEC if path.OwnerOnly { val[PROCESS] = val[PROCESS] | OWNER } + if len(path.AllowedArgs) > 0 { + val[PROCESS] = val[PROCESS] | ARGSET + } if len(path.FromSource) == 0 { - var key InnerKey if len(path.ExecName) > 0 { copy(key.Path[:], []byte(path.ExecName)) } else { @@ -129,7 +137,6 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec } } else { for _, src := range path.FromSource { - var key InnerKey if len(path.ExecName) > 0 { copy(key.Path[:], []byte(path.ExecName)) } else { @@ -145,10 +152,18 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec } } } + if len(path.AllowedArgs) > 0 { + var argList []string + argKey.InnerKey = key + argKey.MntNS = be.ContainerMap[id].Key.MntNS + argKey.PidNS = be.ContainerMap[id].Key.PidNS + argList = append(argList, path.AllowedArgs...) + newrules.ArgumentsList[argKey] = argList + } } for _, dir := range secPolicy.Spec.Process.MatchDirectories { - var val [2]uint8 + var val [2]uint16 val[PROCESS] = val[PROCESS] | EXEC if dir.OwnerOnly { val[PROCESS] = val[PROCESS] | OWNER @@ -180,7 +195,7 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec } for _, path := range secPolicy.Spec.File.MatchPaths { - var val [2]uint8 + var val [2]uint16 val[FILE] = val[FILE] | READ if path.OwnerOnly { val[FILE] = val[FILE] | OWNER @@ -217,7 +232,7 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec } for _, dir := range secPolicy.Spec.File.MatchDirectories { - var val [2]uint8 + var val [2]uint16 val[FILE] = val[FILE] | READ if dir.OwnerOnly { val[FILE] = val[FILE] | OWNER @@ -252,14 +267,14 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec } for _, net := range secPolicy.Spec.Network.MatchProtocols { - var val [2]uint8 + var val [2]uint16 var key = InnerKey{Path: [256]byte{}, Source: [256]byte{}} if val, ok := protocols[strings.ToUpper(net.Protocol)]; ok { - key.Path[0] = PROTOCOL - key.Path[1] = val + key.Path[0] = byte(PROTOCOL) + key.Path[1] = byte(val) } else if val, ok := netType[strings.ToUpper(net.Protocol)]; ok { - key.Path[0] = TYPE - key.Path[1] = val + key.Path[0] = byte(TYPE) // doubt here ( should we increase byte array size?? ) + key.Path[1] = byte(val) } if len(net.FromSource) == 0 { @@ -289,7 +304,7 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec } } for _, capab := range secPolicy.Spec.Capabilities.MatchCapabilities { - var val [2]uint8 + var val [2]uint16 var key = InnerKey{Path: [256]byte{}, Source: [256]byte{}} key.Path[0] = capableKey @@ -341,6 +356,7 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec return } + // adding deletion logic for arguments here too 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) @@ -352,8 +368,10 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec return } + // resolving rulesset for arguments + // Check for differences in Fresh Rules Set and Existing Ruleset - be.resolveConflicts(newrules.ProcWhiteListPosture, be.ContainerMap[id].Rules.ProcWhiteListPosture, newrules.ProcessRuleList, be.ContainerMap[id].Rules.ProcessRuleList, be.ContainerMap[id].Map) + be.resolveConflictsProcessRules(newrules.ProcessRuleList, be.ContainerMap[id].Rules.ProcessRuleList, be.ContainerMap[id].Map, id, be.ContainerMap[id].Rules.ArgumentsList) 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) @@ -370,11 +388,11 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec if newrules.ProcWhiteListPosture { if defaultPosture.FileAction == "block" { - if err := be.ContainerMap[id].Map.Put(PROCWHITELIST, [2]uint8{BlockPosture}); err != nil { + if err := be.ContainerMap[id].Map.Put(PROCWHITELIST, [2]uint16{BlockPosture}); err != nil { be.Logger.Errf("error adding proc whitelist key rule to map for container %s: %s", id, err) } } else { - if err := be.ContainerMap[id].Map.Put(PROCWHITELIST, [2]uint8{AuditPosture}); err != nil { + if err := be.ContainerMap[id].Map.Put(PROCWHITELIST, [2]uint16{AuditPosture}); err != nil { be.Logger.Errf("error adding proc whitelist key rule to map for container %s: %s", id, err) } } @@ -392,14 +410,23 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec be.Logger.Errf("error adding rule to map for container %s: %s", id, err) } } - + for key, val := range newrules.ArgumentsList { + be.ContainerMap[id].Rules.ArgumentsList[key] = val + for _, arg := range val { + var argKey ArgumentsKey + argKey.InnerKey = key.InnerKey + argKey.NsKey = key.NsKey + copy(argKey.Argument[:], []byte(arg)) + be.BPFArgumentsMap.Put(argKey, uint8(1)) + } + } if newrules.FileWhiteListPosture { if defaultPosture.FileAction == "block" { - if err := be.ContainerMap[id].Map.Put(FILEWHITELIST, [2]uint8{BlockPosture}); err != nil { + if err := be.ContainerMap[id].Map.Put(FILEWHITELIST, [2]uint16{BlockPosture}); err != nil { be.Logger.Errf("error adding file whitelist key rule to map for container %s: %s", id, err) } } else { - if err := be.ContainerMap[id].Map.Put(FILEWHITELIST, [2]uint8{AuditPosture}); err != nil { + if err := be.ContainerMap[id].Map.Put(FILEWHITELIST, [2]uint16{AuditPosture}); err != nil { be.Logger.Errf("error adding file whitelist key rule to map for container %s: %s", id, err) } } @@ -419,11 +446,11 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec if newrules.NetWhiteListPosture { if defaultPosture.NetworkAction == "block" { - if err := be.ContainerMap[id].Map.Put(NETWHITELIST, [2]uint8{BlockPosture}); err != nil { + if err := be.ContainerMap[id].Map.Put(NETWHITELIST, [2]uint16{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(NETWHITELIST, [2]uint8{AuditPosture}); err != nil { + if err := be.ContainerMap[id].Map.Put(NETWHITELIST, [2]uint16{AuditPosture}); err != nil { be.Logger.Errf("error adding network key rule to map for container %s: %s", id, err) } } @@ -442,11 +469,11 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec } if newrules.CapWhiteListPosture { if defaultPosture.CapabilitiesAction == "block" { - if err := be.ContainerMap[id].Map.Put(CAPWHITELIST, [2]uint8{BlockPosture}); err != nil { + if err := be.ContainerMap[id].Map.Put(CAPWHITELIST, [2]uint16{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 { + if err := be.ContainerMap[id].Map.Put(CAPWHITELIST, [2]uint16{AuditPosture}); err != nil { be.Logger.Errf("error adding network key rule to map for container %s: %s", id, err) } } @@ -465,7 +492,7 @@ func (be *BPFEnforcer) UpdateContainerRules(id string, securityPolicies []tp.Sec } } -func fuseProcAndFileRules(procList, fileList map[InnerKey][2]uint8) { +func fuseProcAndFileRules(procList, fileList map[InnerKey][2]uint16) { for k, v := range fileList { if val, ok := procList[k]; ok { v[PROCESS] = val[PROCESS] @@ -474,7 +501,7 @@ func fuseProcAndFileRules(procList, fileList map[InnerKey][2]uint8) { } } -func (be *BPFEnforcer) resolveConflicts(newPosture, oldPosture bool, newRuleList, oldRuleList map[InnerKey][2]uint8, cmap *ebpf.Map) { +func (be *BPFEnforcer) resolveConflicts(newPosture, oldPosture bool, newRuleList, oldRuleList map[InnerKey][2]uint16, cmap *ebpf.Map) { // We delete existing elements which are not in the fresh rule set for key := range oldRuleList { if _, ok := newRuleList[key]; !ok { @@ -488,9 +515,42 @@ func (be *BPFEnforcer) resolveConflicts(newPosture, oldPosture bool, newRuleList } } } +func (be *BPFEnforcer) resolveConflictsProcessRules(newRuleList, oldRuleList map[InnerKey][2]uint16, cmap *ebpf.Map, id string, argListMap map[ArgListKey][]string) { + + // We delete existing elements which are not in the fresh rule set + for key := range oldRuleList { + if _, ok := newRuleList[key]; !ok { + // Delete Element from Container Map + if err := cmap.Delete(key); err != nil { + if !errors.Is(err, os.ErrNotExist) { + be.Logger.Err(err.Error()) + } + } + // Deleting Arguments if exists + delete(oldRuleList, key) + // deleting the arguments map valus + if len(argListMap) > 0 { + var argKey ArgListKey + argKey.InnerKey = key + argKey.MntNS = be.ContainerMap[id].Key.MntNS + argKey.PidNS = be.ContainerMap[id].Key.PidNS + for _, arg := range argListMap[argKey] { + var bpfArgKey ArgumentsKey + bpfArgKey.InnerKey = argKey.InnerKey + bpfArgKey.NsKey = argKey.NsKey + copy(bpfArgKey.Argument[:], []byte(arg)) + be.BPFArgumentsMap.Delete(bpfArgKey) + } + delete(argListMap, argKey) + } + + } + } + +} // dirtoMap extracts parent directories from the Path Key and adds it as hints in the Container Rule Map -func dirtoMap(idx int, p, src string, m map[InnerKey][2]uint8, val [2]uint8) { +func dirtoMap(idx int, p, src string, m map[InnerKey][2]uint16, val [2]uint16) { var key InnerKey if src != "" { copy(key.Source[:], []byte(src)) diff --git a/KubeArmor/go.mod b/KubeArmor/go.mod index ddbe17a5e9..240fec5d18 100644 --- a/KubeArmor/go.mod +++ b/KubeArmor/go.mod @@ -65,7 +65,6 @@ require ( github.com/emicklei/go-restful/v3 v3.11.2 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect diff --git a/KubeArmor/types/types.go b/KubeArmor/types/types.go index 1b6046f18d..f32db37ea3 100644 --- a/KubeArmor/types/types.go +++ b/KubeArmor/types/types.go @@ -347,10 +347,11 @@ type MatchSourceType struct { // ProcessPathType Structure type ProcessPathType struct { - Path string `json:"path,omitempty"` - ExecName string `json:"execname,omitempty"` - OwnerOnly bool `json:"ownerOnly,omitempty"` - FromSource []MatchSourceType `json:"fromSource,omitempty"` + Path string `json:"path,omitempty"` + ExecName string `json:"execname,omitempty"` + OwnerOnly bool `json:"ownerOnly,omitempty"` + FromSource []MatchSourceType `json:"fromSource,omitempty"` + AllowedArgs []string `json:"allowedArgs,omitempty"` Severity int `json:"severity,omitempty"` Tags []string `json:"tags,omitempty"` diff --git a/KubeArmor/utils/bpflsmprobe/probe_bpfeb.o b/KubeArmor/utils/bpflsmprobe/probe_bpfeb.o index 1e17482196..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 254d96e3c8..4446f3ada9 100644 Binary files a/KubeArmor/utils/bpflsmprobe/probe_bpfel.o and b/KubeArmor/utils/bpflsmprobe/probe_bpfel.o differ diff --git a/README.md b/README.md index 2dbf693261..6817f4231d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![](.gitbook/assets/logo.png) -[![Build Status](https://github.com/kubearmor/KubeArmor/actions/workflows/ci-go.yml/badge.svg)](https://github.com/kubearmor/KubeArmor/actions/workflows/ci-go.yml/) +[![Build Status](https://github.com/kubearmor/KubeArmor/actions/workflows/ci-test-go.yml/badge.svg)](https://github.com/kubearmor/KubeArmor/actions/workflows/ci-test-go.yml/) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/5401/badge)](https://bestpractices.coreinfrastructure.org/projects/5401) [![CLOMonitor](https://img.shields.io/endpoint?url=https://clomonitor.io/api/projects/cncf/kubearmor/badge)](https://clomonitor.io/projects/cncf/kubearmor) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/kubearmor/kubearmor/badge)](https://securityscorecards.dev/viewer/?uri=github.com/kubearmor/KubeArmor) diff --git a/deployments/CRD/KubeArmorClusterPolicy.yaml b/deployments/CRD/KubeArmorClusterPolicy.yaml index 75bfcced26..cc567cdabe 100644 --- a/deployments/CRD/KubeArmorClusterPolicy.yaml +++ b/deployments/CRD/KubeArmorClusterPolicy.yaml @@ -366,6 +366,10 @@ spec: path: pattern: ^\/+.*[^\/]$ type: string + allowedArgs: + items: + type: string + type: array severity: maximum: 10 minimum: 1 diff --git a/deployments/CRD/KubeArmorHostPolicy.yaml b/deployments/CRD/KubeArmorHostPolicy.yaml index 497c216ff7..b3787dc5ed 100644 --- a/deployments/CRD/KubeArmorHostPolicy.yaml +++ b/deployments/CRD/KubeArmorHostPolicy.yaml @@ -375,6 +375,10 @@ spec: path: pattern: ^\/+.*[^\/]$ type: string + allowedArgs: + items: + type: string + type: array severity: maximum: 10 minimum: 1 diff --git a/deployments/CRD/KubeArmorPolicy.yaml b/deployments/CRD/KubeArmorPolicy.yaml index ce3ef593fa..88067f177b 100644 --- a/deployments/CRD/KubeArmorPolicy.yaml +++ b/deployments/CRD/KubeArmorPolicy.yaml @@ -365,6 +365,10 @@ spec: path: pattern: ^\/+.*[^\/]$ type: string + allowedArgs: + items: + type: string + type: array severity: maximum: 10 minimum: 1 diff --git a/pkg/KubeArmorController/api/security.kubearmor.com/v1/common.go b/pkg/KubeArmorController/api/security.kubearmor.com/v1/common.go index 4d9611c612..ed7ef9c4cb 100644 --- a/pkg/KubeArmorController/api/security.kubearmor.com/v1/common.go +++ b/pkg/KubeArmorController/api/security.kubearmor.com/v1/common.go @@ -31,6 +31,9 @@ type ProcessPathType struct { // +kubebuilder:validation:Optional Path MatchPathType `json:"path,omitempty"` + // +kubebuilder:validation:Optional + AllowedArgs []string `json:"allowedArgs,omitempty"` + // +kubebuilder:validation:Optional ExecName MatchBinType `json:"execname,omitempty"` diff --git a/tests/k8s_env/ksp/ksp_test.go b/tests/k8s_env/ksp/ksp_test.go index 37d48b5218..c146df597b 100644 --- a/tests/k8s_env/ksp/ksp_test.go +++ b/tests/k8s_env/ksp/ksp_test.go @@ -665,6 +665,33 @@ var _ = Describe("Ksp", func() { Expect(err).To(BeNil()) Expect(res.Found).To(BeTrue()) }) + It("it can allow certain arguments in process execution", func() { + if strings.Contains(K8sRuntimeEnforcer(), "apparmor") { + Skip("Skipping due to args rule only supported by BPFLSM") + } + err := K8sApplyFile("multiubuntu/ksp-ubuntu-1-allow-proc-args.yaml") + Expect(err).To(BeNil()) + + // Start KubeArmor Logs + err = KarmorLogStart("policy", "multiubuntu", "Process", ub1) + Expect(err).To(BeNil()) + + AssertCommand(ub1, "multiubuntu", []string{"bash", "-c", "'python -m pydoc'"}, + MatchRegexp("*Permission denied"), true, + ) + + expect := protobuf.Alert{ + PolicyName: "ksp-ubuntu-1-allow-proc-args", + Action: "Block", + Result: "Permission denied", + } + + res, err := KarmorGetTargetAlert(5*time.Second, &expect) + Expect(err).To(BeNil()) + Expect(res.Found).To(BeTrue()) + + //ksp-group-1-allow-proc-args + }) }) diff --git a/tests/k8s_env/ksp/multiubuntu/ksp-ubuntu-1-allow-proc-args.yaml b/tests/k8s_env/ksp/multiubuntu/ksp-ubuntu-1-allow-proc-args.yaml new file mode 100644 index 0000000000..4779f0f88d --- /dev/null +++ b/tests/k8s_env/ksp/multiubuntu/ksp-ubuntu-1-allow-proc-args.yaml @@ -0,0 +1,30 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorPolicy +metadata: + name: ksp-ubuntu-1-allow-proc-args + namespace: multiubuntu +spec: + severity: 5 + message: "block all arguments except allowedArgs" + selector: + matchLabels: + container: ubuntu-1 + process: + matchPaths: + - path: /usr/bin/python3.6 + allowedArgs: + - -m + - random + action: + Block + +# multiubuntu_test_12 + +# test +# python -m random +# allowed +# python -m pydoc +# permission denied +# python +# permission denied +