Skip to content

Commit

Permalink
Merge pull request #145 from kubescape/bump-storage
Browse files Browse the repository at this point in the history
update applicationprofile type
  • Loading branch information
matthyx committed Nov 15, 2023
2 parents 3e6e589 + 9a96516 commit f36051a
Show file tree
Hide file tree
Showing 10 changed files with 238 additions and 86 deletions.
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

0 comments on commit f36051a

Please sign in to comment.