Skip to content

Commit

Permalink
Support custom constraint (templates)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrueg committed Nov 26, 2024
1 parent 67e69cd commit 0919e1d
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 24 deletions.
11 changes: 10 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/plexsystems/konstraint
go 1.23.0

require (
github.com/Masterminds/sprig/v3 v3.3.0
github.com/open-policy-agent/frameworks/constraint v0.0.0-20220218180203-c2a0d8cdf85a
github.com/open-policy-agent/opa v0.69.0
github.com/sirupsen/logrus v1.9.3
Expand All @@ -15,6 +16,9 @@ require (
)

require (
dario.cat/mergo v1.0.1 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
Expand All @@ -38,14 +42,17 @@ require (
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
Expand All @@ -57,16 +64,18 @@ require (
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/oauth2 v0.22.0 // indirect
Expand Down
22 changes: 20 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
Expand Down Expand Up @@ -90,6 +98,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
Expand All @@ -115,8 +125,12 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
Expand Down Expand Up @@ -160,14 +174,16 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
Expand Down Expand Up @@ -234,6 +250,8 @@ go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
Expand Down
20 changes: 20 additions & 0 deletions internal/commands/constraint_template.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: {{ .Kind }}
metadata:
{{- if .Annotations }}
annotations: {{- range $key, $value := .Annotations }}
{{ $key }}: {{ $value }}
{{ end -}}
{{ end -}}
{{- if .Labels }}
labels: {{- range $key, $value := .Labels }}
{{ $key }}: {{ $value }}
{{ end -}}
{{ end -}}
name: {{ .Name }}
spec:
match:
kinds:
{{- if ne .Enforcement "deny" }}
enforcementAction: {{ .Enforcement }}
{{- end -}}
15 changes: 15 additions & 0 deletions internal/commands/constrainttemplate_template.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: {{ .Name }}
spec:
crd:
spec:
names:
kind: {{ .Kind }}
targets:
- libs: {{- range .Dependencies }}
- |- {{- . | nindent 6 -}}
{{ end }}
rego: |- {{- .Source | nindent 6 }}
target: admission.k8s.gatekeeper.sh
97 changes: 77 additions & 20 deletions internal/commands/create.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package commands

import (
"bytes"
"encoding/json"
"fmt"
"os"
"path/filepath"
"text/template"

"github.com/plexsystems/konstraint/internal/rego"

"github.com/Masterminds/sprig/v3"
v1 "github.com/open-policy-agent/frameworks/constraint/pkg/apis/templates/v1"
"github.com/open-policy-agent/frameworks/constraint/pkg/apis/templates/v1beta1"
log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -50,10 +53,21 @@ Create constraints with the Gatekeeper enforcement action set to dryrun
if err := viper.BindPFlag("constraint-template-version", cmd.PersistentFlags().Lookup("constraint-template-version")); err != nil {
return fmt.Errorf("bind constraint-template-version flag: %w", err)
}
if err := viper.BindPFlag("constraint-template-custom-template-file", cmd.PersistentFlags().Lookup("constraint-template-custom-template-file")); err != nil {
return fmt.Errorf("bind constraint-template-custom-template-file flag: %w", err)
}
if err := viper.BindPFlag("constraint-custom-template-file", cmd.PersistentFlags().Lookup("constraint-custom-template-file")); err != nil {
return fmt.Errorf("bind constraint-custom-template-file flag: %w", err)
}

if err := viper.BindPFlag("partial-constraints", cmd.PersistentFlags().Lookup("partial-constraints")); err != nil {
return fmt.Errorf("bind partial-constraints flag: %w", err)
}

if cmd.PersistentFlags().Lookup("constraint-template-custom-template-file").Changed && cmd.PersistentFlags().Lookup("constraint-template-version").Changed {
return fmt.Errorf("need to set either constraint-template-custom-template-file or constraint-template-version")
}

path := "."
if len(args) > 0 {
path = args[0]
Expand All @@ -68,6 +82,8 @@ Create constraints with the Gatekeeper enforcement action set to dryrun
cmd.PersistentFlags().Bool("skip-constraints", false, "Skip generation of constraints")
cmd.PersistentFlags().String("constraint-template-version", "v1beta1", "Set the version of ConstraintTemplates")
cmd.PersistentFlags().Bool("partial-constraints", false, "Generate partial Constraints for policies with parameters")
cmd.PersistentFlags().String("constraint-template-custom-template-file", "", "Path to a custom template file to generate constraint templates")
cmd.PersistentFlags().String("constraint-custom-template-file", "", "Path to a custom template file to generate constraints")

return &cmd
}
Expand Down Expand Up @@ -107,21 +123,36 @@ func runCreateCommand(path string) error {
}

constraintTemplateVersion := viper.GetString("constraint-template-version")
constraintTemplateCustomTemplateFile := viper.GetString("constraint-template-custom-template-file")

var constraintTemplate any
switch constraintTemplateVersion {
case "v1":
constraintTemplate = getConstraintTemplatev1(violation, logger)
case "v1beta1":
constraintTemplate = getConstraintTemplatev1beta1(violation, logger)
default:
return fmt.Errorf("unsupported API version for constrainttemplate: %s", constraintTemplateVersion)
}
var constraintTemplateBytes []byte

constraintTemplateBytes, err := yaml.Marshal(constraintTemplate)
if err != nil {
return fmt.Errorf("marshal constrainttemplate: %w", err)
}
if constraintTemplateCustomTemplateFile != "" {
customTemplate, err := os.ReadFile(constraintTemplateCustomTemplateFile)
if err != nil {
return fmt.Errorf("unable to open/read template file: %w", err)
}
constraintTemplateBytes, err = renderTemplate(violation, customTemplate, logger)
if err != nil {
return fmt.Errorf("unable to render custom template: %w", err)
}
} else {
switch constraintTemplateVersion {
case "v1":
constraintTemplate = getConstraintTemplatev1(violation, logger)
case "v1beta1":
constraintTemplate = getConstraintTemplatev1beta1(violation, logger)
default:
return fmt.Errorf("unsupported API version for constrainttemplate: %s", constraintTemplateVersion)
}
constraintTemplateBytes, err = yaml.Marshal(constraintTemplate)

if err != nil {
return fmt.Errorf("marshal constrainttemplate: %w", err)
}

}
if err := os.WriteFile(filepath.Join(outputDir, templateFileName), constraintTemplateBytes, 0644); err != nil {
return fmt.Errorf("writing template: %w", err)
}
Expand All @@ -137,16 +168,28 @@ func runCreateCommand(path string) error {
continue
}

constraint, err := getConstraint(violation, logger)
if err != nil {
return fmt.Errorf("get constraint: %w", err)
}
constraintCustomTemplateFile := viper.GetString("constraint-custom-template-file")
var constraintBytes []byte
if constraintCustomTemplateFile != "" {
customTemplate, err := os.ReadFile(constraintCustomTemplateFile)
if err != nil {
return fmt.Errorf("unable to open/read template file: %w", err)
}
constraintBytes, err = renderTemplate(violation, customTemplate, logger)
if err != nil {
return fmt.Errorf("unable to render custom constraint: %w", err)
}
} else {
constraint, err := getConstraint(violation, logger)
if err != nil {
return fmt.Errorf("get constraint: %w", err)
}

constraintBytes, err := yaml.Marshal(constraint)
if err != nil {
return fmt.Errorf("marshal constraint: %w", err)
constraintBytes, err = yaml.Marshal(constraint)
if err != nil {
return fmt.Errorf("marshal constraint: %w", err)
}
}

if err := os.WriteFile(filepath.Join(outputDir, constraintFileName), constraintBytes, 0644); err != nil {
return fmt.Errorf("writing constraint: %w", err)
}
Expand All @@ -157,6 +200,20 @@ func runCreateCommand(path string) error {
return nil
}

func renderTemplate(violation rego.Rego, appliedTemplate []byte, _ *log.Entry) ([]byte, error) {
t, err := template.New("template").Funcs(sprig.FuncMap()).Parse(string(appliedTemplate))
if err != nil {
return nil, fmt.Errorf("parsing template: %w", err)
}
buf := new(bytes.Buffer)

if err := t.Execute(buf, violation); err != nil {
return nil, fmt.Errorf("executing template: %w", err)
}

return buf.Bytes(), nil
}

func getConstraintTemplatev1(violation rego.Rego, logger *log.Entry) *v1.ConstraintTemplate {
constraintTemplate := v1.ConstraintTemplate{
TypeMeta: metav1.TypeMeta{
Expand Down
3 changes: 2 additions & 1 deletion internal/commands/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/plexsystems/konstraint/internal/rego"

"github.com/Masterminds/sprig/v3"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand Down Expand Up @@ -111,7 +112,7 @@ func runDocCommand(path string) error {
appliedTemplate = string(b)
}

t, err := template.New("docs").Parse(appliedTemplate)
t, err := template.New("docs").Funcs(sprig.FuncMap()).Parse(appliedTemplate)

if err != nil {
return fmt.Errorf("parsing template: %w", err)
Expand Down

0 comments on commit 0919e1d

Please sign in to comment.