Skip to content

Commit

Permalink
Adds in-mem PolicyReportStore implementation (#107) (#150)
Browse files Browse the repository at this point in the history
* Adds in-mem `PolicyReportStore` implementation (#107)
Part of #107

As specified in the original issue, this commit:
- Create a store Go interface that satisfies the current store implementation at report/store.go
- Refactor current report/store.go into report/store_kubernetes.go, as an implementation of the store interface
- Adds a new memory store with 2 maps, one with `ClusterPolicyReports` and another one with `PolicyReports`
- Provide an optional cli flag, --store TYPE, with possible types memory, kubernetes. Defaults to --store kubernetes for ease on first Kubewarden installations.

Besides that, this commit also:
- Changes `printJSON` to `outputScan` to make it more aligned with what the input params is
- `outputScan` only outputs at the end of the flow, to prevent having a lot of logs whenever someone scans the entire cluster (we were outputting the entire store on each namespace loop)

It also seems that running `make fmt` introduced a couple of unwanted changes that I'm happy to revert to keep this commit smaller. Finally, I also had to do a couple of tweaks to make golangci lint happy.

Signed-off-by: Nuno Nelas <nuno.nelas@icloud.com>

* Review improvements:
- Removes `RetryOnConflict` since we will only need them for Kubernetes backend
- Changes "enums" to lower-case (and updates README accordingly)

Signed-off-by: Nuno Nelas <nuno.nelas@icloud.com>

* Review improvements:
- Removes Update methods from PolicyReportStore interface since there's no current usage for them

Signed-off-by: Nuno Nelas <nuno.nelas@icloud.com>

* Review improvements:
- Removes `mutex` since there's no parallel processing
- Changes logLevels for both `PolicyReportStore` to debug to prevent output of non-relevant information by default. Also, aligns what is logged between `PolicyReport` and `ClusterPolicyReport` and also `MemoryPolicyReportStore` and `KubernetesPolicyReportStore`

Signed-off-by: Nuno Nelas <nuno.nelas@icloud.com>

* Review improvements:
- Removes `require` dependency from unit-tests. Instead, uses if-clauses to validate the returning errors
- Given that I was changing go.mod, I also updated some of our dependencies
- Still struggling with `ireturn`. Sometimes it works and build succeeds, sometimes it doesn't

Signed-off-by: Nuno Nelas <nuno.nelas@icloud.com>

* Review improvements:
- Changes godocs since it doesn't make much sense to have an interface knowing about it's implementations

Signed-off-by: Nuno Nelas <nuno.nelas@icloud.com>

* Review improvements:
- Simplifies cmd/root.go logic, since it should only read cmd flags and thus shouldn't have any business logic. There's still an `outputScan` that should also be moved after clarification of #150 (comment)

Signed-off-by: Nuno Nelas <nuno.nelas@icloud.com>

* Review improvements:
- As discussed in #150 (comment), this commit removes `outputScan` logic from `cmd` and moves it to `scanner.go` where it originally was. This ensures we don't add more business logic into a package that should only handle commands

Signed-off-by: Nuno Nelas <nuno.nelas@icloud.com>

* Review improvements:
- Removes "dumb" comment to try to fix golangci. The solution that @jvanz gave [here](#150 (comment)) worked like a charm!
- Removes unused methods from `PolicyReportStore` interface

Signed-off-by: Nuno Nelas <nuno.nelas@icloud.com>

* Review improvements:
- Simplifies `NewMemoryPolicyReportStore` method signature, by removing `error` which is never used

Signed-off-by: Nuno Nelas <nuno.nelas@icloud.com>

* Review improvements:
- Reverts `wg-policy-prototypes` upgrade since this library is not correctly tagged nor has proper releases, so it's best to separate the upgrade to another PR that we can properly test

Signed-off-by: Nuno Nelas <nuno.nelas@icloud.com>

---------

Signed-off-by: Nuno Nelas <nuno.nelas@icloud.com>
  • Loading branch information
nnelas authored Dec 18, 2023
1 parent 1dc71d9 commit c757364
Show file tree
Hide file tree
Showing 17 changed files with 859 additions and 695 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@
The Audit scanner inspects the resources defined in the cluster and
identifies the ones that are violating Kubewarden policies.

The results of the scan are made available via `PolicyReport` objects. Each Namespace
The results of the scan can be made available via `PolicyReport` objects. Each Namespace
has its own dedicated `PolicyReport`. Cluster-wide resources compliance is available via
the `ClusterPolicyReport` resource.

Instead of relying on `PolicyReport` objects, one can also configure Audit scanner to
save all this information in-memory only, by specifying `--store memory`.

# Deployment

We recommend to rely on the [kubewarden-controller](https://github.com/kubewarden/kubewarden-controller)
Expand Down
102 changes: 54 additions & 48 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package cmd
import (
"fmt"

"github.com/kubewarden/audit-scanner/internal/report"

logconfig "github.com/kubewarden/audit-scanner/internal/log"
"github.com/kubewarden/audit-scanner/internal/policies"
"github.com/kubewarden/audit-scanner/internal/resources"
Expand All @@ -27,63 +29,65 @@ type Scanner interface {
var level logconfig.Level

// print result of scan as JSON to stdout
var printJSON bool
var outputScan bool

// list of namespaces to be skipped from scan
var skippedNs []string

// skip SSL cert validation when connecting to PolicyServers endpoints
var insecureSSL bool

var (
// rootCmd represents the base command when called without any subcommands
rootCmd = &cobra.Command{
Use: "audit-scanner",
Short: "Reports evaluation of existing Kubernetes resources with your already deployed Kubewarden policies",
Long: `Scans resources in your kubernetes cluster with your already deployed Kubewarden policies.
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "audit-scanner",
Short: "Reports evaluation of existing Kubernetes resources with your already deployed Kubewarden policies",
Long: `Scans resources in your kubernetes cluster with your already deployed Kubewarden policies.
Each namespace will have a PolicyReport with the outcome of the scan for resources within this namespace.
There will be a ClusterPolicyReport with results for cluster-wide resources.`,

RunE: func(cmd *cobra.Command, args []string) error {
level.SetZeroLogLevel()
namespace, err := cmd.Flags().GetString("namespace")
if err != nil {
return err
}
kubewardenNamespace, err := cmd.Flags().GetString("kubewarden-namespace")
if err != nil {
return err
}
clusterWide, err := cmd.Flags().GetBool("cluster")
if err != nil {
return err
}
policyServerURL, err := cmd.Flags().GetString("policy-server-url")
if err != nil {
return err
}
caCertFile, err := cmd.Flags().GetString("extra-ca")
if err != nil {
return err
}

policiesFetcher, err := policies.NewFetcher(kubewardenNamespace, skippedNs)
if err != nil {
return err
}
resourcesFetcher, err := resources.NewFetcher(kubewardenNamespace, policyServerURL)
if err != nil {
return err
}
scanner, err := scanner.NewScanner(policiesFetcher, resourcesFetcher, printJSON, insecureSSL, caCertFile)
if err != nil {
return err
}

return startScanner(namespace, clusterWide, scanner)
},
}
)
RunE: func(cmd *cobra.Command, args []string) error {
level.SetZeroLogLevel()
namespace, err := cmd.Flags().GetString("namespace")
if err != nil {
return err
}
kubewardenNamespace, err := cmd.Flags().GetString("kubewarden-namespace")
if err != nil {
return err
}
clusterWide, err := cmd.Flags().GetBool("cluster")
if err != nil {
return err
}
policyServerURL, err := cmd.Flags().GetString("policy-server-url")
if err != nil {
return err
}
caCertFile, err := cmd.Flags().GetString("extra-ca")
if err != nil {
return err
}
storeType, err := cmd.Flags().GetString("store")
if err != nil {
return err
}

policiesFetcher, err := policies.NewFetcher(kubewardenNamespace, skippedNs)
if err != nil {
return err
}
resourcesFetcher, err := resources.NewFetcher(kubewardenNamespace, policyServerURL)
if err != nil {
return err
}
scanner, err := scanner.NewScanner(storeType, policiesFetcher, resourcesFetcher, outputScan, insecureSSL, caCertFile)
if err != nil {
return err
}

return startScanner(namespace, clusterWide, scanner)
},
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
Expand All @@ -96,6 +100,7 @@ func Execute() {
log.Fatal().Err(err).Msg("Error on cmd.Execute()")
}
}

func startScanner(namespace string, clusterWide bool, scanner Scanner) error {
if clusterWide && namespace != "" {
log.Fatal().Msg("Cannot scan cluster wide and only a namespace at the same time")
Expand Down Expand Up @@ -124,8 +129,9 @@ func init() {
rootCmd.Flags().StringP("kubewarden-namespace", "k", defaultKubewardenNamespace, "namespace where the Kubewarden components (e.g. PolicyServer) are installed (required)")
rootCmd.Flags().StringP("policy-server-url", "u", "", "URI to the PolicyServers the Audit Scanner will query. Example: https://localhost:3000. Useful for out-of-cluster debugging")
rootCmd.Flags().VarP(&level, "loglevel", "l", fmt.Sprintf("level of the logs. Supported values are: %v", logconfig.SupportedValues))
rootCmd.Flags().BoolVarP(&printJSON, "output-scan", "o", false, "print result of scan in JSON to stdout")
rootCmd.Flags().BoolVarP(&outputScan, "output-scan", "o", false, "print result of scan in JSON to stdout")
rootCmd.Flags().StringSliceVarP(&skippedNs, "ignore-namespaces", "i", nil, "comma separated list of namespace names to be skipped from scan. This flag can be repeated")
rootCmd.Flags().BoolVar(&insecureSSL, "insecure-ssl", false, "skip SSL cert validation when connecting to PolicyServers endpoints. Useful for development")
rootCmd.Flags().StringP("extra-ca", "f", "", "File path to CA cert in PEM format of PolicyServer endpoints")
rootCmd.Flags().StringP("store", "s", report.KUBERNETES, fmt.Sprintf("PolicyReport store type. Supported values are: %v", report.SupportedTypes))
}
3 changes: 0 additions & 3 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ func TestStartScannerForANamespace(t *testing.T) {
mockScanner := mockScanner{}

err := startScanner(namespace, false, &mockScanner)

if err != nil {
t.Errorf("err should be nil, but got %s", err.Error())
}
Expand All @@ -29,7 +28,6 @@ func TestStartScannerForAllNamespaces(t *testing.T) {

// analogous to passing no flags
err := startScanner("", false, &mockScanner)

if err != nil {
t.Errorf("err should be nil, but got %s", err.Error())
}
Expand All @@ -48,7 +46,6 @@ func TestScanClusterResources(t *testing.T) {
mockScanner := mockScanner{}

err := startScanner("", true, &mockScanner)

if err != nil {
t.Errorf("err should be nil, but got %s", err.Error())
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/kubewarden/kubewarden-controller v1.9.0
github.com/rs/zerolog v1.31.0
github.com/spf13/cobra v1.8.0
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb
k8s.io/api v0.29.0
k8s.io/apimachinery v0.29.0
k8s.io/client-go v0.29.0
Expand Down Expand Up @@ -51,7 +52,6 @@ require (
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.10.0 // indirect
golang.org/x/sys v0.13.0 // indirect
Expand Down
Loading

0 comments on commit c757364

Please sign in to comment.