Skip to content

Commit

Permalink
Merge pull request #1704 from DelusionalOptimist/feat/improve-non-k8s…
Browse files Browse the repository at this point in the history
…-policy-handling

feat(core): add label/regex matching in non-k8s
  • Loading branch information
Prateeknandle authored Mar 27, 2024
2 parents 2b5e829 + 39d9969 commit fca7ec5
Show file tree
Hide file tree
Showing 9 changed files with 591 additions and 167 deletions.
2 changes: 1 addition & 1 deletion KubeArmor/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ run: build
.PHONY: run-container
run-container: build
cd $(CURDIR); sudo rm -f /tmp/kubearmor.log
cd $(CURDIR); DEBUG=true sudo -E ./kubearmor -logPath=/tmp/kubearmor.log -enableKubeArmorHostPolicy -enableKubeArmorPolicy -k8s=false -criSocket=unix:///var/run/docker.sock
cd $(CURDIR); sudo -E ./kubearmor -logPath=/tmp/kubearmor.log -enableKubeArmorHostPolicy -enableKubeArmorPolicy -k8s=false -criSocket=unix:///var/run/docker.sock

.PHONY: run-host-only
run-host-only: build
Expand Down
60 changes: 60 additions & 0 deletions KubeArmor/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"os/exec"
"path/filepath"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -57,6 +59,24 @@ func ContainsElement(slice interface{}, element interface{}) bool {
return false
}

// MatchesRegex function
func MatchesRegex(key, element string, array []string) bool {
for _, item := range array {
if strings.Contains(item, key) {
expr, err := regexp.CompilePOSIX(element)
if err != nil {
kg.Warnf("Failed to compile regex: %s", element)
return false
}

return expr.MatchString(item)
}
}

// key not found in array
return true
}

// ObjCommaCanBeExpanded Function
func ObjCommaCanBeExpanded(objptr interface{}) bool {
ovptr := reflect.ValueOf(objptr)
Expand Down Expand Up @@ -441,6 +461,26 @@ func MatchIdentities(identities []string, superIdentities []string) bool {

// if super identities not include identity, return false
for _, identity := range identities {

// match regex if container name or host name label present
if strings.Contains(identity, "kubearmor.io/container.name") {
if !MatchesRegex("kubearmor.io/container.name", identity, superIdentities) {
matched = false
break
}

continue
}

if strings.Contains(identity, "kubearmor.io/hostname") {
if !MatchesRegex("kubearmor.io/hostname", identity, superIdentities) {
matched = false
break
}

continue
}

if !ContainsElement(superIdentities, identity) {
matched = false
break
Expand Down Expand Up @@ -521,3 +561,23 @@ func GetBootTime() string {

return currentTime.Add(-time.Duration(info.Uptime) * time.Second).Truncate(time.Second).UTC().String()
}

func GetLabelsFromString(labelString string) (map[string]string, []string) {
labelsMap := make(map[string]string)

labelsSlice := strings.Split(labelString, ",")
for _, label := range labelsSlice {
key, value, ok := strings.Cut(label, "=")
if !ok {
continue
}

labelsMap[key] = value
}

sort.Slice(labelsSlice, func(i, j int) bool {
return labelsSlice[i] < labelsSlice[j]
})

return labelsMap, labelsSlice
}
137 changes: 122 additions & 15 deletions KubeArmor/core/containerdHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"golang.org/x/exp/slices"

"github.com/kubearmor/KubeArmor/KubeArmor/common"
kl "github.com/kubearmor/KubeArmor/KubeArmor/common"
cfg "github.com/kubearmor/KubeArmor/KubeArmor/config"
kg "github.com/kubearmor/KubeArmor/KubeArmor/log"
Expand Down Expand Up @@ -209,7 +210,7 @@ func (ch *ContainerdHandler) GetContainerInfo(ctx context.Context, containerID s

// == //

if cfg.GlobalCfg.StateAgent && !cfg.GlobalCfg.K8sEnv {
if !cfg.GlobalCfg.K8sEnv {
container.ContainerImage = res.Container.Image //+ kl.GetSHA256ofImage(inspect.Image)

container.NodeName = cfg.GlobalCfg.Host
Expand All @@ -221,6 +222,13 @@ func (ch *ContainerdHandler) GetContainerInfo(ctx context.Context, containerID s
for k, v := range spec.Annotations {
labels = append(labels, k+"="+v)
}

// for policy matching
labels = append(labels, "namespaceName="+container.NamespaceName)
if _, ok := containerLabels["kubearmor.io/container.name"]; !ok {
labels = append(labels, "kubearmor.io/container.name="+container.ContainerName)
}

container.Labels = strings.Join(labels, ",")
}

Expand Down Expand Up @@ -301,12 +309,89 @@ func (dm *KubeArmorDaemon) UpdateContainerdContainer(ctx context.Context, contai
return false
}

endpoint := tp.EndPoint{}
endPoint := tp.EndPoint{}

dm.ContainersLock.Lock()
if _, ok := dm.Containers[container.ContainerID]; !ok {
dm.Containers[container.ContainerID] = container
dm.ContainersLock.Unlock()

// create/update endPoint in non-k8s mode
if !dm.K8sEnabled {
endPointEvent := "ADDED"
endPointIdx := -1

containerLabels, containerIdentities := common.GetLabelsFromString(container.Labels)

dm.EndPointsLock.Lock()
// if a named endPoint exists we update
for idx, ep := range dm.EndPoints {
if container.ContainerName == ep.EndPointName || kl.MatchIdentities(ep.Identities, containerIdentities) {
endPointEvent = "UPDATED"
endPointIdx = idx
endPoint = ep
break
}
}

switch endPointEvent {
case "ADDED":
endPoint.EndPointName = container.ContainerName
endPoint.ContainerName = container.ContainerName
endPoint.NamespaceName = container.NamespaceName

endPoint.Containers = []string{container.ContainerID}

endPoint.Labels = containerLabels
endPoint.Identities = containerIdentities

endPoint.PolicyEnabled = tp.KubeArmorPolicyEnabled
endPoint.ProcessVisibilityEnabled = true
endPoint.FileVisibilityEnabled = true
endPoint.NetworkVisibilityEnabled = true
endPoint.CapabilitiesVisibilityEnabled = true

endPoint.AppArmorProfiles = []string{"kubearmor_" + container.ContainerName}

globalDefaultPosture := tp.DefaultPosture{
FileAction: cfg.GlobalCfg.DefaultFilePosture,
NetworkAction: cfg.GlobalCfg.DefaultNetworkPosture,
CapabilitiesAction: cfg.GlobalCfg.DefaultCapabilitiesPosture,
}
endPoint.DefaultPosture = globalDefaultPosture

dm.SecurityPoliciesLock.RLock()
for _, secPol := range dm.SecurityPolicies {
if kl.MatchIdentities(secPol.Spec.Selector.Identities, endPoint.Identities) {
endPoint.SecurityPolicies = append(endPoint.SecurityPolicies, secPol)
}
}
dm.SecurityPoliciesLock.RUnlock()

dm.EndPoints = append(dm.EndPoints, endPoint)
case "UPDATED":
// in case of AppArmor enforcement when endPoint has to be created first
endPoint.Containers = append(endPoint.Containers, container.ContainerID)

// if this container has any additional identities, add them
endPoint.Identities = append(endPoint.Identities, containerIdentities...)
endPoint.Identities = slices.Compact(endPoint.Identities)

// add other policies
endPoint.SecurityPolicies = []tp.SecurityPolicy{}
dm.SecurityPoliciesLock.RLock()
for _, secPol := range dm.SecurityPolicies {
if kl.MatchIdentities(secPol.Spec.Selector.Identities, endPoint.Identities) {
endPoint.SecurityPolicies = append(endPoint.SecurityPolicies, secPol)
}
}
dm.SecurityPoliciesLock.RUnlock()

dm.EndPoints[endPointIdx] = endPoint
}
dm.EndPointsLock.Unlock()
}

} else if dm.Containers[container.ContainerID].PidNS == 0 && dm.Containers[container.ContainerID].MntNS == 0 {
// this entry was updated by kubernetes before docker detects it
// thus, we here use the info given by kubernetes instead of the info given by docker
Expand Down Expand Up @@ -345,7 +430,21 @@ func (dm *KubeArmorDaemon) UpdateContainerdContainer(ctx context.Context, contai
dm.EndPoints[idx].PrivilegedContainers[container.ContainerName] = struct{}{}
}

endpoint = dm.EndPoints[idx]
// add identities and labels if non-k8s
if !dm.K8sEnabled {
labelsSlice := strings.Split(container.Labels, ",")
for _, label := range labelsSlice {
key, value, ok := strings.Cut(label, "=")
if !ok {
continue
}

endPoint.Labels[key] = value
endPoint.Identities = append(endPoint.Identities, key+"="+value)
}
}

endPoint = dm.EndPoints[idx]

break
}
Expand All @@ -361,23 +460,15 @@ func (dm *KubeArmorDaemon) UpdateContainerdContainer(ctx context.Context, contai
dm.SystemMonitor.AddContainerIDToNsMap(containerID, container.NamespaceName, container.PidNS, container.MntNS)
dm.RuntimeEnforcer.RegisterContainer(containerID, container.PidNS, container.MntNS)

if len(endpoint.SecurityPolicies) > 0 { // struct can be empty or no policies registered for the endpoint yet
dm.Logger.UpdateSecurityPolicies("ADDED", endpoint)
if dm.RuntimeEnforcer != nil && endpoint.PolicyEnabled == tp.KubeArmorPolicyEnabled {
if len(endPoint.SecurityPolicies) > 0 { // struct can be empty or no policies registered for the endPoint yet
dm.Logger.UpdateSecurityPolicies("ADDED", endPoint)
if dm.RuntimeEnforcer != nil && endPoint.PolicyEnabled == tp.KubeArmorPolicyEnabled {
// enforce security policies
dm.RuntimeEnforcer.UpdateSecurityPolicies(endpoint)
dm.RuntimeEnforcer.UpdateSecurityPolicies(endPoint)
}
}
}

if !dm.K8sEnabled {
dm.ContainersLock.Lock()
dm.EndPointsLock.Lock()
dm.MatchandUpdateContainerSecurityPolicies(containerID)
dm.EndPointsLock.Unlock()
dm.ContainersLock.Unlock()
}

if cfg.GlobalCfg.StateAgent {
container.Status = "running"
go dm.StateAgent.PushContainerEvent(container, state.EventAdded)
Expand All @@ -400,6 +491,22 @@ func (dm *KubeArmorDaemon) UpdateContainerdContainer(ctx context.Context, contai
delete(dm.Containers, containerID)
dm.ContainersLock.Unlock()

// delete endpoint if no security rules and containers
if !dm.K8sEnabled {
idx := 0
endpointsLength := len(dm.EndPoints)
for idx < endpointsLength {
endpoint := dm.EndPoints[idx]
if container.NamespaceName == endpoint.NamespaceName && container.ContainerName == endpoint.EndPointName &&
len(endpoint.SecurityPolicies) == 0 && len(endpoint.Containers) == 0 {
dm.EndPoints = append(dm.EndPoints[:idx], dm.EndPoints[idx+1:]...)
endpointsLength--
idx--
}
idx++
}
}

dm.EndPointsLock.Lock()
for idx, endPoint := range dm.EndPoints {
if endPoint.NamespaceName == container.NamespaceName && endPoint.EndPointName == container.EndPointName && kl.ContainsElement(endPoint.Containers, container.ContainerID) {
Expand Down
Loading

0 comments on commit fca7ec5

Please sign in to comment.