From b722107d273699ab2cf0ce686bec2366ad144e1a Mon Sep 17 00:00:00 2001 From: Ankur Kothiwal Date: Mon, 5 Jun 2023 23:56:48 +0530 Subject: [PATCH] restructure policy format to remove redundant allow rule Signed-off-by: Ankur Kothiwal --- src/crownjewel/crownjewel.go | 112 +++++++++++++++++++++-------------- 1 file changed, 68 insertions(+), 44 deletions(-) diff --git a/src/crownjewel/crownjewel.go b/src/crownjewel/crownjewel.go index 0853ae5e..fb86b1f0 100644 --- a/src/crownjewel/crownjewel.go +++ b/src/crownjewel/crownjewel.go @@ -4,9 +4,11 @@ import ( "context" "encoding/json" "fmt" + "strconv" "strings" "github.com/accuknox/auto-policy-discovery/src/cluster" + "github.com/accuknox/auto-policy-discovery/src/common" "github.com/accuknox/auto-policy-discovery/src/config" cfg "github.com/accuknox/auto-policy-discovery/src/config" "github.com/accuknox/auto-policy-discovery/src/libs" @@ -95,6 +97,7 @@ func StopCrownjewelCronJob() { } } +// Create Crown Jewel Policy based on K8s object type func CrownjewelPolicyMain() { client := cluster.ConnectK8sClient() deployments, err := client.AppsV1().Deployments("").List(context.Background(), metav1.ListOptions{}) @@ -118,34 +121,34 @@ func CrownjewelPolicyMain() { return } for _, d := range deployments.Items { - _, err := GetMountPaths(client, d.Name, d.Namespace, d.Spec.Template.Labels) + err := getCrownjewelPolicy(client, d.Name, d.Namespace, d.Spec.Template.Labels) if err != nil { log.Error().Msg("Error getting mount paths, err=" + err.Error()) } } for _, r := range replicaSets.Items { - _, err := GetMountPaths(client, r.Name, r.Namespace, r.Spec.Template.Labels) + err := getCrownjewelPolicy(client, r.Name, r.Namespace, r.Spec.Template.Labels) if err != nil { log.Error().Msg("Error getting mount paths, err=" + err.Error()) } } for _, s := range statefulSets.Items { - _, err := GetMountPaths(client, s.Name, s.Namespace, s.Spec.Template.Labels) + err := getCrownjewelPolicy(client, s.Name, s.Namespace, s.Spec.Template.Labels) if err != nil { log.Error().Msg("Error getting mount paths, err=" + err.Error()) } } for _, rs := range daemonsets.Items { - _, err := GetMountPaths(client, rs.Name, rs.Namespace, rs.Spec.Template.Labels) + err := getCrownjewelPolicy(client, rs.Name, rs.Namespace, rs.Spec.Template.Labels) if err != nil { log.Error().Msg("Error getting mount paths, err=" + err.Error()) } } } -// var log *zerolog.Logger type LabelMap = map[string]string +// Get list of running processes from observability data func getProcessList(client kubernetes.Interface, namespace string, labels types.LabelMap) ([]string, error) { var processList []string duplicatePaths := make(map[string]bool) @@ -165,12 +168,16 @@ func getProcessList(client kubernetes.Interface, namespace string, labels types. Type: "process,file", }) if err != nil { - // log.Warn().Msgf("Error getting summary data for pod %s, container %s, namespace %s: %s", pod.Name, container.Name, pod.Namespace, err.Error()) + log.Warn().Msgf("Error getting summary data for pod %s, container %s, namespace %s: %s", pod.Name, container.Name, pod.Namespace, err.Error()) break } for _, fileData := range sumResp.FileData { if !duplicatePaths[fileData.Source] { + // ignore serviceaccount related process + if strings.Contains(fileData.Destination, "serviceaccount") { + continue + } processList = append(processList, fileData.Source) duplicatePaths[fileData.Source] = true } @@ -180,6 +187,28 @@ func getProcessList(client kubernetes.Interface, namespace string, labels types. return processList, nil } +// Get all mounted paths +func getVolumeMountPaths(client kubernetes.Interface, labels LabelMap) ([]string, error) { + podList, err := client.CoreV1().Pods("").List(context.Background(), metav1.ListOptions{ + LabelSelector: libs.LabelMapToString(labels), + }) + if err != nil { + return nil, fmt.Errorf("failed to get pod list: %v", err) + } + + var mountPaths []string + + for _, pod := range podList.Items { + for _, container := range pod.Spec.Containers { + for _, volumeMount := range container.VolumeMounts { + mountPaths = append(mountPaths, volumeMount.MountPath) + } + } + } + return mountPaths, nil +} + +// Get used mount paths from observability data func usedMountPath(client kubernetes.Interface, namespace string, labels types.LabelMap) ([]string, map[string]string, error) { var sumResponses []string fromSource := make(map[string]string) @@ -200,7 +229,7 @@ func usedMountPath(client kubernetes.Interface, namespace string, labels types.L Type: "process,file", }) if err != nil { - // log.Warn().Msgf("Error getting summary data for pod %s, container %s, namespace %s: %s", pod.Name, container.Name, pod.Namespace, err.Error()) + log.Warn().Msgf("Error getting summary data for pod %s, container %s, namespace %s: %s", pod.Name, container.Name, pod.Namespace, err.Error()) break } @@ -213,26 +242,7 @@ func usedMountPath(client kubernetes.Interface, namespace string, labels types.L return sumResponses, fromSource, nil } -func getVolumeMountPaths(client kubernetes.Interface, labels LabelMap) ([]string, error) { - podList, err := client.CoreV1().Pods("").List(context.Background(), metav1.ListOptions{ - LabelSelector: libs.LabelMapToString(labels), - }) - if err != nil { - return nil, fmt.Errorf("failed to get pod list: %v", err) - } - - var mountPaths []string - - for _, pod := range podList.Items { - for _, container := range pod.Spec.Containers { - for _, volumeMount := range container.VolumeMounts { - mountPaths = append(mountPaths, volumeMount.MountPath) - } - } - } - return mountPaths, nil -} - +// Match used mounts paths with actually accessed mount paths func accessedMountPaths(sumResp, mnt []string) ([]string, error) { var matchedMountPaths []string duplicatePaths := make(map[string]bool) @@ -248,11 +258,10 @@ func accessedMountPaths(sumResp, mnt []string) ([]string, error) { return matchedMountPaths, nil } -func GetMountPaths(client kubernetes.Interface, name, namespace string, labels LabelMap) ([]string, error) { - +// Generate crown jewel policy +func getCrownjewelPolicy(client kubernetes.Interface, cname, namespace string, labels LabelMap) error { var policies []types.KnoxSystemPolicy - var mountPaths []string var matchedMountPaths []string var ms types.MatchSpec action := "Allow" @@ -260,7 +269,7 @@ func GetMountPaths(client kubernetes.Interface, name, namespace string, labels L // mount paths being used (from observability) sumResp, fromSrc, _ := usedMountPath(client, namespace, labels) - // total mount paths being used (from k8s cluster) + // all mount paths being used (from k8s cluster) mnt, _ := getVolumeMountPaths(client, labels) // mount paths being used and are present in observability data (accessed mount paths) @@ -269,14 +278,15 @@ func GetMountPaths(client kubernetes.Interface, name, namespace string, labels L // process paths being used and are present in observability data matchedProcessPaths, _ := getProcessList(client, namespace, labels) + // filters to check the namespaces to be ignored nsFilter := config.CurrentCfg.ConfigSysPolicy.NsFilter nsNotFilter := config.CurrentCfg.ConfigSysPolicy.NsNotFilter if len(nsFilter) > 0 { for _, ns := range nsFilter { if strings.Contains(namespace, ns) { - policy := createCrownjewelPolicy(ms, name, namespace, action, labels, mnt, matchedMountPaths, matchedProcessPaths, fromSrc) - + policy := createCrownjewelPolicy(ms, cname, namespace, action, labels, mnt, matchedMountPaths, matchedProcessPaths, fromSrc) + // Check for empty policy if policy.Spec.File.MatchDirectories == nil && policy.Spec.File.MatchPaths == nil && policy.Spec.Process.MatchDirectories == nil && policy.Spec.Process.MatchPaths == nil { continue @@ -287,7 +297,8 @@ func GetMountPaths(client kubernetes.Interface, name, namespace string, labels L } else if len(nsNotFilter) > 0 { for _, notns := range nsNotFilter { if !strings.Contains(namespace, notns) { - policy := createCrownjewelPolicy(ms, name, namespace, action, labels, mnt, matchedMountPaths, matchedProcessPaths, fromSrc) + policy := createCrownjewelPolicy(ms, cname, namespace, action, labels, mnt, matchedMountPaths, matchedProcessPaths, fromSrc) + // Check for empty policy if policy.Spec.File.MatchDirectories == nil && policy.Spec.File.MatchPaths == nil && policy.Spec.Process.MatchDirectories == nil && policy.Spec.Process.MatchPaths == nil { continue @@ -300,26 +311,39 @@ func GetMountPaths(client kubernetes.Interface, name, namespace string, labels L jsonData, err := json.Marshal(policies) if err != nil { log.Error().Msg("Error marshaling" + err.Error()) - return nil, nil + return nil } yamlData, err := yaml.JSONToYAML(jsonData) if err != nil { log.Error().Msg("Error converting JSON to YAML:" + err.Error()) - return nil, nil + return nil } fmt.Println(string(yamlData)) systempolicy.UpdateSysPolicies(policies) - return mountPaths, nil + return nil } -func buildSystemPolicy(name, ns, action string, labels LabelMap, matchDirs []types.KnoxMatchDirectories, matchPaths []types.KnoxMatchPaths) types.KnoxSystemPolicy { +// Build Crown jewel System policy structure +func buildSystemPolicy(cname, ns, action string, labels LabelMap, matchDirs []types.KnoxMatchDirectories, matchPaths []types.KnoxMatchPaths) types.KnoxSystemPolicy { + clustername := config.GetCfgClusterName() + + // expand the labels to be in string format + var combinedLabels []string + for key, value := range labels { + label := fmt.Sprintf("%s=%s", key, value) + combinedLabels = append(combinedLabels, label) + } + labelsString := strings.Join(combinedLabels, ",") + + // create policy name + name := strconv.FormatUint(uint64(common.HashInt(ns+clustername+cname+labelsString)), 10) return types.KnoxSystemPolicy{ APIVersion: "v1", Kind: "KubeArmorPolicy", Metadata: map[string]string{ - "name": "autopol-assets-" + name, + "name": "autopol-sensitive-" + name, "namespace": ns, }, Spec: types.KnoxSystemSpec{ @@ -338,11 +362,13 @@ func buildSystemPolicy(name, ns, action string, labels LabelMap, matchDirs []typ } } -func createCrownjewelPolicy(ms types.MatchSpec, name, namespace, action string, labels LabelMap, matchedDirPts, matchedMountPts, matchedProcessPts []string, fromSrc map[string]string) types.KnoxSystemPolicy { +func createCrownjewelPolicy(ms types.MatchSpec, cname, namespace, action string, labels LabelMap, matchedDirPts, matchedMountPts, matchedProcessPts []string, fromSrc map[string]string) types.KnoxSystemPolicy { var matchDirs []types.KnoxMatchDirectories i := 1 for _, dirpath := range matchedDirPts { action = "Block" + // TODO: handle serviceaccount token access + // ignore serviceaccount token related accesses if strings.Contains(dirpath, "serviceaccount") { continue } @@ -375,7 +401,6 @@ func createCrownjewelPolicy(ms types.MatchSpec, name, namespace, action string, Dir: dirpath + "/", Recursive: true, FromSource: fromSourceVal, - Action: action, } if action == "Allow" { @@ -404,13 +429,12 @@ func createCrownjewelPolicy(ms types.MatchSpec, name, namespace, action string, var matchPaths []types.KnoxMatchPaths for _, processpath := range matchedProcessPts { matchPath := types.KnoxMatchPaths{ - Path: processpath, - Action: "Allow", + Path: processpath, } matchPaths = append(matchPaths, matchPath) } - policy := buildSystemPolicy(name, namespace, action, labels, matchDirs, matchPaths) + policy := buildSystemPolicy(cname, namespace, action, labels, matchDirs, matchPaths) return policy }