-
Notifications
You must be signed in to change notification settings - Fork 349
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
support for unorchestrated rootfull Podman using OCI hook
Signed-off-by: Cheithanya <cheithanya2002@gmail.com>
- Loading branch information
1 parent
dacac4a
commit 6dda08a
Showing
14 changed files
with
1,194 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#!/bin/bash | ||
set -e | ||
|
||
/KubeArmor/compile.sh | ||
|
||
/KubeArmor/deployHook |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,250 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright 2022 Authors of KubeArmor | ||
|
||
package core | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"io" | ||
"log" | ||
"net" | ||
"os" | ||
"path/filepath" | ||
"sync/atomic" | ||
|
||
kl "github.com/kubearmor/KubeArmor/KubeArmor/common" | ||
cfg "github.com/kubearmor/KubeArmor/KubeArmor/config" | ||
"github.com/kubearmor/KubeArmor/KubeArmor/types" | ||
) | ||
|
||
const kubearmorDir = "/var/run/kubearmor" | ||
|
||
// ContainerEngineHandler defines the interface that any container engine must implement if supports OCI hook | ||
type ContainerEngineHandler interface { | ||
HandleCreateContainer(container types.Container) | ||
HandleDeleteContainer(containerID string) | ||
} | ||
|
||
type PodmanHandler struct { | ||
daemon *KubeArmorDaemon | ||
} | ||
func NewPodmanHandler(dm *KubeArmorDaemon) *PodmanHandler { | ||
return &PodmanHandler{daemon: dm} | ||
} | ||
func (p *PodmanHandler) HandleCreateContainer(container types.Container) { | ||
p.daemon.UpdatePodmanContainer(container.ContainerID, container, "create") | ||
} | ||
func (p *PodmanHandler) HandleDeleteContainer(containerID string) { | ||
p.daemon.UpdatePodmanContainer(containerID, p.daemon.Containers[containerID], "destroy") | ||
} | ||
|
||
|
||
type CRIOHandler struct { | ||
daemon *KubeArmorDaemon | ||
} | ||
func NewCRIOHandler(dm *KubeArmorDaemon) *CRIOHandler { | ||
return &CRIOHandler{daemon: dm} | ||
} | ||
func (c *CRIOHandler) HandleCreateContainer(container types.Container) { | ||
c.daemon.handleContainerCreate(container) | ||
} | ||
func (c *CRIOHandler) HandleDeleteContainer(containerID string) { | ||
c.daemon.handleContainerDelete(containerID) | ||
} | ||
|
||
// ListenToHook starts listening on a UNIX socket and waits for container hooks | ||
// to pass new containers | ||
func (dm *KubeArmorDaemon) ListenToHook() { | ||
if err := os.MkdirAll(kubearmorDir, 0750); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
listenPath := filepath.Join(kubearmorDir, "ka.sock") | ||
err := os.Remove(listenPath) // in case kubearmor crashed and the socket wasn't removed (cleaning the socket file if got crashed) | ||
if err != nil && !errors.Is(err, os.ErrNotExist) { | ||
log.Fatal(err) | ||
} | ||
|
||
socket, err := net.Listen("unix", listenPath) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// Set the permissions of ka.sock to 777 so that rootless podman with user level priviledges can also communicate with the socket | ||
if err := os.Chmod(listenPath, 0777); err != nil { | ||
log.Fatalf("failed to set permissions on %s: %v", listenPath, err) | ||
} | ||
|
||
defer socket.Close() | ||
defer os.Remove(listenPath) | ||
ready := &atomic.Bool{} | ||
|
||
for { | ||
conn, err := socket.Accept() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
go dm.handleConn(conn, ready) | ||
} | ||
|
||
} | ||
|
||
// handleConn gets container details from container hooks. | ||
func (dm *KubeArmorDaemon) handleConn(conn net.Conn, ready *atomic.Bool) { | ||
// We need to makes sure that no containers accepted until all containers created before KubeArmor | ||
// are sent first. This is done mainly to avoid race conditions between hooks sending in | ||
// data that some containers were deleted only for process responsible for sending previous containers | ||
// to send that these containers are created. Which will leave KubeArmor in an incorrect state. | ||
defer conn.Close() | ||
buf := make([]byte, 4096) | ||
|
||
for { | ||
n, err := conn.Read(buf) | ||
if err == io.EOF { | ||
return | ||
} | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
data := types.HookRequest{} | ||
|
||
err = json.Unmarshal(buf[:n], &data) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
if data.Detached { | ||
// we want KubeArmor to start accepting containers after | ||
// all previous container are set | ||
defer ready.Store(true) | ||
} else if !ready.Load() { | ||
_, err = conn.Write([]byte("err")) | ||
if err == io.EOF { | ||
return | ||
} else if err != nil { | ||
log.Println(err) | ||
return | ||
} | ||
continue | ||
} | ||
_, err = conn.Write([]byte("ok")) | ||
if err == io.EOF { | ||
return | ||
} else if err != nil { | ||
log.Println(err) | ||
return | ||
} | ||
|
||
containerLabels,_ := kl.GetLabelsFromString(data.Container.Labels) | ||
// Determine which engine is being used (Podman or CRI-O for now support OCI hooks) | ||
var handler ContainerEngineHandler | ||
if containerLabels["containerType"] == "podman" { | ||
handler = NewPodmanHandler(dm) | ||
} else { | ||
handler = NewCRIOHandler(dm) | ||
} | ||
|
||
// Handle the container create or delete event | ||
if data.Operation == types.HookContainerCreate { | ||
handler.HandleCreateContainer(data.Container) | ||
} else { | ||
handler.HandleDeleteContainer(data.Container.ContainerID) | ||
} | ||
|
||
} | ||
} | ||
func (dm *KubeArmorDaemon) handleContainerCreate(container types.Container) { | ||
endpoint := types.EndPoint{} | ||
|
||
dm.Logger.Printf("added %s", container.ContainerID) | ||
|
||
dm.ContainersLock.Lock() | ||
defer dm.ContainersLock.Unlock() | ||
if _, ok := dm.Containers[container.ContainerID]; !ok { | ||
dm.Containers[container.ContainerID] = container | ||
} else if dm.Containers[container.ContainerID].PidNS == 0 && dm.Containers[container.ContainerID].MntNS == 0 { | ||
c := dm.Containers[container.ContainerID] | ||
c.MntNS = container.MntNS | ||
c.PidNS = container.PidNS | ||
c.AppArmorProfile = container.AppArmorProfile | ||
dm.Containers[c.ContainerID] = c | ||
|
||
dm.EndPointsLock.Lock() | ||
for idx, endPoint := range dm.EndPoints { | ||
if endPoint.NamespaceName == container.NamespaceName && endPoint.EndPointName == container.EndPointName && kl.ContainsElement(endPoint.Containers, container.ContainerID) { | ||
|
||
// update apparmor profiles | ||
if !kl.ContainsElement(endPoint.AppArmorProfiles, container.AppArmorProfile) { | ||
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{}{} | ||
} | ||
|
||
endpoint = dm.EndPoints[idx] | ||
|
||
break | ||
} | ||
} | ||
dm.EndPointsLock.Unlock() | ||
} | ||
|
||
if len(dm.OwnerInfo) > 0 { | ||
container.Owner = dm.OwnerInfo[container.EndPointName] | ||
} | ||
|
||
if dm.SystemMonitor != nil && cfg.GlobalCfg.Policy { | ||
dm.SystemMonitor.AddContainerIDToNsMap(container.ContainerID, container.NamespaceName, container.PidNS, container.MntNS) | ||
dm.RuntimeEnforcer.RegisterContainer(container.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 == types.KubeArmorPolicyEnabled { | ||
// enforce security policies | ||
dm.RuntimeEnforcer.UpdateSecurityPolicies(endpoint) | ||
} | ||
} | ||
} | ||
} | ||
func (dm *KubeArmorDaemon) handleContainerDelete(containerID string) { | ||
dm.ContainersLock.Lock() | ||
container, ok := dm.Containers[containerID] | ||
dm.Logger.Printf("deleted %s", containerID) | ||
if !ok { | ||
dm.ContainersLock.Unlock() | ||
return | ||
} | ||
delete(dm.Containers, containerID) | ||
dm.ContainersLock.Unlock() | ||
|
||
dm.EndPointsLock.Lock() | ||
for idx, endPoint := range dm.EndPoints { | ||
if endPoint.NamespaceName == container.NamespaceName && endPoint.EndPointName == container.EndPointName && kl.ContainsElement(endPoint.Containers, container.ContainerID) { | ||
|
||
// update apparmor profiles | ||
for idxA, profile := range endPoint.AppArmorProfiles { | ||
if profile == container.AppArmorProfile { | ||
dm.EndPoints[idx].AppArmorProfiles = append(dm.EndPoints[idx].AppArmorProfiles[:idxA], dm.EndPoints[idx].AppArmorProfiles[idxA+1:]...) | ||
break | ||
} | ||
} | ||
|
||
break | ||
} | ||
} | ||
dm.EndPointsLock.Unlock() | ||
|
||
if dm.SystemMonitor != nil && cfg.GlobalCfg.Policy { | ||
// update NsMap | ||
dm.SystemMonitor.DeleteContainerIDFromNsMap(containerID, container.NamespaceName, container.PidNS, container.MntNS) | ||
dm.RuntimeEnforcer.UnregisterContainer(containerID) | ||
} | ||
|
||
} | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.