Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update applicationprofile type #145

Merged
merged 4 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
github.com/kubescape/backend v0.0.13
github.com/kubescape/go-logger v0.0.21
github.com/kubescape/k8s-interface v0.0.148
github.com/kubescape/storage v0.0.33
github.com/kubescape/storage v0.0.38
github.com/panjf2000/ants/v2 v2.8.1
github.com/spf13/viper v1.16.0
github.com/stretchr/testify v1.8.4
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,8 @@ github.com/kubescape/go-logger v0.0.21 h1:4ZRIEw3UGUH6BG/cH3yiqFipzQSfGAoCrxlsZu
github.com/kubescape/go-logger v0.0.21/go.mod h1:x3HBpZo3cMT/WIdy18BxvVVd5D0e/PWFVk/HiwBNu3g=
github.com/kubescape/k8s-interface v0.0.148 h1:vtXDUjvCow5wMdDvb5c/Td9SpafeRqsV/LnOd0NVVsE=
github.com/kubescape/k8s-interface v0.0.148/go.mod h1:5sz+5Cjvo98lTbTVDiDA4MmlXxeHSVMW/wR0V3hV4K8=
github.com/kubescape/storage v0.0.33 h1:CAD2A6cO1mekX6ecOGYrHfY5HMOZWm3uzoxSTafqxgs=
github.com/kubescape/storage v0.0.33/go.mod h1:ObCIVOnVyWwRwU0iuKTzOnrJQScqPgkw0FgvSINwosY=
github.com/kubescape/storage v0.0.38 h1:LR+QTqjeYw4kM1repIltDLDQGqHgKmG196lQyHP+xUw=
github.com/kubescape/storage v0.0.38/go.mod h1:ObCIVOnVyWwRwU0iuKTzOnrJQScqPgkw0FgvSINwosY=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func main() {
// Create the application profile manager
var applicationProfileManager applicationprofilemanager.ApplicationProfileManagerClient
if cfg.EnableApplicationProfile {
applicationProfileManager, err = applicationprofilemanagerv1.CreateApplicationProfileManager(ctx, cfg, k8sClient, storageClient)
applicationProfileManager, err = applicationprofilemanagerv1.CreateApplicationProfileManager(ctx, cfg, clusterData.ClusterName, k8sClient, storageClient)
if err != nil {
logger.L().Ctx(ctx).Fatal("error creating the application profile manager", helpers.Error(err))
}
Expand Down
229 changes: 156 additions & 73 deletions pkg/applicationprofilemanager/v1/applicationprofile_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
"node-agent/pkg/k8sclient"
"node-agent/pkg/storage"
"node-agent/pkg/utils"
"sort"
"time"

"github.com/armosec/utils-k8s-go/wlid"
mapset "github.com/deckarep/golang-set/v2"
"github.com/goradd/maps"
containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection"
Expand All @@ -26,6 +28,7 @@ import (

type ApplicationProfileManager struct {
cfg config.Config
clusterName string
ctx context.Context
capabilitiesSets maps.SafeMap[string, mapset.Set[string]] // key is k8sContainerID
execSets maps.SafeMap[string, map[string]mapset.Set[string]] // key is k8sContainerID
Expand All @@ -38,9 +41,10 @@ type ApplicationProfileManager struct {

var _ applicationprofilemanager.ApplicationProfileManagerClient = (*ApplicationProfileManager)(nil)

func CreateApplicationProfileManager(ctx context.Context, cfg config.Config, k8sClient k8sclient.K8sClientInterface, storageClient storage.StorageClient) (*ApplicationProfileManager, error) {
func CreateApplicationProfileManager(ctx context.Context, cfg config.Config, clusterName string, k8sClient k8sclient.K8sClientInterface, storageClient storage.StorageClient) (*ApplicationProfileManager, error) {
return &ApplicationProfileManager{
cfg: cfg,
clusterName: clusterName,
ctx: ctx,
k8sClient: k8sClient,
storageClient: storageClient,
Expand All @@ -57,6 +61,24 @@ func (am *ApplicationProfileManager) ensureInstanceID(ctx context.Context, conta
return
}
pod := wl.(*workloadinterface.Workload)
// find parentWlid
kind, name, err := am.k8sClient.CalculateWorkloadParentRecursive(pod)
if err != nil {
logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to calculate workload parent", helpers.Error(err))
return
}
parentWorkload, err := am.k8sClient.GetWorkload(pod.GetNamespace(), kind, name)
if err != nil {
logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to get parent workload", helpers.Error(err))
return
}
w := parentWorkload.(*workloadinterface.Workload)
watchedContainer.Wlid = w.GenerateWlid(am.clusterName)
err = wlid.IsWlidValid(watchedContainer.Wlid)
if err != nil {
logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to validate WLID", helpers.Error(err))
return
}
// find instanceID
instanceIDs, err := instanceidhandler.GenerateInstanceID(pod)
if err != nil {
Expand All @@ -69,6 +91,38 @@ func (am *ApplicationProfileManager) ensureInstanceID(ctx context.Context, conta
watchedContainer.InstanceID = instanceIDs[i]
}
}
// find container type and index
// containers
if watchedContainer.ContainerType == utils.Unknown {
containers, err := pod.GetContainers()
if err != nil {
logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to get containers", helpers.Error(err))
return
}
for i, c := range containers {
if c.Name == container.K8s.ContainerName {
watchedContainer.ContainerIndex = i
watchedContainer.ContainerType = utils.Container
break
}
}
}
// initContainers
if watchedContainer.ContainerType == utils.Unknown {
initContainers, err := pod.GetInitContainers()
if err != nil {
logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to get init containers", helpers.Error(err))
return
}
for i, c := range initContainers {
if c.Name == container.K8s.ContainerName {
watchedContainer.ContainerIndex = i
watchedContainer.ContainerType = utils.InitContainer
break
}
}
}
// FIXME ephemeralContainers are not supported yet
}

func (am *ApplicationProfileManager) deleteResources(watchedContainer *utils.WatchedContainerData) {
Expand Down Expand Up @@ -115,101 +169,130 @@ func (am *ApplicationProfileManager) saveProfile(ctx context.Context, watchedCon
return
}

// activity sets
// check if we have new activities to save
var addedActivities int
syscalls := mapset.NewSet[string]()
// existing activity
existingActivity, _ := am.storageClient.GetApplicationActivity(slug, namespace)
if existingActivity != nil {
existingActivity, _ := am.storageClient.GetApplicationActivity(namespace, slug)
if existingActivity != nil && existingActivity.Spec.Syscalls != nil {
syscalls.Append(existingActivity.Spec.Syscalls...)
}
// new activity
newActivity := &v1beta1.ApplicationActivity{
ObjectMeta: metav1.ObjectMeta{
Name: slug,
Annotations: map[string]string{
instanceidhandler.WlidMetadataKey: watchedContainer.Wlid,
instanceidhandler.InstanceIDMetadataKey: watchedContainer.InstanceID.GetStringFormatted(),
instanceidhandler.ContainerNameMetadataKey: watchedContainer.InstanceID.GetContainerName(),
instanceidhandler.ImageIDMetadataKey: watchedContainer.ImageID,
instanceidhandler.StatusMetadataKey: "",
},
Labels: utils.GetLabels(watchedContainer),
},
}
// add syscalls
newSyscalls, err := am.syscallPeekFunc(watchedContainer.NsMntId)
if err == nil {
syscalls.Append(newSyscalls...)
} else {
// get syscalls from IG
observedSyscalls, err := am.syscallPeekFunc(watchedContainer.NsMntId)
if err != nil {
logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to get syscalls", helpers.Error(err))
}
newActivity.Spec.Syscalls = syscalls.ToSlice()
if err := am.storageClient.CreateApplicationActivity(newActivity, namespace); err != nil {
logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to save application activity", helpers.Error(err))
addedActivities += syscalls.Append(observedSyscalls...)
// new activity
if addedActivities > 0 {
newActivity := &v1beta1.ApplicationActivity{
ObjectMeta: metav1.ObjectMeta{
Name: slug,
Annotations: map[string]string{
instanceidhandler.WlidMetadataKey: watchedContainer.Wlid,
instanceidhandler.StatusMetadataKey: "",
},
Labels: utils.GetLabels(watchedContainer, true),
},
}
// add syscalls
newActivity.Spec.Syscalls = syscalls.ToSlice()
// save application activity
if err := am.storageClient.CreateApplicationActivity(newActivity, namespace); err != nil {
logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to save application activity", helpers.Error(err))
}
logger.L().Debug("ApplicationProfileManager - saved application activity", helpers.String("slug", slug), helpers.String("container ID", watchedContainer.ContainerID), helpers.String("k8s workload", watchedContainer.K8sContainerID))
}

// profile sets
capabilities := am.capabilitiesSets.Get(watchedContainer.K8sContainerID)
execs := am.execSets.Get(watchedContainer.K8sContainerID)
opens := am.openSets.Get(watchedContainer.K8sContainerID)
var addedProfiles int
capabilities := mapset.NewSet[string]()
execs := make(map[string]mapset.Set[string])
opens := make(map[string]mapset.Set[string])
// existing profile
existingProfile, _ := am.storageClient.GetApplicationProfile(slug, namespace)
if existingProfile != nil {
capabilities.Append(existingProfile.Spec.Capabilities...)
for _, exec := range existingProfile.Spec.Execs {
existingProfile, _ := am.storageClient.GetApplicationProfile(namespace, slug)
existingProfileContainer := utils.GetApplicationProfileContainer(existingProfile, watchedContainer.ContainerType, watchedContainer.ContainerIndex)
if existingProfileContainer != nil {
capabilities.Append(existingProfileContainer.Capabilities...)
for _, exec := range existingProfileContainer.Execs {
if _, exist := execs[exec.Path]; !exist {
execs[exec.Path] = mapset.NewSet[string]()
}
execs[exec.Path].Append(exec.Args...)
}
for _, open := range existingProfile.Spec.Opens {
for _, open := range existingProfileContainer.Opens {
if _, exist := opens[open.Path]; !exist {
opens[open.Path] = mapset.NewSet[string]()
}
opens[open.Path].Append(open.Flags...)
}
}
// new profile
newProfile := &v1beta1.ApplicationProfile{
ObjectMeta: metav1.ObjectMeta{
Name: slug,
Annotations: map[string]string{
instanceidhandler.WlidMetadataKey: watchedContainer.Wlid,
instanceidhandler.InstanceIDMetadataKey: watchedContainer.InstanceID.GetStringFormatted(),
instanceidhandler.ContainerNameMetadataKey: watchedContainer.InstanceID.GetContainerName(),
instanceidhandler.ImageIDMetadataKey: watchedContainer.ImageID,
instanceidhandler.StatusMetadataKey: "",
},
Labels: utils.GetLabels(watchedContainer),
},
}
// add capabilities
newProfile.Spec.Capabilities = capabilities.ToSlice()
// add execs
newProfile.Spec.Execs = make([]v1beta1.ExecCalls, 0)
for path, exec := range execs {
newProfile.Spec.Execs = append(newProfile.Spec.Execs, v1beta1.ExecCalls{
Path: path,
Args: exec.ToSlice(),
})
}
// add opens
newProfile.Spec.Opens = make([]v1beta1.OpenCalls, 0)
for path, open := range opens {
newProfile.Spec.Opens = append(newProfile.Spec.Opens, v1beta1.OpenCalls{
Path: path,
Flags: open.ToSlice(),
})
}
if err := am.storageClient.CreateApplicationProfile(newProfile, namespace); err != nil {
logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to save application profile", helpers.Error(err))
// get capabilities, execs and opens from IG
addedProfiles += capabilities.Append(am.capabilitiesSets.Get(watchedContainer.K8sContainerID).ToSlice()...)
for path, exec := range am.execSets.Get(watchedContainer.K8sContainerID) {
if _, exist := execs[path]; !exist {
execs[path] = mapset.NewSet[string]()
}
addedProfiles += execs[path].Append(exec.ToSlice()...)
}
// profile summary
summary := &v1beta1.ApplicationProfileSummary{
ObjectMeta: newProfile.ObjectMeta,
for path, open := range am.openSets.Get(watchedContainer.K8sContainerID) {
if _, exist := opens[path]; !exist {
opens[path] = mapset.NewSet[string]()
}
addedProfiles += opens[path].Append(open.ToSlice()...)
}
if err := am.storageClient.CreateApplicationProfileSummary(summary, namespace); err != nil {
logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to save application profile summary", helpers.Error(err))
// new profile
if addedProfiles > 0 {
newProfile := &v1beta1.ApplicationProfile{
ObjectMeta: metav1.ObjectMeta{
Name: slug,
Annotations: map[string]string{
instanceidhandler.WlidMetadataKey: watchedContainer.Wlid,
instanceidhandler.StatusMetadataKey: "",
},
Labels: utils.GetLabels(watchedContainer, true),
},
}
newProfileContainer := &v1beta1.ApplicationProfileContainer{
Name: watchedContainer.InstanceID.GetContainerName(),
}
// add capabilities
newProfileContainer.Capabilities = capabilities.ToSlice()
sort.Strings(newProfileContainer.Capabilities)
// add execs
newProfileContainer.Execs = make([]v1beta1.ExecCalls, 0)
for path, exec := range execs {
args := exec.ToSlice()
sort.Strings(args)
newProfileContainer.Execs = append(newProfileContainer.Execs, v1beta1.ExecCalls{
Path: path,
Args: args,
})
}
// add opens
newProfileContainer.Opens = make([]v1beta1.OpenCalls, 0)
for path, open := range opens {
flags := open.ToSlice()
sort.Strings(flags)
newProfileContainer.Opens = append(newProfileContainer.Opens, v1beta1.OpenCalls{
Path: path,
Flags: flags,
})
}
// insert application profile container
utils.InsertApplicationProfileContainer(newProfile, watchedContainer.ContainerType, watchedContainer.ContainerIndex, newProfileContainer)
// save application profile
if err := am.storageClient.CreateApplicationProfile(newProfile, namespace); err != nil {
logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to save application profile", helpers.Error(err))
}
logger.L().Debug("ApplicationProfileManager - saved application profile", helpers.String("slug", slug), helpers.String("container ID", watchedContainer.ContainerID), helpers.String("k8s workload", watchedContainer.K8sContainerID))
// profile summary
summary := &v1beta1.ApplicationProfileSummary{
ObjectMeta: newProfile.ObjectMeta,
}
if err := am.storageClient.CreateApplicationProfileSummary(summary, namespace); err != nil {
logger.L().Ctx(ctx).Error("ApplicationProfileManager - failed to save application profile summary", helpers.Error(err))
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestApplicationProfileManager(t *testing.T) {
ctx := context.TODO()
k8sClient := &k8sclient.K8sClientMock{}
storageClient := &storage.StorageHttpClientMock{}
am, err := CreateApplicationProfileManager(ctx, cfg, k8sClient, storageClient)
am, err := CreateApplicationProfileManager(ctx, cfg, "cluster", k8sClient, storageClient)
assert.NoError(t, err)
// report container started
container := &containercollection.Container{
Expand Down Expand Up @@ -69,9 +69,9 @@ func TestApplicationProfileManager(t *testing.T) {
sort.Strings(storageClient.ApplicationActivities[0].Spec.Syscalls)
assert.Equal(t, []string{"dup", "listen", "open"}, storageClient.ApplicationActivities[0].Spec.Syscalls)
assert.Equal(t, 2, len(storageClient.ApplicationProfiles))
sort.Strings(storageClient.ApplicationProfiles[0].Spec.Capabilities)
assert.Equal(t, []string{"NET_BIND_SERVICE", "NET_BROADCAST"}, storageClient.ApplicationProfiles[0].Spec.Capabilities)
assert.Equal(t, []v1beta1.ExecCalls{{Path: "/bin/bash", Args: []string{"-c", "ls"}, Envs: []string(nil)}}, storageClient.ApplicationProfiles[0].Spec.Execs)
assert.Equal(t, []v1beta1.OpenCalls{{Path: "/etc/passwd", Flags: []string{"O_RDONLY"}}}, storageClient.ApplicationProfiles[0].Spec.Opens)
sort.Strings(storageClient.ApplicationProfiles[0].Spec.Containers[0].Capabilities)
assert.Equal(t, []string{"NET_BIND_SERVICE", "NET_BROADCAST"}, storageClient.ApplicationProfiles[0].Spec.Containers[1].Capabilities)
assert.Equal(t, []v1beta1.ExecCalls{{Path: "/bin/bash", Args: []string{"-c", "ls"}, Envs: []string(nil)}}, storageClient.ApplicationProfiles[0].Spec.Containers[1].Execs)
assert.Equal(t, []v1beta1.OpenCalls{{Path: "/etc/passwd", Flags: []string{"O_RDONLY"}}}, storageClient.ApplicationProfiles[0].Spec.Containers[1].Opens)
assert.Equal(t, 2, len(storageClient.ApplicationProfileSummaries))
}
4 changes: 4 additions & 0 deletions pkg/k8sclient/k8sclient_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ func (k K8sClientMock) GetWorkload(namespace, _, name string) (k8sinterface.IWor
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "log",
"image": "fluentbit",
},
map[string]interface{}{
"name": "cont",
"image": "nginx",
Expand Down
2 changes: 1 addition & 1 deletion pkg/sbomhandler/v1/sbomhandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (sc *SBOMHandler) FilterSBOM(watchedContainer *utils.WatchedContainerData,
instanceidhandler.ImageIDMetadataKey: watchedContainer.ImageID,
instanceidhandler.StatusMetadataKey: "",
},
Labels: utils.GetLabels(watchedContainer),
Labels: utils.GetLabels(watchedContainer, false),
},
}
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/storage/storage_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ func (sc *StorageHttpClientMock) GetApplicationActivity(_, _ string) (*spdxv1bet
func (sc *StorageHttpClientMock) GetApplicationProfile(_, _ string) (*spdxv1beta1.ApplicationProfile, error) {
return &spdxv1beta1.ApplicationProfile{
Spec: spdxv1beta1.ApplicationProfileSpec{
Capabilities: []string{"NET_BROADCAST"},
Containers: []spdxv1beta1.ApplicationProfileContainer{
{Capabilities: []string{"SYS_ADMIN"}},
{Capabilities: []string{"NET_BROADCAST"}},
},
},
}, nil
}
Expand Down
Loading
Loading