Skip to content

Commit

Permalink
karmor recommend: handling policy-templates
Browse files Browse the repository at this point in the history
- Updated `policy recommend` with `--update` flag
- Added functions to check for new policy-template releases
- Added functions to generate rules.yaml from metadata.yaml in policy-templates
- Updated policy creation logic will select embeded rules.yaml if it cannot generate rules.yaml from policy-templates
- Updated rules structure to include policy-template policies
- Updated runtime policy generation logic
  - added ability to continue generate policies from rules.yaml even if runtime policy generation fails
- Included `and operation` on preconditions while creating policy
- Updated code to use existing rules.yaml if user doesnt want to downlaod policy-templates

Fixes: #176

Signed-off-by: vishnusomank <[email protected]>
  • Loading branch information
vishnusomank authored and wazir-ahmed committed Sep 23, 2022
1 parent b9fb711 commit ba6ce81
Show file tree
Hide file tree
Showing 13 changed files with 421 additions and 236 deletions.
20 changes: 20 additions & 0 deletions cmd/recommend.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
package cmd

import (
"fmt"

"github.com/kubearmor/kubearmor-client/recommend"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

Expand All @@ -22,9 +25,26 @@ var recommendCmd = &cobra.Command{
return nil
},
}
var updateCmd = &cobra.Command{
Use: "update",
Short: "Updates policy-template cache",
Long: "Updates the local cache of policy-templates ($HOME/.cache/karmor)",
RunE: func(cmd *cobra.Command, args []string) error {

if d, err := recommend.DownloadAndUnzipRelease(); err != nil {
fmt.Println(d)
return err
}
log.WithFields(log.Fields{
"Current Version": recommend.CurrentVersion,
}).Info("policy-templates updated")
return nil
},
}

func init() {
rootCmd.AddCommand(recommendCmd)
recommendCmd.AddCommand(updateCmd)

recommendCmd.Flags().StringSliceVarP(&recommendOptions.Images, "image", "i", []string{}, "Container image list (comma separated)")
recommendCmd.Flags().StringSliceVarP(&recommendOptions.Labels, "labels", "l", []string{}, "User defined labels for policy (comma separated)")
Expand Down
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ require (
sigs.k8s.io/yaml v1.3.0
)

require k8s.io/utils v0.0.0-20220812165043-ad590609e2e5
require (
github.com/cavaliergopher/grab/v3 v3.0.1
github.com/google/go-github v17.0.0+incompatible
k8s.io/utils v0.0.0-20220812165043-ad590609e2e5
)

require (
cloud.google.com/go/compute v1.7.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIHXju0Fr4=
github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYptb8Kl3DFGmsjpq4=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
Expand Down Expand Up @@ -528,6 +530,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-github/v30 v30.1.0 h1:VLDx+UolQICEOKu2m4uAoMti1SxuEBAl7RSEG16L+Oo=
github.com/google/go-github/v30 v30.1.0/go.mod h1:n8jBpHl45a/rlBUtRJMOG4GhNADUQFEufcolZ95JfU8=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
Expand Down
4 changes: 4 additions & 0 deletions recommend/imageHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ func saveImageToTar(imageName string) string {

func checkForSpec(spec string, fl []string) []string {
var matches []string
if !strings.HasSuffix(spec, "*") {
spec = fmt.Sprintf("%s$", spec)
}

re := regexp.MustCompile(spec)
for _, name := range fl {
if re.Match([]byte(name)) {
Expand Down
128 changes: 30 additions & 98 deletions recommend/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,93 +9,24 @@ import (
"path/filepath"
"strings"

"github.com/accuknox/auto-policy-discovery/src/types"
"github.com/clarketm/json"
"github.com/fatih/color"
pol "github.com/kubearmor/KubeArmor/pkg/KubeArmorPolicy/api/security.kubearmor.com/v1"
log "github.com/sirupsen/logrus"
"k8s.io/utils/strings/slices"
"sigs.k8s.io/yaml"
)

func (r *SysRule) convertToKnoxRule() types.KnoxSys {
knoxRule := types.KnoxSys{}
fromSourceArr := []types.KnoxFromSource{}

for _, eachSource := range r.FromSource {
if eachSource != "" {
if strings.HasSuffix(eachSource, "/") {
fromSourceArr = append(fromSourceArr, types.KnoxFromSource{
Dir: eachSource,
})
} else {
fromSourceArr = append(fromSourceArr, types.KnoxFromSource{
Path: eachSource,
})
}
}
}

for _, path := range r.Path {
if strings.HasSuffix(path, "/") {

dirRule := types.KnoxMatchDirectories{
Dir: path,
Recursive: r.Recursive,
FromSource: fromSourceArr,
OwnerOnly: r.OwnerOnly,
}
knoxRule.MatchDirectories = append(knoxRule.MatchDirectories, dirRule)
} else {
pathRule := types.KnoxMatchPaths{
Path: path,
FromSource: fromSourceArr,
OwnerOnly: r.OwnerOnly,
}
knoxRule.MatchPaths = append(knoxRule.MatchPaths, pathRule)
}
}

return knoxRule
}

// networkToKnoxRule function to include KubeArmor network rules
func (r *NetRule) networkToKnoxRule() types.NetworkRule {
knoxNetRule := types.NetworkRule{}
fromSourceArr := []types.KnoxFromSource{}

if r.FromSource != "" {
if strings.HasSuffix(r.FromSource, "/") {
fromSourceArr = append(fromSourceArr, types.KnoxFromSource{
Dir: r.FromSource,
})
} else {
fromSourceArr = append(fromSourceArr, types.KnoxFromSource{
Path: r.FromSource,
})
}
}
for _, protocol := range r.Protocol {

protoRule := types.KnoxMatchProtocols{
Protocol: protocol,
FromSource: fromSourceArr,
}
knoxNetRule.MatchProtocols = append(knoxNetRule.MatchProtocols, protoRule)
func addPolicyRule(policy *pol.KubeArmorPolicy, r pol.KubeArmorPolicySpec) {

if len(r.File.MatchDirectories) != 0 || len(r.File.MatchPaths) != 0 {
policy.Spec.File = r.File
}

return knoxNetRule
}

func addPolicyRule(policy *types.KubeArmorPolicy, r Rules) {
if r.FileRule != nil {
policy.Spec.File = r.FileRule.convertToKnoxRule()
}
if r.ProcessRule != nil {
policy.Spec.Process = r.ProcessRule.convertToKnoxRule()
if len(r.Process.MatchDirectories) != 0 || len(r.Process.MatchPaths) != 0 {
policy.Spec.Process = r.Process
}
if r.NetworkRule != nil {
policy.Spec.Network = r.NetworkRule.networkToKnoxRule()
if len(r.Network.MatchProtocols) != 0 {
policy.Spec.Network = r.Network
}
}

Expand All @@ -110,31 +41,30 @@ func mkPathFromTag(tag string) string {
return r.Replace(tag)
}

func (img *ImageInfo) createPolicy(ms MatchSpec) (types.KubeArmorPolicy, error) {
policy := types.KubeArmorPolicy{
APIVersion: "security.kubearmor.com/v1",
Kind: "KubeArmorPolicy",
Metadata: map[string]string{},
Spec: types.KnoxSystemSpec{
func (img *ImageInfo) createPolicy(ms MatchSpec) (pol.KubeArmorPolicy, error) {
policy := pol.KubeArmorPolicy{
Spec: pol.KubeArmorPolicySpec{
Severity: 1, // by default
Selector: types.Selector{
Selector: pol.SelectorType{
MatchLabels: map[string]string{}},
},
}
policy.APIVersion = "security.kubearmor.com/v1"
policy.Kind = "KubeArmorPolicy"

policy.Metadata["name"] = img.getPolicyName(ms.Name)
policy.ObjectMeta.Name = img.getPolicyName(ms.Name)

if img.Namespace != "" {
policy.Metadata["namespace"] = img.Namespace
policy.ObjectMeta.Namespace = img.Namespace
}

policy.Spec.Action = ms.OnEvent.Action
policy.Spec.Severity = ms.OnEvent.Severity
if ms.OnEvent.Message != "" {
policy.Spec.Message = ms.OnEvent.Message
policy.Spec.Action = ms.Spec.Action
policy.Spec.Severity = ms.Spec.Severity
if ms.Spec.Message != "" {
policy.Spec.Message = ms.Spec.Message
}
if len(ms.OnEvent.Tags) > 0 {
policy.Spec.Tags = ms.OnEvent.Tags
if len(ms.Spec.Tags) > 0 {
policy.Spec.Tags = ms.Spec.Tags
}

if len(img.Labels) > 0 {
Expand All @@ -144,21 +74,24 @@ func (img *ImageInfo) createPolicy(ms MatchSpec) (types.KubeArmorPolicy, error)
policy.Spec.Selector.MatchLabels["kubearmor.io/container.name"] = repotag[0]
}

addPolicyRule(&policy, ms.Rules)
addPolicyRule(&policy, ms.Spec)
return policy, nil
}

func (img *ImageInfo) checkPreconditions(ms MatchSpec) bool {
matches := checkForSpec(filepath.Join(ms.Precondition), img.FileList)
return len(matches) > 0
var matches []string
for _, preCondition := range ms.Precondition {
matches = append(matches, checkForSpec(filepath.Join(preCondition), img.FileList)...)
}
return len(matches) >= len(ms.Precondition)
}

func matchTags(ms MatchSpec) bool {
if len(options.Tags) <= 0 {
return true
}
for _, t := range options.Tags {
if slices.Contains(ms.OnEvent.Tags, t) {
if slices.Contains(ms.Spec.Tags, t) {
return true
}
}
Expand Down Expand Up @@ -214,8 +147,7 @@ func (img *ImageInfo) getPolicyFromImageInfo() {

err = createRuntimePolicy(img)
if err != nil {
log.WithError(err).Error("Failed to create runtime policy")
return
log.Infof("Failed to create runtime policy for %s/%s/%s. err=%s", img.Namespace, img.Deployment, img.Name, err)
}

ms, err = getNextRule(&idx)
Expand Down
51 changes: 13 additions & 38 deletions recommend/policyRules.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,17 @@ import (
"sigs.k8s.io/yaml"

"github.com/fatih/color"
pol "github.com/kubearmor/KubeArmor/pkg/KubeArmorPolicy/api/security.kubearmor.com/v1"
log "github.com/sirupsen/logrus"
)

// MatchSpec spec to match for defining policy
type MatchSpec struct {
Name string `json:"name" yaml:"name"`
Precondition string `json:"precondition" yaml:"precondition"`
Description Description `json:"description" yaml:"description"`
Rules Rules `json:"rules" yaml:"rules"`
OnEvent OnEvent `json:"onEvent" yaml:"onEvent"`
Name string `json:"name" yaml:"name"`
Precondition []string `json:"precondition" yaml:"precondition"`
Description Description `json:"description" yaml:"description"`
Yaml string `json:"yaml" yaml:"yaml"`
Spec pol.KubeArmorPolicySpec `json:"spec,omitempty" yaml:"spec,omitempty"`
}

// Ref for the policy rules
Expand All @@ -37,59 +38,33 @@ type Description struct {
Detailed string `json:"detailed" yaml:"detailed"`
}

// SysRule specifics a file/process rule. Note that if the Path ends in "/" it is considered to be Directory rule
type SysRule struct {
FromSource []string `json:"fromSource" yaml:"fromSource"`
Path []string `json:"path" yaml:"path"`
Recursive bool `json:"recursive" yaml:"recursive"`
OwnerOnly bool `json:"ownerOnly" yaml:"ownerOnly"`
}

// NetRule specifies a KubeArmor network rule.
type NetRule struct {
FromSource string `json:"fromSource" yaml:"fromSource"`
Protocol []string `json:"protocol" yaml:"protocol"`
}

// Rules set of applicable rules. In the future, we might have other types of rules.
type Rules struct {
FileRule *SysRule `json:"fileRule" yaml:"fileRule"`
ProcessRule *SysRule `json:"processRule" yaml:"processRule"`
NetworkRule *NetRule `json:"networkRule" yaml:"networkRule"`
}

// OnEvent the information that is emitted in the telemetry/alert when the matching event is witnessed
type OnEvent struct {
Severity int `json:"severity" yaml:"severity"`
Message string `json:"message" yaml:"message"`
Tags []string `json:"tags" yaml:"tags"`
Action string `json:"action" yaml:"action"`
}

var policyRules []MatchSpec

//go:embed yaml/rules.yaml
var policyRulesYAML []byte

func init() {
policyRulesJSON, err := yaml.YAMLToJSON(policyRulesYAML)
func updateRulesYAML(yamlFile []byte) string {
policyRules = []MatchSpec{}
if len(yamlFile) < 30 {
yamlFile = policyRulesYAML
}
policyRulesJSON, err := yaml.YAMLToJSON(yamlFile)
if err != nil {
color.Red("failed to convert policy rules yaml to json")
log.WithError(err).Fatal("failed to convert policy rules yaml to json")
}

var jsonRaw map[string]json.RawMessage
err = json.Unmarshal(policyRulesJSON, &jsonRaw)
if err != nil {
color.Red("failed to unmarshal policy rules json")
log.WithError(err).Fatal("failed to unmarshal policy rules json")
}

err = json.Unmarshal(jsonRaw["policyRules"], &policyRules)
if err != nil {
color.Red("failed to unmarshal policy rules")
log.WithError(err).Fatal("failed to unmarshal policy rules")
}
return string(jsonRaw["version"])
}

func getNextRule(idx *int) (MatchSpec, error) {
Expand Down
Loading

0 comments on commit ba6ce81

Please sign in to comment.