Skip to content

Commit

Permalink
enforcer: add host security enforcement support
Browse files Browse the repository at this point in the history
  • Loading branch information
daemon1024 committed Jul 12, 2022
1 parent 7197c7c commit d6d95e2
Show file tree
Hide file tree
Showing 7 changed files with 338 additions and 4 deletions.
9 changes: 6 additions & 3 deletions KubeArmor/BPF/enforcer.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ int BPF_PROG(enforce_proc, struct linux_binprm *bprm, int ret) {
.mnt_ns = get_task_mnt_ns_id(t)};

if (okey.pid_ns == PROC_PID_INIT_INO) {
return 0;
okey.pid_ns = 0;
okey.mnt_ns = 0;
}

u32 *inner = bpf_map_lookup_elem(&kubearmor_containers, &okey);
Expand Down Expand Up @@ -359,7 +360,8 @@ int BPF_PROG(enforce_file, struct file *file) { // check if ret code available
.mnt_ns = get_task_mnt_ns_id(t)};

if (okey.pid_ns == PROC_PID_INIT_INO) {
return 0;
okey.pid_ns = 0;
okey.mnt_ns = 0;
}

u32 *inner = bpf_map_lookup_elem(&kubearmor_containers, &okey);
Expand Down Expand Up @@ -534,7 +536,8 @@ int BPF_PROG(enforce_net, struct socket *sock, struct sockaddr *address,
.mnt_ns = get_task_mnt_ns_id(t)};

if (okey.pid_ns == PROC_PID_INIT_INO) {
return 0;
okey.pid_ns = 0;
okey.mnt_ns = 0;
}

u32 *inner = bpf_map_lookup_elem(&kubearmor_containers, &okey);
Expand Down
18 changes: 18 additions & 0 deletions KubeArmor/enforcer/bpflsm/enforcer.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/link"
"github.com/cilium/ebpf/rlimit"

cfg "github.com/kubearmor/KubeArmor/KubeArmor/config"
fd "github.com/kubearmor/KubeArmor/KubeArmor/feeder"
tp "github.com/kubearmor/KubeArmor/KubeArmor/types"
)
Expand Down Expand Up @@ -104,6 +106,10 @@ func NewBPFEnforcer(node tp.Node, logger *fd.Feeder) (*BPFEnforcer, error) {
return be, err
}

if cfg.GlobalCfg.HostPolicy {
be.AddHostToMap()
}

return be, nil
}

Expand All @@ -121,6 +127,18 @@ func (be *BPFEnforcer) UpdateSecurityPolicies(endPoint tp.EndPoint) {

}

// UpdateHostSecurityPolicies updates rules for the host
func (be *BPFEnforcer) UpdateHostSecurityPolicies(secPolicies []tp.HostSecurityPolicy) {
// skip if BPFEnforcer is not active
if be == nil {
return
}

be.Logger.Print("Updating host rules")
be.UpdateHostRules(secPolicies)

}

// DestroyBPFEnforcer cleans up the objects for BPF LSM Enforcer
func (be *BPFEnforcer) DestroyBPFEnforcer() error {
if be == nil {
Expand Down
Binary file modified KubeArmor/enforcer/bpflsm/enforcer_bpfeb.o
Binary file not shown.
Binary file modified KubeArmor/enforcer/bpflsm/enforcer_bpfel.o
Binary file not shown.
278 changes: 278 additions & 0 deletions KubeArmor/enforcer/bpflsm/hostRulesHandling.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 Authors of KubeArmor

package bpflsm

import (
"errors"
"os"
"strings"

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

// UpdateHostRules updates host rules map with new rules and resolves conflicting rules
func (be *BPFEnforcer) UpdateHostRules(securityPolicies []tp.HostSecurityPolicy) {
id := "host"

var newrules RuleList

newrules.ProcessBlackList = make(map[InnerKey][8]byte)
newrules.ProcessWhiteList = make(map[InnerKey][8]byte)
newrules.ProcWhiteListPosture = false

newrules.FileBlackList = make(map[InnerKey][8]byte)
newrules.FileWhiteList = make(map[InnerKey][8]byte)
newrules.FileWhiteListPosture = false

newrules.NetworkBlackList = make(map[InnerKey][8]byte)
newrules.NetworkWhiteList = make(map[InnerKey][8]byte)
newrules.NetWhiteListPosture = false

// Generate Fresh Rule Set based on Updated Security Policies
for _, secPolicy := range securityPolicies {
for _, path := range secPolicy.Spec.Process.MatchPaths {

var val [8]byte
val[EXEC] = 1
val[READ] = 1 // Exec needs to pass through file open so need to provide this
if path.OwnerOnly {
val[OWNER] = 1
}
if len(path.FromSource) == 0 {
var key InnerKey
copy(key.Path[:], []byte(path.Path))
if path.Action == "Allow" && cfg.GlobalCfg.HostDefaultFilePosture == "block" {
newrules.ProcWhiteListPosture = true
newrules.ProcessWhiteList[key] = val
} else if path.Action == "Block" && !newrules.ProcWhiteListPosture {
newrules.ProcessBlackList[key] = val
}
} else {
for _, src := range path.FromSource {
var key InnerKey
copy(key.Path[:], []byte(path.Path))
copy(key.Source[:], []byte(src.Path))
if path.Action == "Allow" && cfg.GlobalCfg.HostDefaultFilePosture == "block" {
newrules.ProcWhiteListPosture = true
newrules.ProcessWhiteList[key] = val
} else if path.Action == "Block" && !newrules.ProcWhiteListPosture {
newrules.ProcessBlackList[key] = val
}
}
}
}

for _, dir := range secPolicy.Spec.Process.MatchDirectories {
var val [8]byte
val[EXEC] = 1
val[READ] = 1 // Exec needs to pass through file open so need to provide this
if dir.OwnerOnly {
val[OWNER] = 1
}
if dir.Recursive {
val[RECURSIVE] = 1
}
if len(dir.FromSource) == 0 {
if dir.Action == "Allow" && cfg.GlobalCfg.HostDefaultFilePosture == "block" {
newrules.ProcWhiteListPosture = true
dirtoMap(dir.Directory, "", newrules.ProcessWhiteList, val)
} else if dir.Action == "Block" && !newrules.ProcWhiteListPosture {
dirtoMap(dir.Directory, "", newrules.ProcessBlackList, val)
}
} else {
for _, src := range dir.FromSource {
if dir.Action == "Allow" && cfg.GlobalCfg.HostDefaultFilePosture == "block" {
newrules.ProcWhiteListPosture = true
dirtoMap(dir.Directory, src.Path, newrules.ProcessWhiteList, val)
} else if dir.Action == "Block" && !newrules.ProcWhiteListPosture {
dirtoMap(dir.Directory, src.Path, newrules.ProcessBlackList, val)
}
}
}
}

for _, path := range secPolicy.Spec.File.MatchPaths {
var val [8]byte
val[READ] = 1
if path.OwnerOnly {
val[OWNER] = 1
}
if !path.ReadOnly {
val[WRITE] = 1
}
if len(path.FromSource) == 0 {
var key InnerKey
copy(key.Path[:], []byte(path.Path))
if path.Action == "Allow" && cfg.GlobalCfg.HostDefaultFilePosture == "block" {
newrules.FileWhiteListPosture = true
newrules.FileWhiteList[key] = val
} else if path.Action == "Block" && !newrules.FileWhiteListPosture {
newrules.FileBlackList[key] = val
}
} else {
for _, src := range path.FromSource {
var key InnerKey
copy(key.Path[:], []byte(path.Path))
copy(key.Source[:], []byte(src.Path))
if path.Action == "Allow" && cfg.GlobalCfg.HostDefaultFilePosture == "block" {
newrules.FileWhiteListPosture = true
newrules.FileWhiteList[key] = val
} else if path.Action == "Block" && !newrules.FileWhiteListPosture {
newrules.FileBlackList[key] = val
}
}
}
}

for _, dir := range secPolicy.Spec.File.MatchDirectories {
var val [8]byte
val[READ] = 1
if dir.OwnerOnly {
val[OWNER] = 1
}
if !dir.ReadOnly {
val[WRITE] = 1
}
if dir.Recursive {
val[RECURSIVE] = 1
}
if len(dir.FromSource) == 0 {
if dir.Action == "Allow" && cfg.GlobalCfg.HostDefaultFilePosture == "block" {
newrules.FileWhiteListPosture = true
dirtoMap(dir.Directory, "", newrules.FileWhiteList, val)
} else if dir.Action == "Block" && !newrules.FileWhiteListPosture {
dirtoMap(dir.Directory, "", newrules.FileBlackList, val)
}
} else {
for _, src := range dir.FromSource {
if dir.Action == "Allow" && cfg.GlobalCfg.HostDefaultFilePosture == "block" {
newrules.FileWhiteListPosture = true
dirtoMap(dir.Directory, src.Path, newrules.FileWhiteList, val)
} else if dir.Action == "Block" && !newrules.FileWhiteListPosture {
dirtoMap(dir.Directory, src.Path, newrules.FileBlackList, val)
}
}
}
}

for _, net := range secPolicy.Spec.Network.MatchProtocols {
var val [8]byte
var key = InnerKey{Path: [256]byte{}}
if val, ok := protocols[strings.ToUpper(net.Protocol)]; ok {
key.Path[0] = PROTOCOL
key.Path[1] = val
} else if val, ok := netType[strings.ToUpper(net.Protocol)]; ok {
key.Path[0] = TYPE
key.Path[1] = val
}

if len(net.FromSource) == 0 {
if net.Action == "Allow" && cfg.GlobalCfg.HostDefaultNetworkPosture == "block" {
newrules.NetWhiteListPosture = true
newrules.NetworkWhiteList[key] = val
} else if net.Action == "Block" && !newrules.NetWhiteListPosture {
newrules.NetworkBlackList[key] = val
}
} else {
for _, src := range net.FromSource {
copy(key.Source[:], []byte(src.Path))
if net.Action == "Allow" && cfg.GlobalCfg.HostDefaultNetworkPosture == "block" {
newrules.NetWhiteListPosture = true
newrules.NetworkWhiteList[key] = val
} else if net.Action == "Block" && !newrules.NetWhiteListPosture {
newrules.NetworkBlackList[key] = val
}

}
}
}
}

// Check for differences in Fresh Rules Set and Existing Ruleset
be.resolveConflicts(newrules.ProcWhiteListPosture, be.ContainerMap[id].Rules.ProcWhiteListPosture, newrules.ProcessBlackList, be.ContainerMap[id].Rules.ProcessBlackList, newrules.ProcessWhiteList, be.ContainerMap[id].Rules.ProcessWhiteList, be.ContainerMap[id].Map)
be.resolveConflicts(newrules.FileWhiteListPosture, be.ContainerMap[id].Rules.FileWhiteListPosture, newrules.FileBlackList, be.ContainerMap[id].Rules.FileBlackList, newrules.FileWhiteList, be.ContainerMap[id].Rules.FileWhiteList, be.ContainerMap[id].Map)
be.resolveConflicts(newrules.NetWhiteListPosture, be.ContainerMap[id].Rules.NetWhiteListPosture, newrules.NetworkBlackList, be.ContainerMap[id].Rules.NetworkBlackList, newrules.NetworkWhiteList, be.ContainerMap[id].Rules.NetworkWhiteList, be.ContainerMap[id].Map)

// Update Posture
if list, ok := be.ContainerMap[id]; ok {
list.Rules.ProcWhiteListPosture = newrules.ProcWhiteListPosture
list.Rules.FileWhiteListPosture = newrules.FileWhiteListPosture
list.Rules.NetWhiteListPosture = newrules.NetWhiteListPosture

be.ContainerMap[id] = list
}

if newrules.ProcWhiteListPosture {
if err := be.ContainerMap[id].Map.Put(PROCWHITELIST, [8]byte{}); err != nil {
be.Logger.Errf("error adding rule to map for container %s: %s", id, err)
}
for key, val := range newrules.ProcessWhiteList {
be.ContainerMap[id].Rules.ProcessWhiteList[key] = val
if err := be.ContainerMap[id].Map.Put(key, val); err != nil {
be.Logger.Errf("error adding rule to map for container %s: %s", id, err)
}
}
} else {
if err := be.ContainerMap[id].Map.Delete(PROCWHITELIST); err != nil {
if !errors.Is(err, os.ErrNotExist) {
be.Logger.Err(err.Error())
}
}
for key, val := range newrules.ProcessBlackList {
be.ContainerMap[id].Rules.ProcessBlackList[key] = val
if err := be.ContainerMap[id].Map.Put(key, val); err != nil {
be.Logger.Errf("error adding rule to map for container %s: %s", id, err)
}
}
}

if newrules.FileWhiteListPosture {
if err := be.ContainerMap[id].Map.Put(FILEWHITELIST, [8]byte{}); err != nil {
be.Logger.Errf("error adding rule to map for container %s: %s", id, err)
}
for key, val := range newrules.FileWhiteList {
be.ContainerMap[id].Rules.FileWhiteList[key] = val
if err := be.ContainerMap[id].Map.Put(key, val); err != nil {
be.Logger.Errf("error adding rule to map for container %s: %s", id, err)
}
}
} else {
if err := be.ContainerMap[id].Map.Delete(FILEWHITELIST); err != nil {
if !errors.Is(err, os.ErrNotExist) {
be.Logger.Err(err.Error())
}
}
for key, val := range newrules.FileBlackList {
be.ContainerMap[id].Rules.FileBlackList[key] = val
if err := be.ContainerMap[id].Map.Put(key, val); err != nil {
be.Logger.Errf("error adding rule to map for container %s: %s", id, err)
}
}
}

if newrules.NetWhiteListPosture {
if err := be.ContainerMap[id].Map.Put(NETWHITELIST, [8]byte{}); err != nil {
be.Logger.Errf("error adding rule to map for container %s: %s", id, err)
}
for key, val := range newrules.NetworkWhiteList {
be.ContainerMap[id].Rules.NetworkWhiteList[key] = val
if err := be.ContainerMap[id].Map.Put(key, val); err != nil {
be.Logger.Errf("error adding rule to map for container %s: %s", id, err)
}
}
} else {
if err := be.ContainerMap[id].Map.Delete(NETWHITELIST); err != nil {
if !errors.Is(err, os.ErrNotExist) {
be.Logger.Err(err.Error())
}
}
for key, val := range newrules.NetworkBlackList {
be.ContainerMap[id].Rules.NetworkBlackList[key] = val
if err := be.ContainerMap[id].Map.Put(key, val); err != nil {
be.Logger.Errf("error adding rule to map for container %s: %s", id, err)
}
}
}
}
33 changes: 33 additions & 0 deletions KubeArmor/enforcer/bpflsm/mapHelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,36 @@ func (be *BPFEnforcer) DeleteContainerIDFromMap(containerID string) {
}
delete(be.ContainerMap, containerID)
}

// AddHostToMap adds host to Outer eBPF container Map for initialising enforcement tracking and initiates an InnerMap to store the host specific rules
func (be *BPFEnforcer) AddHostToMap() {
key := NsKey{PidNS: 0, MntNS: 0}

be.ContainerMapLock.Lock()
defer be.ContainerMapLock.Unlock()

im, err := ebpf.NewMap(be.InnerMapSpec)
if err != nil {
be.Logger.Errf("error creating host policy map: %s", err)
return
}

var rules RuleList

rules.ProcessBlackList = make(map[InnerKey][8]byte)
rules.ProcessWhiteList = make(map[InnerKey][8]byte)
rules.ProcWhiteListPosture = false

rules.FileBlackList = make(map[InnerKey][8]byte)
rules.FileWhiteList = make(map[InnerKey][8]byte)
rules.FileWhiteListPosture = false

rules.NetworkBlackList = make(map[InnerKey][8]byte)
rules.NetworkWhiteList = make(map[InnerKey][8]byte)
rules.NetWhiteListPosture = false

be.ContainerMap["host"] = ContainerKV{Key: key, Map: im, Rules: rules}
if err := be.BPFContainerMap.Put(key, im); err != nil {
be.Logger.Errf("error adding host to outer map: %s", err)
}
}
4 changes: 3 additions & 1 deletion KubeArmor/enforcer/runtimeEnforcer.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,9 @@ func (re *RuntimeEnforcer) UpdateHostSecurityPolicies(secPolicies []tp.HostSecur
return
}

if re.EnforcerType == "AppArmor" {
if re.EnforcerType == "BPFLSM" {
re.bpfEnforcer.UpdateHostSecurityPolicies(secPolicies)
} else if re.EnforcerType == "AppArmor" {
re.appArmorEnforcer.UpdateHostSecurityPolicies(secPolicies)
} else if re.EnforcerType == "SELinux" {
re.seLinuxEnforcer.UpdateHostSecurityPolicies(secPolicies)
Expand Down

0 comments on commit d6d95e2

Please sign in to comment.