Skip to content

Commit

Permalink
feat: lenient apparmor profiles
Browse files Browse the repository at this point in the history
Signed-off-by: Rudraksh Pareek <[email protected]>
  • Loading branch information
DelusionalOptimist authored and PrimalPimmy committed Jan 16, 2024
1 parent 933047a commit 4b6fe7c
Show file tree
Hide file tree
Showing 16 changed files with 481 additions and 52 deletions.
31 changes: 31 additions & 0 deletions KubeArmor/core/containerdHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"strconv"
"time"

"golang.org/x/exp/slices"

kl "github.com/kubearmor/KubeArmor/KubeArmor/common"
cfg "github.com/kubearmor/KubeArmor/KubeArmor/config"
kg "github.com/kubearmor/KubeArmor/KubeArmor/log"
Expand All @@ -29,6 +31,26 @@ import (
// == Containerd Handler == //
// ======================== //

// DefaultCaps contains all the default capabilities given to a
// container by containerd runtime
// Taken from - https://github.com/containerd/containerd/blob/main/oci/spec.go
var defaultCaps = []string{
"CAP_CHOWN",
"CAP_DAC_OVERRIDE",
"CAP_FSETID",
"CAP_FOWNER",
"CAP_MKNOD",
"CAP_NET_RAW",
"CAP_SETGID",
"CAP_SETUID",
"CAP_SETFCAP",
"CAP_SETPCAP",
"CAP_NET_BIND_SERVICE",
"CAP_SYS_CHROOT",
"CAP_KILL",
"CAP_AUDIT_WRITE",
}

// Containerd Handler
var Containerd *ContainerdHandler

Expand Down Expand Up @@ -142,6 +164,11 @@ func (ch *ContainerdHandler) GetContainerInfo(ctx context.Context, containerID s
spec := iface.(*specs.Spec)
container.AppArmorProfile = spec.Process.ApparmorProfile

// if a container has additional caps than default, we mark it as privileged
if spec.Process.Capabilities != nil && slices.Compare(spec.Process.Capabilities.Permitted, defaultCaps) >= 0 {
container.Privileged = true
}

// == //

taskReq := pt.ListPidsRequest{ContainerID: container.ContainerID}
Expand Down Expand Up @@ -282,6 +309,10 @@ func (dm *KubeArmorDaemon) UpdateContainerdContainer(ctx context.Context, contai
dm.EndPoints[idx].AppArmorProfiles = append(dm.EndPoints[idx].AppArmorProfiles, container.AppArmorProfile)
}

if container.Privileged && dm.EndPoints[idx].PrivilegedContainers != nil {
dm.EndPoints[idx].PrivilegedContainers[container.ContainerName] = struct{}{}
}

break
}
}
Expand Down
5 changes: 5 additions & 0 deletions KubeArmor/core/crioHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ func (ch *CrioHandler) GetContainerInfo(ctx context.Context, containerID string)

// path to container's root storage
container.AppArmorProfile = containerInfo.RuntimeSpec.Process.ApparmorProfile
container.Privileged = containerInfo.Privileged

pid := strconv.Itoa(containerInfo.Pid)

Expand Down Expand Up @@ -240,6 +241,10 @@ func (dm *KubeArmorDaemon) UpdateCrioContainer(ctx context.Context, containerID,
dm.EndPoints[idx].AppArmorProfiles = append(dm.EndPoints[idx].AppArmorProfiles, container.AppArmorProfile)
}

if container.Privileged && dm.EndPoints[idx].PrivilegedContainers != nil {
dm.EndPoints[idx].PrivilegedContainers[container.ContainerName] = struct{}{}
}

break
}
}
Expand Down
7 changes: 7 additions & 0 deletions KubeArmor/core/dockerHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ func (dh *DockerHandler) GetContainerInfo(containerID string) (tp.Container, err
}

container.AppArmorProfile = inspect.AppArmorProfile
if inspect.HostConfig != nil {
container.Privileged = inspect.HostConfig.Privileged
}

// == //

Expand Down Expand Up @@ -241,6 +244,10 @@ func (dm *KubeArmorDaemon) GetAlreadyDeployedDockerContainers() {
dm.EndPoints[idx].AppArmorProfiles = append(dm.EndPoints[idx].AppArmorProfiles, container.AppArmorProfile)
}

if container.Privileged && dm.EndPoints[idx].PrivilegedContainers != nil {
dm.EndPoints[idx].PrivilegedContainers[container.ContainerName] = struct{}{}
}

break
}
}
Expand Down
38 changes: 34 additions & 4 deletions KubeArmor/core/kubeUpdate.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,11 @@ func (dm *KubeArmorDaemon) UpdateEndPointWithPod(action string, pod tp.K8sPod) {
newPoint.AppArmorProfiles = append(newPoint.AppArmorProfiles, container.AppArmorProfile)
}

// if container is privileged
if _, ok := pod.PrivilegedContainers[container.ContainerName]; ok {
container.Privileged = true
}

dm.Containers[containerID] = container

// in case if container runtime detect the container and emit that event before pod event then
Expand Down Expand Up @@ -433,6 +438,11 @@ func (dm *KubeArmorDaemon) UpdateEndPointWithPod(action string, pod tp.K8sPod) {
newEndPoint.AppArmorProfiles = append(newEndPoint.AppArmorProfiles, container.AppArmorProfile)
}

// if container is privileged
if _, ok := pod.PrivilegedContainers[container.ContainerName]; ok {
container.Privileged = true
}

dm.Containers[containerID] = container
// in case if container runtime detect the container and emit that event before pod event then
// the container id will be added to NsMap with "Unknown" namespace
Expand Down Expand Up @@ -688,6 +698,8 @@ func (dm *KubeArmorDaemon) WatchK8sPods() {
}
}

pod.PrivilegedContainers = make(map[string]struct{})
pod.PrivilegedAppArmorProfiles = make(map[string]struct{})
if dm.RuntimeEnforcer != nil && dm.RuntimeEnforcer.EnforcerType == "AppArmor" {
appArmorAnnotations := map[string]string{}
updateAppArmor := false
Expand Down Expand Up @@ -723,7 +735,7 @@ func (dm *KubeArmorDaemon) WatchK8sPods() {
}
}
} else if pod.Metadata["owner.controller"] == "Pod" {
pod, err := K8s.K8sClient.CoreV1().Pods("default").Get(context.Background(), "my-pod", metav1.GetOptions{})
pod, err := K8s.K8sClient.CoreV1().Pods(pod.Metadata["namespaceName"]).Get(context.Background(), podOwnerName, metav1.GetOptions{})
if err == nil {
for _, c := range pod.Spec.Containers {
containers = append(containers, c.Name)
Expand All @@ -746,16 +758,34 @@ func (dm *KubeArmorDaemon) WatchK8sPods() {
}
}

var privileged bool
for _, container := range event.Object.Spec.Containers {
// store privileged containers
if container.SecurityContext != nil &&
((container.SecurityContext.Privileged != nil && *container.SecurityContext.Privileged) ||
(container.SecurityContext.Capabilities != nil && len(container.SecurityContext.Capabilities.Add) > 0)) {
pod.PrivilegedContainers[container.Name] = struct{}{}
privileged = true
}

if _, ok := appArmorAnnotations[container.Name]; !ok && kl.ContainsElement(containers, container.Name) {
appArmorAnnotations[container.Name] = "kubearmor-" + pod.Metadata["namespaceName"] + "-" + podOwnerName + "-" + container.Name
profileName := "kubearmor-" + pod.Metadata["namespaceName"] + "-" + podOwnerName + "-" + container.Name
appArmorAnnotations[container.Name] = profileName
updateAppArmor = true

// if the container is privileged or it has more than one capabilities added
// handle the apparmor profile generation with privileged rules
if privileged {
// container name is unique for all containers in a pod
pod.PrivilegedAppArmorProfiles[profileName] = struct{}{}
}

}
}

if event.Type == "ADDED" {
// update apparmor profiles
dm.RuntimeEnforcer.UpdateAppArmorProfiles(pod.Metadata["podName"], "ADDED", appArmorAnnotations)
dm.RuntimeEnforcer.UpdateAppArmorProfiles(pod.Metadata["podName"], "ADDED", appArmorAnnotations, pod.PrivilegedAppArmorProfiles)

if updateAppArmor && pod.Annotations["kubearmor-policy"] == "enabled" {
if deploymentName, ok := pod.Metadata["owner.controllerName"]; ok {
Expand Down Expand Up @@ -794,7 +824,7 @@ func (dm *KubeArmorDaemon) WatchK8sPods() {
}
} else if event.Type == "DELETED" {
// update apparmor profiles
dm.RuntimeEnforcer.UpdateAppArmorProfiles(pod.Metadata["podName"], "DELETED", appArmorAnnotations)
dm.RuntimeEnforcer.UpdateAppArmorProfiles(pod.Metadata["podName"], "DELETED", appArmorAnnotations, pod.PrivilegedAppArmorProfiles)
}
}

Expand Down
17 changes: 16 additions & 1 deletion KubeArmor/core/unorchestratedUpdates.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,9 +430,16 @@ func (dm *KubeArmorDaemon) ParseAndUpdateContainerSecurityPolicy(event tp.K8sKub
endPointIndex := -1
newPoint := tp.EndPoint{}

var privilegedProfiles map[string]struct{}
for idx, endPoint := range dm.EndPoints {
endPointIndex++

/*
if _, ok := endPoint.PrivilegedContainers[containername]; ok {
privilegedProfiles[appArmorAnnotations[containername]] = struct{}{}
}
*/

// update container rules if there exists another container with same policy.Metadata["policyName"]
for policyIndex, policy := range endPoint.SecurityPolicies {
if policy.Metadata["namespaceName"] == secPolicy.Metadata["namespaceName"] && policy.Metadata["policyName"] == secPolicy.Metadata["policyName"] && endPoint.EndPointName != containername {
Expand Down Expand Up @@ -509,7 +516,7 @@ func (dm *KubeArmorDaemon) ParseAndUpdateContainerSecurityPolicy(event tp.K8sKub
}

if event.Type == "ADDED" {
dm.RuntimeEnforcer.UpdateAppArmorProfiles(containername, "ADDED", appArmorAnnotations)
dm.RuntimeEnforcer.UpdateAppArmorProfiles(containername, "ADDED", appArmorAnnotations, privilegedProfiles)

newPoint.SecurityPolicies = append(newPoint.SecurityPolicies, secPolicy)
if i < 0 {
Expand All @@ -524,12 +531,20 @@ func (dm *KubeArmorDaemon) ParseAndUpdateContainerSecurityPolicy(event tp.K8sKub
newPoint.NetworkVisibilityEnabled = true
newPoint.CapabilitiesVisibilityEnabled = true
newPoint.Containers = []string{}

newPoint.PrivilegedContainers = map[string]struct{}{}

dm.ContainersLock.Lock()
for idx, ctr := range dm.Containers {
if ctr.ContainerName == containername {
newPoint.Containers = append(newPoint.Containers, ctr.ContainerID)
ctr.NamespaceName = newPoint.NamespaceName
ctr.EndPointName = newPoint.EndPointName
/*
if ctr.Privileged {
newPoint.PrivilegedContainers[containername] = struct{}{}
}
*/
dm.Containers[idx] = ctr
}
}
Expand Down
Loading

0 comments on commit 4b6fe7c

Please sign in to comment.