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: support for kyverno policies #333

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
earthly 0.8.15
golang 1.22.7
golang 1.23.4
golangci-lint 1.62.2
helm 3.16.3
helm-cr 1.6.1
Expand Down
2 changes: 1 addition & 1 deletion Tiltfile
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ k8s_resource(
port_forwards=['2345:2345', '8080:8080'],
resource_deps=[
# 'go-build',
'go-test',
# 'go-test',
'k8s:namespace',
'argocd',
'argocd-crds',
Expand Down
2 changes: 1 addition & 1 deletion charts/kubechecks/templates/clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ rules:
resources: ['applications', 'appprojects', 'applicationsets', 'services']
verbs: ['get', 'list', 'watch']
- apiGroups: [''] # The core API group, which is indicated by an empty string
resources: ['secrets']
resources: ['secrets', 'configmaps']
verbs: ['get', 'list', 'watch']
5 changes: 5 additions & 0 deletions cmd/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ var ControllerCmd = &cobra.Command{
log.Fatal().Err(err).Msg("failed to process schema locations")
}

log.Info().Strs("locations", cfg.KyvernoPoliciesLocation).Msg("processing kyverno policies locations")
if err = processLocations(ctx, ctr, cfg.KyvernoPoliciesLocation); err != nil {
log.Fatal().Err(err).Msg("failed to process kyverno policies locations")
}

processors, err := getProcessors(ctr)
if err != nil {
log.Fatal().Err(err).Msg("failed to create processors")
Expand Down
9 changes: 9 additions & 0 deletions cmd/processors.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/zapier/kubechecks/pkg/checks/diff"
"github.com/zapier/kubechecks/pkg/checks/hooks"
"github.com/zapier/kubechecks/pkg/checks/kubeconform"
"github.com/zapier/kubechecks/pkg/checks/kyverno"
"github.com/zapier/kubechecks/pkg/checks/preupgrade"
"github.com/zapier/kubechecks/pkg/checks/rego"
"github.com/zapier/kubechecks/pkg/container"
Expand Down Expand Up @@ -57,5 +58,13 @@ func getProcessors(ctr container.Container) ([]checks.ProcessorEntry, error) {
})
}

if ctr.Config.EnableKyvernoChecks {
procs = append(procs, checks.ProcessorEntry{
Name: "running kyverno check",
Processor: kyverno.Check,
WorstState: ctr.Config.WorstPreupgradeState,
})
}

return procs, nil
}
5 changes: 5 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ func init() {
newStringOpts().
withDefault("kubechecks again"))
stringSliceFlag(flags, "additional-apps-namespaces", "Additional namespaces other than the ArgoCDNamespace to monitor for applications.")
boolFlag(flags, "enable-kyverno-checks", "Enable kyverno policy checks.")
stringFlag(flags, "kyverno-policies-location", "Sets kyverno policy locations to be used for every check request. This is a git url in either git or http(s) format.")
stringSliceFlag(flags, "kyverno-policies-paths", "Sets the paths inside the kyverno-policies-location that contains the policies. Default to root of the repository.",
newStringSliceOpts().
withDefault([]string{"."}))

panicIfError(viper.BindPFlags(flags))
setupLogOutput()
Expand Down
266 changes: 219 additions & 47 deletions go.mod

Large diffs are not rendered by default.

721 changes: 616 additions & 105 deletions go.sum

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion localdev/kubechecks/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,20 @@ configMap:
# KUBECHECKS_SCHEMAS_LOCATION: https://github.com/zapier/kubecheck-schemas.git
KUBECHECKS_TIDY_OUTDATED_COMMENTS_MODE: "delete"
KUBECHECKS_ENABLE_CONFTEST: "false"
KUBECHECKS_ENABLE_KYVERNO_CHECKS: "true"
KUBECHECKS_KYVERNO_POLICIES_LOCATION: "https://gitlab.com/zapier/team-sre/service-kyverno.git"
KUBECHECKS_KYVERNO_POLICIES_PATHS: "argocd/production/templates/checks"
KUBECHECKS_ARGOCD_SEND_FULL_REPOSITORY: "true"
KUBECHECKS_ARGOCD_REPOSITORY_ENDPOINT: argocd-repo-server.kubechecks:8081
GRPC_ENFORCE_ALPN_ENABLED: false


deployment:
annotations:
reloader.stakater.com/auto: "true"

image:
pullPolicy: Never
pullPolicy: IfNotPresent
name: "kubechecks"
tag: ""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- ../../base
- ../../base

patchesStrategicMerge:
- replica-patch.yaml
patches:
- path: replica-patch.yaml
12 changes: 12 additions & 0 deletions pkg/checks/kyverno/check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package kyverno

import (
"context"

"github.com/zapier/kubechecks/pkg/checks"
"github.com/zapier/kubechecks/pkg/msg"
)

func Check(ctx context.Context, request checks.Request) (msg.Result, error) {
return kyvernoValidate(ctx, request.Container, request.AppName, request.KubernetesVersion, request.YamlManifests)
}
92 changes: 92 additions & 0 deletions pkg/checks/kyverno/kyverno.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package kyverno

import (
"context"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/rs/zerolog/log"
"github.com/zapier/kubechecks/pkg"
"github.com/zapier/kubechecks/pkg/container"
"github.com/zapier/kubechecks/pkg/msg"
"go.opentelemetry.io/otel"

"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/apply"
)

var tracer = otel.Tracer("pkg/checks/kyverno")

func kyvernoValidate(ctx context.Context, ctr container.Container, appName, targetKubernetesVersion string, appManifests []string) (msg.Result, error) {
_, span := tracer.Start(ctx, "KyvernoValidate")
defer span.End()

log.Debug().Msg("Creating temporary file for app manifests")
tempFile, err := os.CreateTemp("/tmp", "appManifests-*.yaml")
if err != nil {
log.Error().Err(err).Msg("Failed to create temporary file")
return msg.Result{}, err
}
defer os.Remove(tempFile.Name())

log.Debug().Str("tempFile", tempFile.Name()).Msg("Temporary file created")

for _, manifest := range appManifests {
if _, err := tempFile.WriteString(manifest + "\n"); err != nil {
log.Error().Err(err).Msg("Failed to write manifest to temporary file")
return msg.Result{}, err
}
}
Comment on lines +35 to +40
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you need --- to seperate the manifests from each other here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's right! Nice catch.
FYI, this is still in progress. So, it's not fully ready


if err := tempFile.Close(); err != nil {
log.Error().Err(err).Msg("Failed to close temporary file")
return msg.Result{}, err
}

// This calls the kyverno apply -r <RESOURCE_FILE> <POLICY LOCATIONS ...> command
applyCommand := apply.Command()
applyCommand.SetArgs(
append(
getPoliciesLocations(ctr),
[]string{"-r", tempFile.Name()}...))
var output strings.Builder
applyCommand.SetOutput(&output)
if err := applyCommand.Execute(); err != nil {
log.Error().Err(err).Msg("Failed to execute kyverno apply command")
return msg.Result{}, err
}
log.Info().Msg(output.String())

var cr msg.Result
if output.Len() == 0 {
cr.State = pkg.StateWarning
} else {
cr.State = pkg.StateSuccess
}
Comment on lines +61 to +66
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's no output, then it failed? This seems ... strange, no?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is more like a dummy script to just see what happens.

We'll need to parse the output to know if it failed or not.


log.Debug().Str("report", output.String()).Msg("Kyverno validation completed")
cr.Summary = "<b>Show kyverno report:</b>"
cr.Details = fmt.Sprintf(">Kyverno Policy Report \n\n%s", output.String())

log.Debug().Msg("Kyverno validation completed")

return cr, nil
}

func getPoliciesLocations(ctr container.Container) []string {
cfg := ctr.Config

// schemas configured globally
var locations []string

for _, location := range cfg.KyvernoPoliciesLocation {
for _, path := range cfg.KyvernoPoliciesPaths {
locations = append(locations, filepath.Join(location, path))
}
}

log.Debug().Strs("locations", locations).Msg("processed kyverno policies locations")

return locations
}
4 changes: 4 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ type ServerConfig struct {
// -- preupgrade
EnablePreupgrade bool `mapstructure:"enable-preupgrade"`
WorstPreupgradeState pkg.CommitState `mapstructure:"worst-preupgrade-state"`
// -- kyverno
EnableKyvernoChecks bool `mapstructure:"enable-kyverno-checks"`
KyvernoPoliciesLocation []string `mapstructure:"kyverno-policies-location"`
KyvernoPoliciesPaths []string `mapstructure:"kyverno-policies-paths"`
Comment on lines +72 to +75
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also add a WorstKyvernoState pkg.CommitState field, which would allow people to either warn or fail depending on configuration.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah! That'll be added


// misc
AdditionalAppsNamespaces []string `mapstructure:"additional-apps-namespaces"`
Expand Down
Loading