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

feat: (recommend) Implement recommend functionality for Docker Client #461

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
88334c6
cmd: rename k8s client to k8sclient
tesla59 Sep 15, 2024
c739a95
cmd: wrap k8sClient in interface to use in recommend package
tesla59 Sep 16, 2024
0feed1a
k8s: fix ListDeployment method
tesla59 Sep 16, 2024
ddb361a
recommend: refactor k8s policy generation
tesla59 Sep 22, 2024
2819a3f
recommend: use image name as Object's name while generating policy
tesla59 Sep 25, 2024
5474f33
recommend: remove DeploymentName Field from Object{}
tesla59 Sep 25, 2024
a95b469
recommend: use ListOptions to select deployment by labels
tesla59 Sep 26, 2024
82d6666
k8s: generate recommended policies for CronJob, DaemonSet and Jobs
tesla59 Sep 29, 2024
993274e
k8s: generate recommend policy for statefulset and unowned replicaset
tesla59 Sep 29, 2024
2209139
k8s: remove K8sClientWrapper abstraction
tesla59 Sep 29, 2024
c759cd7
recommend: initialize dockerClient and generate policies for containe…
tesla59 Sep 29, 2024
dd2788a
recommend: add new flag k8s to specify which client to use
tesla59 Oct 2, 2024
d52989f
recommend: fallback to docker client if k8s client is not present
tesla59 Oct 14, 2024
f7e473f
recommend: only log the client if images is not specified
tesla59 Oct 22, 2024
e98124d
recommend: generate policyDir based on image namespace set to null
tesla59 Nov 19, 2024
e5d621a
recommend: trim \n in final report generation
tesla59 Nov 19, 2024
b720655
Merge remote-tracking branch 'origin/main' into tesla/non-k8s/karmor-…
tesla59 Jan 8, 2025
541d5de
recommend: handle err when listing objects
tesla59 Jan 8, 2025
25c981c
Merge remote-tracking branch 'origin/main' into tesla/non-k8s/karmor-…
tesla59 Jan 19, 2025
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
4 changes: 2 additions & 2 deletions cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ var installCmd = &cobra.Command{
if err := installOptions.Env.CheckAndSetValidEnvironmentOption(cmd.Flag("env").Value.String()); err != nil {
return fmt.Errorf("error in checking environment option: %v", err)
}
if err := install.K8sLegacyInstaller(client, installOptions); err != nil {
if err := install.K8sLegacyInstaller(k8sClient, installOptions); err != nil {
return fmt.Errorf("error installing kubearmor in legacy mode: %v", err)
}
} else {
if err := install.K8sInstaller(client, installOptions); err != nil {
if err := install.K8sInstaller(k8sClient, installOptions); err != nil {
return fmt.Errorf("error installing kubearmor: %v", err)
}
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ var logCmd = &cobra.Command{
Long: `Observe Logs from KubeArmor`,
RunE: func(cmd *cobra.Command, args []string) error {
log.StopChan = make(chan struct{})
return log.StartObserver(client, logOptions)
return log.StartObserver(k8sClient, logOptions)
},
}

Expand Down
4 changes: 1 addition & 3 deletions cmd/probe.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,8 @@ and what KubeArmor features will be supported e.g: observability, enforcement, e
If KubeArmor is running, It probes which environment KubeArmor is running on (e.g: systemd mode, kubernetes etc.),
the supported KubeArmor features in the environment, the pods being handled by KubeArmor and the policies running on each of these pods`,
RunE: func(cmd *cobra.Command, args []string) error {

err := probe.PrintProbeResult(client, probeInstallOptions)
err := probe.PrintProbeResult(k8sClient, probeInstallOptions)
return err

},
}

Expand Down
19 changes: 16 additions & 3 deletions cmd/recommend.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
package cmd

import (
"context"
"github.com/kubearmor/kubearmor-client/recommend"
"github.com/kubearmor/kubearmor-client/recommend/common"
genericpolicies "github.com/kubearmor/kubearmor-client/recommend/engines/generic_policies"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var recommendOptions common.Options
Expand All @@ -19,16 +21,26 @@ var recommendCmd = &cobra.Command{
Short: "Recommend Policies",
Long: `Recommend policies based on container image, k8s manifest or the actual runtime env`,
RunE: func(cmd *cobra.Command, args []string) error {
err := recommend.Recommend(client, recommendOptions, genericpolicies.GenericPolicy{})
return err
if recommendOptions.K8s {
// Check if k8sClient can connect to the server by listing namespaces
_, err := k8sClient.K8sClientset.CoreV1().Namespaces().List(context.Background(), v1.ListOptions{})
if err != nil {
if len(recommendOptions.Images) == 0 { // only log the client if no images are provided
log.Error("K8s client is not initialized, using docker client instead")
}
return recommend.Recommend(dockerClient, recommendOptions, genericpolicies.GenericPolicy{})
}
return recommend.Recommend(k8sClient, recommendOptions, genericpolicies.GenericPolicy{})
} else {
return recommend.Recommend(dockerClient, recommendOptions, genericpolicies.GenericPolicy{})
}
},
}
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 _, err := genericpolicies.DownloadAndUnzipRelease(); err != nil {
return err
}
Expand All @@ -50,4 +62,5 @@ func init() {
recommendCmd.Flags().StringVarP(&recommendOptions.ReportFile, "report", "r", "report.txt", "report file")
recommendCmd.Flags().StringSliceVarP(&recommendOptions.Tags, "tag", "t", []string{}, "tags (comma-separated) to apply. Eg. PCI-DSS, MITRE")
recommendCmd.Flags().StringVarP(&recommendOptions.Config, "config", "c", common.UserHome()+"/.docker/config.json", "absolute path to image registry configuration file")
recommendCmd.Flags().BoolVarP(&recommendOptions.K8s, "k8s", "k", true, "Use k8s client instead of docker client")
}
14 changes: 11 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,33 @@
package cmd

import (
"github.com/kubearmor/kubearmor-client/docker"
"github.com/kubearmor/kubearmor-client/k8s"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)

var client *k8s.Client
var k8sClient *k8s.Client
var dockerClient *docker.Client

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
var err error

//Initialise k8sClient for all child commands to inherit
client, err = k8s.ConnectK8sClient()
// fmt.Printf("%v", client.K8sClientset)
k8sClient, err = k8s.ConnectK8sClient()
if err != nil {
log.Error().Msgf("unable to create Kubernetes clients: %s", err.Error())
return err
}

// Initialise dockerClient for all child commands to inherit
dockerClient, err = docker.ConnectDockerClient()
if err != nil {
log.Error().Msgf("unable to create Docker clients: %s", err.Error())
return err
}
return nil
},
Use: "karmor",
Expand Down
3 changes: 1 addition & 2 deletions cmd/rotate-tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ var rotateCmd = &cobra.Command{
Short: "Rotate webhook controller tls certificates",
Long: `Rotate webhook controller tls certificates`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := rotatetls.RotateTLS(client, namespace); err != nil {
if err := rotatetls.RotateTLS(k8sClient, namespace); err != nil {
return err
}
return nil
Expand All @@ -20,6 +20,5 @@ var rotateCmd = &cobra.Command{

func init() {
rootCmd.AddCommand(rotateCmd)

rotateCmd.Flags().StringVarP(&namespace, "namespace", "n", "kubearmor", "Namespace for resources")
}
2 changes: 1 addition & 1 deletion cmd/selfupdate.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var selfUpdateCmd = &cobra.Command{
Short: "selfupdate this cli tool",
Long: `selfupdate this cli tool for checking the latest release on the github`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := selfupdate.SelfUpdate(client); err != nil {
if err := selfupdate.SelfUpdate(k8sClient); err != nil {
return err
}
return nil
Expand Down
2 changes: 1 addition & 1 deletion cmd/sysdump.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var sysdumpCmd = &cobra.Command{
Short: "Collect system dump information for troubleshooting and error report",
Long: `Collect system dump information for troubleshooting and error reports`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := sysdump.Collect(client, dumpOptions); err != nil {
if err := sysdump.Collect(k8sClient, dumpOptions); err != nil {
return err
}
return nil
Expand Down
4 changes: 2 additions & 2 deletions cmd/uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ var uninstallCmd = &cobra.Command{
Short: "Uninstall KubeArmor from a Kubernetes Cluster",
Long: `Uninstall KubeArmor from a Kubernetes Clusters`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := install.K8sUninstaller(client, uninstallOptions); err != nil {
if err := install.K8sLegacyUninstaller(client, uninstallOptions); err != nil {
if err := install.K8sUninstaller(k8sClient, uninstallOptions); err != nil {
if err := install.K8sLegacyUninstaller(k8sClient, uninstallOptions); err != nil {
return err
}
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var versionCmd = &cobra.Command{
Short: "Display version information",
Long: `Display version information`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := version.PrintVersion(client); err != nil {
if err := version.PrintVersion(k8sClient); err != nil {
return err
}
return nil
Expand Down
3 changes: 1 addition & 2 deletions cmd/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ var vmScriptCmd = &cobra.Command{
Long: `download vm installation script for kvms control plane`,
RunE: func(cmd *cobra.Command, args []string) error {
ip := HTTPIP

if err := vm.GetScript(client, scriptOptions, ip, IsKvmsEnv); err != nil {
if err := vm.GetScript(k8sClient, scriptOptions, ip, IsKvmsEnv); err != nil {
return err
}
return nil
Expand Down
40 changes: 40 additions & 0 deletions docker/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package docker

import (
"context"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
"github.com/kubearmor/kubearmor-client/recommend/common"
"strings"
)

type Client struct {
*client.Client
}

func ConnectDockerClient() (*Client, error) {
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
return nil, err
}
return &Client{cli}, nil
}

func (c *Client) ListObjects(o common.Options) ([]common.Object, error) {
var result []common.Object
containers, err := c.Client.ContainerList(context.Background(), container.ListOptions{
Filters: filters.NewArgs(),
})
if err != nil {
return nil, err
}
for _, ctr := range containers {
result = append(result, common.Object{
Name: strings.TrimPrefix(ctr.Names[0], "/"),
Images: []string{ctr.Image},
Labels: ctr.Labels,
})
}
return result, nil
}
161 changes: 160 additions & 1 deletion k8s/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package k8s

import (
"context"

"github.com/kubearmor/kubearmor-client/recommend/common"
"github.com/rs/zerolog/log"
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -102,3 +102,162 @@ func GetKubeArmorCaSecret(client kubernetes.Interface) (string, string) {
}
return secret.Items[0].Name, secret.Items[0].Namespace
}

func (k *Client) ListObjects(o common.Options) ([]common.Object, error) {
labelSelector := v1.FormatLabelSelector(&v1.LabelSelector{MatchLabels: common.LabelArrayToLabelMap(o.Labels)})
if labelSelector == "<none>" {
labelSelector = ""
}
// CronJobs
cronJobs, err := k.K8sClientset.BatchV1().CronJobs(o.Namespace).List(context.Background(), v1.ListOptions{
LabelSelector: labelSelector,
})
if err != nil {
log.Error().Msgf("error listing cronjobs: %v", err)
return nil, err
}

// DaemonSets
daemonSets, err := k.K8sClientset.AppsV1().DaemonSets(o.Namespace).List(context.Background(), v1.ListOptions{
LabelSelector: labelSelector,
})
if err != nil {
log.Error().Msgf("error listing daemonsets: %v", err)
return nil, err
}

// Deployments
deployments, err := k.K8sClientset.AppsV1().Deployments(o.Namespace).List(context.Background(), v1.ListOptions{
LabelSelector: labelSelector,
})
if err != nil {
log.Error().Msgf("error listing deployments: %v", err)
return nil, err
}

// Jobs
jobs, err := k.K8sClientset.BatchV1().Jobs(o.Namespace).List(context.Background(), v1.ListOptions{
LabelSelector: labelSelector,
})
if err != nil {
log.Error().Msgf("error listing jobs: %v", err)
return nil, err
}

// ReplicaSets
replicaSets, err := k.K8sClientset.AppsV1().ReplicaSets(o.Namespace).List(context.Background(), v1.ListOptions{
LabelSelector: labelSelector,
})
if err != nil {
log.Error().Msgf("error listing replicasets: %v", err)
return nil, err
}

// StatefulSets
statefulSets, err := k.K8sClientset.AppsV1().StatefulSets(o.Namespace).List(context.Background(), v1.ListOptions{
LabelSelector: labelSelector,
})
if err != nil {
log.Error().Msgf("error listing statefulsets: %v", err)
return nil, err
}

var result []common.Object

for _, cj := range cronJobs.Items {
var images []string
for _, container := range cj.Spec.JobTemplate.Spec.Template.Spec.Containers {
images = append(images, container.Image)
}

result = append(result, common.Object{
Name: cj.Name,
Namespace: cj.Namespace,
Labels: cj.Spec.JobTemplate.Spec.Template.Labels,
Images: images,
})
}

for _, ds := range daemonSets.Items {
var images []string
for _, container := range ds.Spec.Template.Spec.Containers {
images = append(images, container.Image)
}

result = append(result, common.Object{
Name: ds.Name,
Namespace: ds.Namespace,
Labels: ds.Spec.Template.Labels,
Images: images,
})
}

for _, dp := range deployments.Items {
var images []string
for _, container := range dp.Spec.Template.Spec.Containers {
images = append(images, container.Image)
}

result = append(result, common.Object{
Name: dp.Name,
Namespace: dp.Namespace,
Labels: dp.Spec.Template.Labels,
Images: images,
})
}

for _, j := range jobs.Items {
var images []string
for _, container := range j.Spec.Template.Spec.Containers {
images = append(images, container.Image)
}

result = append(result, common.Object{
Name: j.Name,
Namespace: j.Namespace,
Labels: j.Spec.Template.Labels,
Images: images,
})
}

for _, rs := range replicaSets.Items {
isOwned := false
for _, owner := range rs.OwnerReferences {
if owner.Kind == "Deployment" || owner.Kind == "StatefulSet" || owner.Kind == "DaemonSet" || owner.Kind == "ReplicaSet" {
isOwned = true
break
}
}
if isOwned {
continue
}

var images []string
for _, container := range rs.Spec.Template.Spec.Containers {
images = append(images, container.Image)
}
result = append(result, common.Object{
Name: rs.Name,
Namespace: rs.Namespace,
Labels: rs.Spec.Template.Labels,
Images: images,
})
}

for _, sts := range statefulSets.Items {
var images []string
for _, container := range sts.Spec.Template.Spec.Containers {
images = append(images, container.Image)
}

result = append(result, common.Object{
Name: sts.Name,
Namespace: sts.Namespace,
Labels: sts.Spec.Template.Labels,
Images: images,
})
}

log.Printf("+%v", result)
return result, nil
}
Loading