From 562bf4ec16a422eab3e0ad78bac99e579fef78b2 Mon Sep 17 00:00:00 2001 From: Prateek Nandle Date: Tue, 6 Feb 2024 16:57:02 +0530 Subject: [PATCH] Syscall Visibility Signed-off-by: Prateek Nandle --- KubeArmor/BPF/system_monitor.c | 70 ++++++++++-- KubeArmor/config/config.go | 15 ++- KubeArmor/core/kubeUpdate.go | 12 +++ KubeArmor/feeder/policyMatcher.go | 4 + KubeArmor/main.go | 2 +- KubeArmor/monitor/processTree.go | 2 + KubeArmor/monitor/systemMonitor.go | 100 +++++++++++++++++- KubeArmor/types/types.go | 2 + deployments/get/objects.go | 1 + deployments/helm/KubeArmor/README.md | 6 +- .../helm/KubeArmor/templates/configmap.yaml | 1 + deployments/helm/KubeArmor/values.yaml | 1 + deployments/helm/KubeArmorOperator/README.md | 5 +- ...erator.kubearmor.com_kubearmorconfigs.yaml | 2 + .../helm/KubeArmorOperator/values.yaml | 1 + deployments/operator/operator.yaml | 2 + getting-started/kubearmor_visibility.md | 6 +- .../v1/kubearmorconfig_types.go | 2 + pkg/KubeArmorOperator/common/defaults.go | 2 + ...erator.kubearmor.com_kubearmorconfigs.yaml | 2 + .../config/samples/kubearmor-test.yaml | 2 + .../config/samples/kubearmor-ubi-test.yaml | 3 +- .../config/samples/sample-config.yml | 1 + .../internal/controller/cluster.go | 6 ++ 24 files changed, 228 insertions(+), 22 deletions(-) diff --git a/KubeArmor/BPF/system_monitor.c b/KubeArmor/BPF/system_monitor.c index 5fd125b751..d1b17d32b2 100644 --- a/KubeArmor/BPF/system_monitor.c +++ b/KubeArmor/BPF/system_monitor.c @@ -307,6 +307,7 @@ enum _PROCESS_PROBE = 1, _NETWORK_PROBE = 2, _CAPS_PROBE = 3, + _SYSCALL_PROBE = 4, _TRACE_SYSCALL = 0, _IGNORE_SYSCALL = 1, @@ -355,6 +356,30 @@ struct kaconfig struct kaconfig kubearmor_config SEC(".maps"); +// == Syscall Visibility == // + +struct syscallvis +{ + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, u32); + __type(value, u32); + __uint(max_entries, 322); + __uint(pinning, LIBBPF_PIN_BY_NAME); +}; + +struct syscallvis kubearmor_syscall_vis SEC(".maps"); + +static __always_inline u32 get_syscalls_visibility(u32 syscallID) +{ + u32 *value = bpf_map_lookup_elem(&kubearmor_syscall_vis, &syscallID); + if (!value) + { + return 0; + } + + return *value; +} + // == Kernel Helpers == // static __always_inline u32 get_pid_ns_id(struct nsproxy *ns) @@ -1611,7 +1636,10 @@ int kprobe__unlink(struct pt_regs *ctx) SEC("kretprobe/__x64_sys_unlink") int kretprobe__unlink(struct pt_regs *ctx) { - return trace_ret_generic(_SYS_UNLINK, ctx, ARG_TYPE0(INT_T) | ARG_TYPE1(FILE_TYPE_T), _FILE_PROBE); + if (!get_syscalls_visibility(_SYS_UNLINK)){ + return 0; + } + return trace_ret_generic(_SYS_UNLINK, ctx, ARG_TYPE0(INT_T) | ARG_TYPE1(FILE_TYPE_T), _SYSCALL_PROBE); } SEC("kprobe/__x64_sys_unlinkat") @@ -1626,7 +1654,10 @@ int kprobe__unlinkat(struct pt_regs *ctx) SEC("kretprobe/__x64_sys_unlinkat") int kretprobe__unlinkat(struct pt_regs *ctx) { - return trace_ret_generic(_SYS_UNLINKAT, ctx, ARG_TYPE0(INT_T) | ARG_TYPE1(FILE_TYPE_T) | ARG_TYPE2(UNLINKAT_FLAG_T), _FILE_PROBE); + if (!get_syscalls_visibility(_SYS_UNLINKAT)){ + return 0; + } + return trace_ret_generic(_SYS_UNLINKAT, ctx, ARG_TYPE0(INT_T) | ARG_TYPE1(FILE_TYPE_T) | ARG_TYPE2(UNLINKAT_FLAG_T), _SYSCALL_PROBE); } SEC("kprobe/__x64_sys_rmdir") @@ -1670,7 +1701,10 @@ int kprobe__chown(struct pt_regs *ctx) SEC("kretprobe/__x64_sys_chown") int kretprobe__chown(struct pt_regs *ctx) { - return trace_ret_generic(_SYS_CHOWN, ctx, ARG_TYPE0(FILE_TYPE_T) | ARG_TYPE1(INT_T) | ARG_TYPE2(INT_T), _FILE_PROBE); + if (!get_syscalls_visibility(_SYS_CHOWN)){ + return 0; + } + return trace_ret_generic(_SYS_CHOWN, ctx, ARG_TYPE0(FILE_TYPE_T) | ARG_TYPE1(INT_T) | ARG_TYPE2(INT_T), _SYSCALL_PROBE); } SEC("kprobe/__x64_sys_fchownat") @@ -1684,7 +1718,10 @@ int kprobe__fchownat(struct pt_regs *ctx) SEC("kretprobe/__x64_sys_fchownat") int kretprobe__fchownat(struct pt_regs *ctx) { - return trace_ret_generic(_SYS_FCHOWNAT, ctx, ARG_TYPE0(INT_T) | ARG_TYPE1(FILE_TYPE_T) | ARG_TYPE2(INT_T) | ARG_TYPE3(INT_T) | ARG_TYPE4(INT_T), _FILE_PROBE); + if (!get_syscalls_visibility(_SYS_FCHOWNAT)){ + return 0; + } + return trace_ret_generic(_SYS_FCHOWNAT, ctx, ARG_TYPE0(INT_T) | ARG_TYPE1(FILE_TYPE_T) | ARG_TYPE2(INT_T) | ARG_TYPE3(INT_T) | ARG_TYPE4(INT_T), _SYSCALL_PROBE); } SEC("kprobe/__x64_sys_setuid") @@ -1698,7 +1735,10 @@ int kprobe__setuid(struct pt_regs *ctx) SEC("kretprobe/__x64_sys_setuid") int kretprobe__setuid(struct pt_regs *ctx) { - return trace_ret_generic(_SYS_SETUID, ctx, ARG_TYPE0(INT_T), _CAPS_PROBE); + if (!get_syscalls_visibility(_SYS_SETUID)){ + return 0; + } + return trace_ret_generic(_SYS_SETUID, ctx, ARG_TYPE0(INT_T), _SYSCALL_PROBE); } SEC("kprobe/__x64_sys_setgid") @@ -1712,7 +1752,10 @@ int kprobe__setgid(struct pt_regs *ctx) SEC("kretprobe/__x64_sys_setgid") int kretprobe__setgid(struct pt_regs *ctx) { - return trace_ret_generic(_SYS_SETGID, ctx, ARG_TYPE0(INT_T), _CAPS_PROBE); + if (!get_syscalls_visibility(_SYS_SETGID)){ + return 0; + } + return trace_ret_generic(_SYS_SETGID, ctx, ARG_TYPE0(INT_T), _SYSCALL_PROBE); } SEC("kprobe/__x64_sys_ptrace") @@ -1726,7 +1769,10 @@ int kprobe__ptrace(struct pt_regs *ctx) SEC("kretprobe/__x64_sys_ptrace") int kretprobe__ptrace(struct pt_regs *ctx) { - return trace_ret_generic(_SYS_PTRACE, ctx, ARG_TYPE0(PTRACE_REQ_T) | ARG_TYPE1(INT_T) | ARG_TYPE2(FILE_TYPE_T), _CAPS_PROBE); + if (!get_syscalls_visibility(_SYS_PTRACE)){ + return 0; + } + return trace_ret_generic(_SYS_PTRACE, ctx, ARG_TYPE0(PTRACE_REQ_T) | ARG_TYPE1(INT_T) | ARG_TYPE2(FILE_TYPE_T), _SYSCALL_PROBE); } SEC("kprobe/__x64_sys_mount") @@ -1740,7 +1786,10 @@ int kprobe__mount(struct pt_regs *ctx) SEC("kretprobe/__x64_sys_mount") int kretprobe__mount(struct pt_regs *ctx) { - return trace_ret_generic(_SYS_MOUNT, ctx, ARG_TYPE0(STR_T) | ARG_TYPE1(STR_T) | ARG_TYPE2(STR_T) | ARG_TYPE3(MOUNT_FLAG_T) | ARG_TYPE4(STR_T), _CAPS_PROBE); + if (!get_syscalls_visibility(_SYS_MOUNT)){ + return 0; + } + return trace_ret_generic(_SYS_MOUNT, ctx, ARG_TYPE0(STR_T) | ARG_TYPE1(STR_T) | ARG_TYPE2(STR_T) | ARG_TYPE3(MOUNT_FLAG_T) | ARG_TYPE4(STR_T), _SYSCALL_PROBE); } SEC("kprobe/__x64_sys_umount") @@ -1754,7 +1803,10 @@ int kprobe__umount(struct pt_regs *ctx) SEC("kretprobe/__x64_sys_umount") int kretprobe__umount(struct pt_regs *ctx) { - return trace_ret_generic(_SYS_UMOUNT, ctx, ARG_TYPE0(STR_T) | ARG_TYPE1(UMOUNT_FLAG_T), _CAPS_PROBE); + if (!get_syscalls_visibility(_SYS_MOUNT)){ + return 0; + } + return trace_ret_generic(_SYS_UMOUNT, ctx, ARG_TYPE0(STR_T) | ARG_TYPE1(UMOUNT_FLAG_T), _SYSCALL_PROBE); } struct tracepoint_syscalls_sys_exit_t diff --git a/KubeArmor/config/config.go b/KubeArmor/config/config.go index b172b1ef7a..a3138ffeba 100644 --- a/KubeArmor/config/config.go +++ b/KubeArmor/config/config.go @@ -55,6 +55,8 @@ type KubearmorConfig struct { InitTimeout string // Timeout for main thread init stages StateAgent bool // enable KubeArmor state agent + + SyscallsVisibility string // Enable/Disable Syscalls Visibility } // GlobalCfg Global configuration for Kubearmor @@ -96,6 +98,7 @@ const ( ConfigDefaultPostureLogs string = "defaultPostureLogs" ConfigInitTimeout string = "initTimeout" ConfigStateAgent string = "enableKubeArmorStateAgent" + ConfigSyscallsVisibility string = "syscallsVisibility" ) func readCmdLineParams() { @@ -111,8 +114,8 @@ func readCmdLineParams() { seLinuxProfileDirStr := flag.String(ConfigSELinuxProfileDir, "/tmp/kubearmor.selinux", "SELinux profile directory") criSocket := flag.String(ConfigCRISocket, "", "path to CRI socket (format: unix:///path/to/file.sock)") - visStr := flag.String(ConfigVisibility, "process,file,network,capabilities", "Container Visibility to use [process,file,network,capabilities,none]") - hostVisStr := flag.String(ConfigHostVisibility, "default", "Host Visibility to use [process,file,network,capabilities,none] (default \"none\" for k8s, \"process,file,network,capabilities\" for VM)") + visStr := flag.String(ConfigVisibility, "process,file,network,capabilities,syscall", "Container Visibility to use [process,file,network,capabilities,syscall,none]") + hostVisStr := flag.String(ConfigHostVisibility, "default", "Host Visibility to use [process,file,network,capabilities,syscall,none] (default \"none\" for k8s, \"process,file,network,capabilities,syscall\" for VM)") policyB := flag.Bool(ConfigKubearmorPolicy, true, "enabling KubeArmorPolicy") hostPolicyB := flag.Bool(ConfigKubearmorHostPolicy, false, "enabling KubeArmorHostPolicy") @@ -144,6 +147,8 @@ func readCmdLineParams() { stateAgent := flag.Bool(ConfigStateAgent, false, "enabling KubeArmor State Agent client") + syscallsVisibility := flag.String(ConfigSyscallsVisibility, "chown,fchownat,mount,unmount,unlink,unlinkat,setuid,setgid,ptrace", "Syscalls Visibility") + flags := []string{} flag.VisitAll(func(f *flag.Flag) { kv := fmt.Sprintf("%s:%v", f.Name, f.Value) @@ -197,6 +202,8 @@ func readCmdLineParams() { viper.SetDefault(ConfigInitTimeout, *initTimeout) viper.SetDefault(ConfigStateAgent, *stateAgent) + + viper.SetDefault(ConfigSyscallsVisibility, *syscallsVisibility) } // LoadConfig Load configuration @@ -270,7 +277,7 @@ func LoadConfig() error { if GlobalCfg.HostVisibility == "default" { if GlobalCfg.KVMAgent || (!GlobalCfg.K8sEnv && GlobalCfg.HostPolicy) { - GlobalCfg.HostVisibility = "process,file,network,capabilities" + GlobalCfg.HostVisibility = "process,file,network,capabilities,syscall" } else { // k8s GlobalCfg.HostVisibility = "none" } @@ -292,6 +299,8 @@ func LoadConfig() error { GlobalCfg.StateAgent = viper.GetBool(ConfigStateAgent) + GlobalCfg.SyscallsVisibility = viper.GetString(ConfigSyscallsVisibility) + kg.Printf("Final Configuration [%+v]", GlobalCfg) return nil diff --git a/KubeArmor/core/kubeUpdate.go b/KubeArmor/core/kubeUpdate.go index 6e57db283f..033b20b089 100644 --- a/KubeArmor/core/kubeUpdate.go +++ b/KubeArmor/core/kubeUpdate.go @@ -79,6 +79,8 @@ func (dm *KubeArmorDaemon) HandleNodeAnnotations(node *tp.Node) { node.NetworkVisibilityEnabled = true } else if visibility == "capabilities" { node.CapabilitiesVisibilityEnabled = true + } else if visibility == "syscall" { + node.SyscallVisibilityEnabled = true } } } @@ -2177,6 +2179,7 @@ func (dm *KubeArmorDaemon) UpdateVisibility(action string, namespace string, vis val.File = visibility.File val.Network = visibility.Network val.Process = visibility.Process + val.Syscall = visibility.Syscall dm.SystemMonitor.NamespacePidsMap[namespace] = val for _, nskey := range val.NsKeys { dm.SystemMonitor.UpdateNsKeyMap("MODIFIED", nskey, visibility) @@ -2188,6 +2191,7 @@ func (dm *KubeArmorDaemon) UpdateVisibility(action string, namespace string, vis Process: visibility.Process, Capability: visibility.Capabilities, Network: visibility.Network, + Syscall: visibility.Syscall, } } dm.Logger.Printf("Namespace %s visibiliy configured %+v", namespace, visibility) @@ -2273,6 +2277,7 @@ func (dm *KubeArmorDaemon) WatchDefaultPosture() cache.InformerSynced { Process: dm.validateVisibility("process", cfg.GlobalCfg.Visibility), Network: dm.validateVisibility("network", cfg.GlobalCfg.Visibility), Capabilities: dm.validateVisibility("capabilities", cfg.GlobalCfg.Visibility), + Syscall: dm.validateVisibility("syscall", cfg.GlobalCfg.Visibility), } // Set Visibility to Namespace Annotation if exists @@ -2282,6 +2287,7 @@ func (dm *KubeArmorDaemon) WatchDefaultPosture() cache.InformerSynced { Process: dm.validateVisibility("process", ns.Annotations[visibilityKey]), Network: dm.validateVisibility("network", ns.Annotations[visibilityKey]), Capabilities: dm.validateVisibility("capabilities", ns.Annotations[visibilityKey]), + Syscall: dm.validateVisibility("syscall", ns.Annotations[visibilityKey]), } } dm.UpdateDefaultPosture("ADDED", ns.Name, defaultPosture, annotated) @@ -2305,6 +2311,7 @@ func (dm *KubeArmorDaemon) WatchDefaultPosture() cache.InformerSynced { Process: dm.validateVisibility("process", cfg.GlobalCfg.Visibility), Network: dm.validateVisibility("network", cfg.GlobalCfg.Visibility), Capabilities: dm.validateVisibility("capabilities", cfg.GlobalCfg.Visibility), + Syscall: dm.validateVisibility("syscall", cfg.GlobalCfg.Visibility), } // Set Visibility to Namespace Annotation if exists @@ -2314,6 +2321,7 @@ func (dm *KubeArmorDaemon) WatchDefaultPosture() cache.InformerSynced { Process: dm.validateVisibility("process", ns.Annotations[visibilityKey]), Network: dm.validateVisibility("network", ns.Annotations[visibilityKey]), Capabilities: dm.validateVisibility("capabilities", ns.Annotations[visibilityKey]), + Syscall: dm.validateVisibility("syscall", ns.Annotations[visibilityKey]), } } dm.UpdateDefaultPosture("MODIFIED", ns.Name, defaultPosture, annotated) @@ -2356,6 +2364,8 @@ func (dm *KubeArmorDaemon) WatchConfigMap() cache.InformerSynced { if cm, ok := obj.(*corev1.ConfigMap); ok && cm.Namespace == cmNS { cfg.GlobalCfg.HostVisibility = cm.Data[cfg.ConfigHostVisibility] cfg.GlobalCfg.Visibility = cm.Data[cfg.ConfigVisibility] + cfg.GlobalCfg.SyscallsVisibility = cm.Data[cfg.ConfigSyscallsVisibility] + dm.SystemMonitor.HandleSyscallsVsibility() if _, ok := cm.Data[cfg.ConfigDefaultPostureLogs]; ok { cfg.GlobalCfg.DefaultPostureLogs = (cm.Data[cfg.ConfigDefaultPostureLogs] == "true") } @@ -2382,6 +2392,8 @@ func (dm *KubeArmorDaemon) WatchConfigMap() cache.InformerSynced { if cm, ok := new.(*corev1.ConfigMap); ok && cm.Namespace == cmNS { cfg.GlobalCfg.HostVisibility = cm.Data[cfg.ConfigHostVisibility] cfg.GlobalCfg.Visibility = cm.Data[cfg.ConfigVisibility] + cfg.GlobalCfg.SyscallsVisibility = cm.Data[cfg.ConfigSyscallsVisibility] + dm.SystemMonitor.HandleSyscallsVsibility() if _, ok := cm.Data[cfg.ConfigDefaultPostureLogs]; ok { cfg.GlobalCfg.DefaultPostureLogs = (cm.Data[cfg.ConfigDefaultPostureLogs] == "true") } diff --git a/KubeArmor/feeder/policyMatcher.go b/KubeArmor/feeder/policyMatcher.go index c241c3943e..ed25b1e776 100644 --- a/KubeArmor/feeder/policyMatcher.go +++ b/KubeArmor/feeder/policyMatcher.go @@ -1748,6 +1748,10 @@ func (fd *Feeder) UpdateMatchedPolicy(log tp.Log) tp.Log { if setLogFields(&log, existCapabilitiesAllowPolicy, "allow", fd.Node.CapabilitiesVisibilityEnabled, false) { return log } + } else if log.Operation == "Syscall" { + if setLogFields(&log, false, "allow", fd.Node.SyscallVisibilityEnabled, false) { + return log + } } } else if log.Type == "MatchedPolicy" { log.Type = "MatchedHostPolicy" diff --git a/KubeArmor/main.go b/KubeArmor/main.go index 66c356ddb7..f311d9fd01 100644 --- a/KubeArmor/main.go +++ b/KubeArmor/main.go @@ -44,7 +44,7 @@ func main() { // initial clean up bpfMapsDir := "/sys/fs/bpf/" - bpfMapsName := []string{"kubearmor_config", "kubearmor_events", "kubearmor_containers", "kubearmor_visibility"} + bpfMapsName := []string{"kubearmor_config", "kubearmor_events", "kubearmor_containers", "kubearmor_visibility", "kubearmor_syscall_vis"} for _, mp := range bpfMapsName { path := bpfMapsDir + mp /* This should not be triggered in ideal cases, diff --git a/KubeArmor/monitor/processTree.go b/KubeArmor/monitor/processTree.go index 4565340327..7e25e83c9a 100644 --- a/KubeArmor/monitor/processTree.go +++ b/KubeArmor/monitor/processTree.go @@ -60,6 +60,7 @@ func (mon *SystemMonitor) AddContainerIDToNsMap(containerID string, namespace st Process: val.Process, Capabilities: val.Capability, Network: val.Network, + Syscall: val.Syscall, }) } } else { @@ -74,6 +75,7 @@ func (mon *SystemMonitor) AddContainerIDToNsMap(containerID string, namespace st Process: strings.Contains(cfg.GlobalCfg.Visibility, "process"), Network: strings.Contains(cfg.GlobalCfg.Visibility, "network"), Capabilities: strings.Contains(cfg.GlobalCfg.Visibility, "capabilities"), + Syscall: strings.Contains(cfg.GlobalCfg.Visibility, "syscall"), } mon.UpdateNsKeyMap("ADDED", key, visibility) } diff --git a/KubeArmor/monitor/systemMonitor.go b/KubeArmor/monitor/systemMonitor.go index 95565c2c70..c3d908a7e5 100644 --- a/KubeArmor/monitor/systemMonitor.go +++ b/KubeArmor/monitor/systemMonitor.go @@ -38,6 +38,7 @@ const ( // how many event the channel can hold SyscallChannelSize = 1 << 13 //8192 DefaultVisibilityKey = uint32(0xc0ffee) + SyscallVisibility = "chown,fchownat,mount,unmount,unlink,unlinkat,setuid,setgid,ptrace" ) // ======================= // @@ -57,6 +58,7 @@ type NsVisibility struct { Process bool Capability bool Network bool + Syscall bool } // ===================== // @@ -132,6 +134,7 @@ type SystemMonitor struct { BpfConfigMap *cle.Map BpfNsVisibilityMap *cle.Map BpfVisibilityMapSpec cle.MapSpec + BpfSyscallVis *cle.Map NsVisibilityMap map[NsKey]*cle.Map NamespacePidsMap map[string]NsVisibility @@ -197,7 +200,7 @@ func NewSystemMonitor(node *tp.Node, nodeLock **sync.RWMutex, logger *fd.Feeder, Type: cle.Hash, KeySize: 4, ValueSize: 4, - MaxEntries: 4, + MaxEntries: 5, } // assign the value of untracked ns from GlobalCfg @@ -251,7 +254,24 @@ func (mon *SystemMonitor) initBPFMaps() error { } } - return errors.Join(errviz, errconfig) + bpfSyscallVis, errsyscallvis := cle.NewMapWithOptions( + &cle.MapSpec{ + Name: "kubearmor_syscall_vis", + Type: cle.Array, + KeySize: 4, + ValueSize: 4, + MaxEntries: 322, + Pinning: cle.PinByName, + InnerMap: &mon.BpfVisibilityMapSpec, + }, cle.MapOptions{ + PinPath: mon.PinPath, + }) + mon.BpfSyscallVis = bpfSyscallVis + if cfg.GlobalCfg.SyscallsVisibility != "" { + mon.HandleSyscallsVsibility() + } + + return errors.Join(errviz, errconfig, errsyscallvis) } // DestroyBPFMaps Function @@ -277,6 +297,17 @@ func (mon *SystemMonitor) DestroyBPFMaps() { mon.Logger.Warnf("error closing bpf map kubearmor_config %v", err) } } + + if mon.BpfSyscallVis != nil { + err := mon.BpfSyscallVis.Unpin() + if err != nil { + mon.Logger.Warnf("error unpinning bpf map kubearmor_syscall_vis %v", err) + } + err = mon.BpfSyscallVis.Close() + if err != nil { + mon.Logger.Warnf("error closing bpf map kubearmor_syscall_vis %v", err) + } + } } // UpdateNsKeyMap Function @@ -299,6 +330,10 @@ func (mon *SystemMonitor) UpdateNsKeyMap(action string, nsKey NsKey, visibility Key: uint32(3), Value: visibilityOff, } + syscall := cle.MapKV{ + Key: uint32(4), + Value: visibilityOff, + } if visibility.File { file.Value = visibilityOn } @@ -311,6 +346,9 @@ func (mon *SystemMonitor) UpdateNsKeyMap(action string, nsKey NsKey, visibility if visibility.Network { network.Value = visibilityOn } + if visibility.Syscall { + syscall.Value = visibilityOn + } if action == "ADDED" { spec := mon.BpfVisibilityMapSpec @@ -318,6 +356,7 @@ func (mon *SystemMonitor) UpdateNsKeyMap(action string, nsKey NsKey, visibility spec.Contents = append(spec.Contents, process) spec.Contents = append(spec.Contents, network) spec.Contents = append(spec.Contents, capability) + spec.Contents = append(spec.Contents, syscall) visibilityMap, err := cle.NewMap(&spec) if err != nil { mon.Logger.Warnf("Cannot create bpf map %s", err) @@ -352,6 +391,10 @@ func (mon *SystemMonitor) UpdateNsKeyMap(action string, nsKey NsKey, visibility if err != nil { mon.Logger.Warnf("Cannot update visibility map. nskey=%+v, value=%+v, scope=capability", nsKey, capability.Value) } + err = visibilityMap.Put(syscall.Key, syscall.Value) + if err != nil { + mon.Logger.Warnf("Cannot update visibility map. nskey=%+v, value=%+v, scope=syscall", nsKey, syscall.Value) + } // Need to lock NsMap to print the following log message mon.NsMapLock.RLock() @@ -390,6 +433,9 @@ func (mon *SystemMonitor) UpdateVisibility() { if strings.Contains(visibilityParams, "capabilities") { hostVisibility.Capabilities = true } + if strings.Contains(visibilityParams, "syscall") { + hostVisibility.Capabilities = true + } } nsKey := NsKey{ @@ -412,6 +458,9 @@ func (mon *SystemMonitor) UpdateVisibility() { if strings.Contains(visibilityParams, "capabilities") { visibility.Capabilities = true } + if strings.Contains(visibilityParams, "syscall") { + visibility.Capabilities = true + } } mon.BpfMapLock.Lock() @@ -420,6 +469,53 @@ func (mon *SystemMonitor) UpdateVisibility() { mon.UpdateNsKeyMap("ADDED", nsKey, visibility) } +func getSyscallID(syscallName string) int { + // Map syscall name to syscall ID + switch syscallName { + case "chown": + return 92 + case "fchownat": + return 260 + case "mount": + return 165 + case "unmount": + return 166 + case "unlink": + return 87 + case "unlinkat": + return 263 + case "setuid": + return 105 + case "setgid": + return 106 + case "ptrace": + return 101 + + default: + return -1 + } +} + +// HandleSyscallsVsibility enable/disable syscalls visibility +func (mon *SystemMonitor) HandleSyscallsVsibility() { + syscallNames := strings.Split(SyscallVisibility, ",") + + for _, name := range syscallNames { + syscallID := getSyscallID(name) + if syscallID != -1 { + if strings.Contains(cfg.GlobalCfg.SyscallsVisibility, name) { + if err := mon.BpfSyscallVis.Update(uint32(syscallID), uint32(1), cle.UpdateAny); err != nil { + fmt.Printf("Error updating eBPF map for syscall %s: %v\n", name, err) + } + } else { + if err := mon.BpfSyscallVis.Update(uint32(syscallID), uint32(0), cle.UpdateAny); err != nil { + fmt.Printf("Error updating eBPF map for syscall %s: %v\n", name, err) + } + } + } + } +} + // InitBPF Function func (mon *SystemMonitor) InitBPF() error { homeDir, err := filepath.Abs(filepath.Dir(os.Args[0])) diff --git a/KubeArmor/types/types.go b/KubeArmor/types/types.go index 20b9e2cf5f..e2a176a1ab 100644 --- a/KubeArmor/types/types.go +++ b/KubeArmor/types/types.go @@ -135,6 +135,7 @@ type Node struct { FileVisibilityEnabled bool `json:"fileVisibilityEnabled"` NetworkVisibilityEnabled bool `json:"networkVisibilityEnabled"` CapabilitiesVisibilityEnabled bool `json:"capabilitiesVisibilityEnabled"` + SyscallVisibilityEnabled bool `json:"syscallVisibilityEnabled"` } // ================ // @@ -584,6 +585,7 @@ type Visibility struct { Process bool `json:"process,omitempty"` Network bool `json:"network,omitempty"` Capabilities bool `json:"capabilties,omitempty"` + Syscall bool `json:"syscall,omitempty"` } // ================== // diff --git a/deployments/get/objects.go b/deployments/get/objects.go index 6bf47d059e..7def7db904 100644 --- a/deployments/get/objects.go +++ b/deployments/get/objects.go @@ -987,6 +987,7 @@ func GetKubearmorConfigMap(namespace, name string) *corev1.ConfigMap { data[cfg.ConfigDefaultCapabilitiesPosture] = "audit" data[cfg.ConfigDefaultNetworkPosture] = "audit" data[cfg.ConfigDefaultPostureLogs] = "true" + data[cfg.ConfigSyscallsVisibility] = "chown,fchownat,mount,unmount,unlink,unlinkat,setuid,setgid,ptrace" return &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{ diff --git a/deployments/helm/KubeArmor/README.md b/deployments/helm/KubeArmor/README.md index c9a842b4b3..47c6384752 100644 --- a/deployments/helm/KubeArmor/README.md +++ b/deployments/helm/KubeArmor/README.md @@ -75,7 +75,7 @@ Usage of ./kubearmor: -hostDefaultNetworkPosture string configuring default enforcement action in global network context {allow|audit|block} (default "audit") -hostVisibility string - Host Visibility to use [process,file,network,capabilities,none] (default "none" for k8s, "process,file,network,capabilities" for VM) (default "default") + Host Visibility to use [process,file,network,capabilities,syscall,none] (default "none" for k8s, "process,file,network,capabilities,syscall" for VM) (default "default") -k8s is k8s env? (default true) -kubeconfig string @@ -86,8 +86,10 @@ Usage of ./kubearmor: lsm preference order to use, available lsms [bpf, apparmor, selinux] (default "bpf,apparmor,selinux") -seLinuxProfileDir string SELinux profile directory (default "/tmp/kubearmor.selinux") + -syscallsVisibility string + Syscalls Visibility (default "chown,fchownat,mount,unmount,unlink,unlinkat,setuid,setgid,ptrace") -visibility string - Container Visibility to use, available visibility [process,file,network,capabilities,none] (default "process,network") + Container Visibility to use, available visibility [process,file,network,capabilities,syscall,none] (default "process,network") ``` ## Verify if all the resources are up and running diff --git a/deployments/helm/KubeArmor/templates/configmap.yaml b/deployments/helm/KubeArmor/templates/configmap.yaml index 2bac64bd7c..86be29987f 100644 --- a/deployments/helm/KubeArmor/templates/configmap.yaml +++ b/deployments/helm/KubeArmor/templates/configmap.yaml @@ -4,6 +4,7 @@ data: defaultCapabilitiesPosture: {{ .Values.kubearmorConfigMap.defaultCapabilitiesPosture }} defaultNetworkPosture: {{ .Values.kubearmorConfigMap.defaultNetworkPosture }} visibility: {{ .Values.kubearmorConfigMap.visibility }} + syscallsVisibility: {{ .Values.kubearmorConfigMap.syscallsVisibility }} kind: ConfigMap metadata: labels: diff --git a/deployments/helm/KubeArmor/values.yaml b/deployments/helm/KubeArmor/values.yaml index 3e824e2a1c..652a26d854 100644 --- a/deployments/helm/KubeArmor/values.yaml +++ b/deployments/helm/KubeArmor/values.yaml @@ -58,6 +58,7 @@ kubearmorConfigMap: defaultCapabilitiesPosture: audit defaultNetworkPosture: audit visibility: process,network + syscallsVisibility: chown,fchownat,mount,unmount,unlink,unlinkat,setuid,setgid,ptrace #volume mounts and volumes kubearmor: diff --git a/deployments/helm/KubeArmorOperator/README.md b/deployments/helm/KubeArmorOperator/README.md index b16c2b4c8c..f899a99d2a 100644 --- a/deployments/helm/KubeArmorOperator/README.md +++ b/deployments/helm/KubeArmorOperator/README.md @@ -54,7 +54,10 @@ spec: enableStdOutMsgs: [show stdout messages for relay server] # DEFAULT - false # default visibility configuration - defaultVisibility: [comma separated: process|file|network] # DEFAULT - process,network + defaultVisibility: [comma separated: process|file|network|syscall] # DEFAULT - process,network + + # per syscall vsisbility + syscallsVisibility: [comma separated: add syscall name for visibility] # DEFAULT - chown,fchownat,mount,unmount,unlink,unlinkat,setuid,setgid,ptrace # KubeArmor image and pull policy kubearmorImage: diff --git a/deployments/helm/KubeArmorOperator/crds/operator.kubearmor.com_kubearmorconfigs.yaml b/deployments/helm/KubeArmorOperator/crds/operator.kubearmor.com_kubearmorconfigs.yaml index 29eb3b6e88..6c204f6271 100644 --- a/deployments/helm/KubeArmorOperator/crds/operator.kubearmor.com_kubearmorconfigs.yaml +++ b/deployments/helm/KubeArmorOperator/crds/operator.kubearmor.com_kubearmorconfigs.yaml @@ -130,6 +130,8 @@ spec: type: object seccompEnabled: type: boolean + syscallsVisibility: + type: string type: object status: description: KubeArmorConfigStatus defines the observed state of KubeArmorConfig diff --git a/deployments/helm/KubeArmorOperator/values.yaml b/deployments/helm/KubeArmorOperator/values.yaml index 7b10aec07b..ede527922d 100644 --- a/deployments/helm/KubeArmorOperator/values.yaml +++ b/deployments/helm/KubeArmorOperator/values.yaml @@ -16,3 +16,4 @@ kubearmorConfig: enableStdOutAlerts: false enableStdOutMsgs: false seccompEnabled: true + syscallsVisibility: chown,fchownat,mount,unmount,unlink,unlinkat,setuid,setgid,ptrace diff --git a/deployments/operator/operator.yaml b/deployments/operator/operator.yaml index fab51b363e..07daf3c41a 100644 --- a/deployments/operator/operator.yaml +++ b/deployments/operator/operator.yaml @@ -128,6 +128,8 @@ spec: type: object seccompEnabled: type: boolean + syscallsVisibility: + type: string type: object status: description: KubeArmorConfigStatus defines the observed state of KubeArmorConfig diff --git a/getting-started/kubearmor_visibility.md b/getting-started/kubearmor_visibility.md index 8a47aa5fdf..4bb6dd98b4 100644 --- a/getting-started/kubearmor_visibility.md +++ b/getting-started/kubearmor_visibility.md @@ -134,7 +134,7 @@ This sample policy blocks execution of the `apt` and `apt-get` commands in wordp * Host Visibility is not enabled by default . To enable Host Visibility we need to annotate the node using `kubectl annotate node` ``` - kubectl annotate node "kubearmor-visibility=process,file,network,capabilities" + kubectl annotate node "kubearmor-visibility=process,file,network,capabilities,syscall" ``` * To confirm it use `kubectl describe` and grep `kubearmor-visibility` @@ -311,9 +311,9 @@ KubeArmor has the ability to let the user select what kind of events have to be ```text kubectl describe ns wordpress-mysql | grep kubearmor-visibility - kubearmor-visibility: process, file, network, capabilities + kubearmor-visibility: process, file, network, capabilities, syscall ``` - * **To update the visibility of namespace :** Now let's update Kubearmor visibility using `kubectl annotate`. Currently KubeArmor supports `process`, `file`, `network`, `capabilities`. + * **To update the visibility of namespace :** Now let's update Kubearmor visibility using `kubectl annotate`. Currently KubeArmor supports `process`, `file`, `network`, `capabilities`, `syscall`. Lets try to update visibility for the namespace `wordpress-mysql` ```text diff --git a/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/kubearmorconfig_types.go b/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/kubearmorconfig_types.go index b43f19c30b..6f6c815cb2 100644 --- a/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/kubearmorconfig_types.go +++ b/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/kubearmorconfig_types.go @@ -51,6 +51,8 @@ type KubeArmorConfigSpec struct { EnableStdOutMsgs bool `json:"enableStdOutMsgs,omitempty"` // +kubebuilder:validation:Optional SeccompEnabled bool `json:"seccompEnabled,omitempty"` + // +kubebuilder:validation:optional + SyscallsVisibility string `json:"syscallsVisibility,omitempty"` } // KubeArmorConfigStatus defines the observed state of KubeArmorConfig diff --git a/pkg/KubeArmorOperator/common/defaults.go b/pkg/KubeArmorOperator/common/defaults.go index f79dfa6ba6..5bd48d0362 100644 --- a/pkg/KubeArmorOperator/common/defaults.go +++ b/pkg/KubeArmorOperator/common/defaults.go @@ -76,6 +76,7 @@ var ( ConfigDefaultCapabilitiesPosture string = "defaultCapabilitiesPosture" ConfigDefaultNetworkPosture string = "defaultNetworkPosture" ConfigDefaultPostureLogs string = "defaultPostureLogs" + ConfigSyscallsVisibility string = "syscallsVisibility" //KubearmorRelayEnvVariables @@ -111,6 +112,7 @@ var ConfigMapData = map[string]string{ ConfigDefaultNetworkPosture: "audit", ConfigVisibility: "process,network,capabilities", ConfigDefaultPostureLogs: "true", + ConfigSyscallsVisibility: "chown,fchownat,mount,unmount,unlink,unlinkat,setuid,setgid,ptrace", } var ConfigDefaultSeccompEnabled = "false" diff --git a/pkg/KubeArmorOperator/config/crd/bases/operator.kubearmor.com_kubearmorconfigs.yaml b/pkg/KubeArmorOperator/config/crd/bases/operator.kubearmor.com_kubearmorconfigs.yaml index 29eb3b6e88..6c204f6271 100644 --- a/pkg/KubeArmorOperator/config/crd/bases/operator.kubearmor.com_kubearmorconfigs.yaml +++ b/pkg/KubeArmorOperator/config/crd/bases/operator.kubearmor.com_kubearmorconfigs.yaml @@ -130,6 +130,8 @@ spec: type: object seccompEnabled: type: boolean + syscallsVisibility: + type: string type: object status: description: KubeArmorConfigStatus defines the observed state of KubeArmorConfig diff --git a/pkg/KubeArmorOperator/config/samples/kubearmor-test.yaml b/pkg/KubeArmorOperator/config/samples/kubearmor-test.yaml index c99076b94a..034d90c359 100644 --- a/pkg/KubeArmorOperator/config/samples/kubearmor-test.yaml +++ b/pkg/KubeArmorOperator/config/samples/kubearmor-test.yaml @@ -15,6 +15,8 @@ spec: defaultNetworkPosture: block defaultVisibility: process,file,network,capabilities seccompEnabled: false + defaultVisibility: process,file,network,capabilities,syscall + syscallsVisibility: chown,fchownat,mount,unmount,unlink,unlinkat,setuid,setgid,ptrace kubearmorImage: image: kubearmor/kubearmor:latest imagePullPolicy: Never diff --git a/pkg/KubeArmorOperator/config/samples/kubearmor-ubi-test.yaml b/pkg/KubeArmorOperator/config/samples/kubearmor-ubi-test.yaml index 1f85fe0205..d8fc9aee6e 100644 --- a/pkg/KubeArmorOperator/config/samples/kubearmor-ubi-test.yaml +++ b/pkg/KubeArmorOperator/config/samples/kubearmor-ubi-test.yaml @@ -13,7 +13,8 @@ spec: defaultCapabilitiesPosture: block defaultFilePosture: block defaultNetworkPosture: block - defaultVisibility: process,file,network,capabilities + defaultVisibility: process,file,network,capabilities,syscall + syscallsVisibility: chown,fchownat,mount,unmount,unlink,unlinkat,setuid,setgid,ptrace kubearmorImage: image: kubearmor/kubearmor-ubi:latest imagePullPolicy: Never diff --git a/pkg/KubeArmorOperator/config/samples/sample-config.yml b/pkg/KubeArmorOperator/config/samples/sample-config.yml index c14a0fc57c..91120fbbf6 100644 --- a/pkg/KubeArmorOperator/config/samples/sample-config.yml +++ b/pkg/KubeArmorOperator/config/samples/sample-config.yml @@ -18,6 +18,7 @@ spec: enableStdOutAlerts: false enableStdOutMsgs: false seccompEnabled: false + syscallsVisibility: chown,fchownat,mount,unmount,unlink,unlinkat,setuid,setgid,ptrace kubearmorImage: image: kubearmor/kubearmor:stable imagePullPolicy: Always diff --git a/pkg/KubeArmorOperator/internal/controller/cluster.go b/pkg/KubeArmorOperator/internal/controller/cluster.go index d9369bbf90..28511da87b 100644 --- a/pkg/KubeArmorOperator/internal/controller/cluster.go +++ b/pkg/KubeArmorOperator/internal/controller/cluster.go @@ -600,6 +600,12 @@ func UpdateConfigMapData(config *opv1.KubeArmorConfigSpec) bool { } } + if config.SyscallsVisibility != "" { + if common.ConfigMapData[common.ConfigSyscallsVisibility] != string(config.SyscallsVisibility) { + common.ConfigMapData[common.ConfigSyscallsVisibility] = string(config.SyscallsVisibility) + updated = true + } + } return updated }