Skip to content

Instantly share code, notes, and snippets.

@flavio
Created July 18, 2024 13:02
Show Gist options
  • Save flavio/4995b1288d27c726c3524a4067ab8715 to your computer and use it in GitHub Desktop.
Save flavio/4995b1288d27c726c3524a4067ab8715 to your computer and use it in GitHub Desktop.
Simplest Kubewarden policy written with Go, to be compiled with tinygo
module github.com/kubewarden/demo
go 1.22
toolchain go1.22.5
require (
github.com/francoispqt/onelog v0.0.0-20190306043706-8c2bb31b10a4
github.com/kubewarden/k8s-objects v1.29.0-kw1
github.com/kubewarden/policy-sdk-go v0.11.0
github.com/wapc/wapc-guest-tinygo v0.3.3
)
require (
github.com/francoispqt/gojay v0.0.0-20181220093123-f2cc13a668ca // indirect
github.com/go-openapi/strfmt v0.21.3 // indirect
)
replace github.com/go-openapi/strfmt => github.com/kubewarden/strfmt v0.1.3
package main
import (
"encoding/json"
"fmt"
"strings"
onelog "github.com/francoispqt/onelog"
corev1 "github.com/kubewarden/k8s-objects/api/core/v1"
kubewarden "github.com/kubewarden/policy-sdk-go"
kubewarden_protocol "github.com/kubewarden/policy-sdk-go/protocol"
wapc "github.com/wapc/wapc-guest-tinygo"
)
const httpBadRequestStatusCode = 400
// This is not a good practice in general. Policy authors should avoid using global variables in the final code
//
//nolint:gochecknoglobals // Allowing global variables just to make the template code simple.
var (
logWriter = kubewarden.KubewardenLogWriter{}
logger = onelog.New(
&logWriter,
onelog.ALL, // shortcut for onelog.DEBUG|onelog.INFO|onelog.WARN|onelog.ERROR|onelog.FATAL
)
)
func main() {
wapc.RegisterFunctions(wapc.Functions{
"validate": validate,
"validate_settings": validateSettings,
})
}
func validate(payload []byte) ([]byte, error) {
// Create a ValidationRequest instance from the incoming payload
validationRequest := kubewarden_protocol.ValidationRequest{}
err := json.Unmarshal(payload, &validationRequest)
if err != nil {
return kubewarden.RejectRequest(
kubewarden.Message(err.Error()),
kubewarden.Code(httpBadRequestStatusCode))
}
// Create a Settings instance from the ValidationRequest object
settings, err := NewSettingsFromValidationReq(&validationRequest)
if err != nil {
return kubewarden.RejectRequest(
kubewarden.Message(err.Error()),
kubewarden.Code(httpBadRequestStatusCode))
}
// Access the **raw** JSON that describes the object
podJSON := validationRequest.Request.Object
// Try to create a Pod instance using the RAW JSON we got from the
// ValidationRequest.
pod := &corev1.Pod{}
if err = json.Unmarshal([]byte(podJSON), pod); err != nil {
return kubewarden.RejectRequest(
kubewarden.Message(
fmt.Sprintf("Cannot decode Pod object: %s", err.Error())),
kubewarden.Code(httpBadRequestStatusCode))
}
logger.DebugWithFields("validating pod object", func(e onelog.Entry) {
e.String("name", pod.Metadata.Name)
e.String("namespace", pod.Metadata.Namespace)
})
if settings.IsNameDenied(pod.Metadata.Name) {
logger.InfoWithFields("rejecting pod object", func(e onelog.Entry) {
e.String("name", pod.Metadata.Name)
e.String("denied_names", strings.Join(settings.DeniedNames, ","))
})
return kubewarden.RejectRequest(
kubewarden.Message(
fmt.Sprintf("The '%s' name is on the deny list", pod.Metadata.Name)),
kubewarden.NoCode)
}
return kubewarden.AcceptRequest()
}
// Settings is the structure that describes the policy settings.
type Settings struct {
DeniedNames []string `json:"denied_names"`
}
// No special checks have to be done.
func (s *Settings) Valid() (bool, error) {
return true, nil
}
func (s *Settings) IsNameDenied(name string) bool {
for _, deniedName := range s.DeniedNames {
if deniedName == name {
return true
}
}
return false
}
func NewSettingsFromValidationReq(validationReq *kubewarden_protocol.ValidationRequest) (Settings, error) {
settings := Settings{}
err := json.Unmarshal(validationReq.Settings, &settings)
return settings, err
}
func validateSettings(payload []byte) ([]byte, error) {
logger.Info("validating settings")
settings := Settings{}
err := json.Unmarshal(payload, &settings)
if err != nil {
return kubewarden.RejectSettings(kubewarden.Message(fmt.Sprintf("Provided settings are not valid: %v", err)))
}
valid, err := settings.Valid()
if err != nil {
return kubewarden.RejectSettings(kubewarden.Message(fmt.Sprintf("Provided settings are not valid: %v", err)))
}
if valid {
return kubewarden.AcceptSettings()
}
logger.Warn("rejecting settings")
return kubewarden.RejectSettings(kubewarden.Message("Provided settings are not valid"))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment