Este documento ha sido traducido utilizando tecnología de traducción automática. Si bien nos esforzamos por proporcionar traducciones precisas, no ofrecemos garantías sobre la integridad, precisión o confiabilidad del contenido traducido. En caso de discrepancia, la versión original en inglés prevalecerá y constituirá el texto autorizado.

Esta es documentación inédita para Admission Controller 1.34-dev.

Escribiendo directivas en bruto

Las directivas en bruto son directivas que pueden evaluar documentos JSON arbitrarios. Para más información sobre las directivas en bruto, consulta la página de directivas en bruto.

Ejemplos

Los siguientes ejemplos deberían parecerte familiares si completaste la sección de validación de este tutorial.

Recuerda marcar la directiva como raw utilizando el campo policyType en la configuración metadata.yml. Consulta la especificación de metadatos para más información.

metadata.yml con policyType: raw
rules:
- apiGroups: [""]
  apiVersions: ["v1"]
  resources: ["pods"]
  operations: ["CREATE"]
mutating: false
contextAware: false
executionMode: kubewarden-wapc
policyType: raw
# Consider the policy for the background audit scans. Default is true. Note the
# intrinsic limitations of the background audit feature on docs.kubewarden.io;
# If your policy hits any limitations, set to false for the audit feature to
# skip this policy and not generate false positives.
backgroundAudit: true
annotations:
  # kubewarden specific:
  io.kubewarden.policy.title: policy-name
  io.kubewarden.policy.version: 0.1.0  # should match the OCI tag
  io.kubewarden.policy.description: Short description
  io.kubewarden.policy.author: "Author name <author-email@example.com>"
  io.kubewarden.policy.url: https://github.com/yourorg/policy-name
  io.kubewarden.policy.source: https://github.com/yourorg/policy-name
  io.kubewarden.policy.license: Apache-2.0
  # The next two annotations are used in the policy report generated by the
  # Audit scanner. Severity indicates policy check result criticality and
  # Category indicates policy category. See more here at docs.kubewarden.io
  io.kubewarden.policy.severity: medium # one of info, low, medium, high, critical. See docs.
  io.kubewarden.policy.category: Resource validation
  # artifacthub specific: (optional, to release in Artifact Hub)
  io.kubewarden.policy.ociUrl: ghcr.io/yourorg/policies/policy-name # must match release workflow oci-target
  io.artifacthub.displayName: Policy Name
  io.artifacthub.resources: Pod
  io.artifacthub.keywords: pod, cool policy, kubewarden

Validación

Quieres escribir una directiva que acepte una solicitud en el siguiente formato:

{
  "request": {
    "user": "alice",
    "action": "delete",
    "resource": "products"
  }
}

y valida que:

  • user está en la lista de usuarios válidos

  • action está en la lista de acciones válidas

  • resource está en la lista de recursos válidos

Comienza por estructurar la directiva utilizando la plantilla de go directiva template. Asegúrate de que todo esté en su lugar con un make, make test y make e2e-tests.

Primero, define los tipos que representan la carga útil de la solicitud.

Necesitas declarar un tipo personalizado RawValidationRequest (crea un archivo request.go), que contenga las estructuras RawValidationRequest y Settings, en lugar de usar el tipo ValidationRequest proporcionado por el SDK:

// RawValidationRequest represents the request that is sent to the validate function by the Policy Server.
type RawValidationRequest struct {
    Request Request `+json:"request"+`
    // Raw policies can have settings.
    Settings Settings `+json:"settings"+`
}

// Request represents the payload of the request.
type Request struct {
    User     string `+json:"user"+`
    Action   string `+json:"action"+`
    Resource string `+json:"resource"+`
}

Luego necesitas definir el tipo Settings y las funciones Valid y validateSettings en settings.go:

La estructura Settings y las funciones Valid y validateSettings en settings.go.
// Settings represents the settings of the policy.
type Settings struct {
    ValidUsers     []string `+json:"validUsers"+`
    ValidActions   []string `+json:"validActions"+`
    ValidResources []string `+json:"validResources"+`
}

// Valid returns true if the settings are valid.
func (s *Settings) Valid() (bool, error) {
    if len(s.ValidUsers) == 0 {
        return false, fmt.Errorf("validUsers cannot be empty")
    }

    if len(s.ValidActions) == 0 {
        return false, fmt.Errorf("validActions cannot be empty")
    }

    if len(s.ValidResources) == 0 {
        return false, fmt.Errorf("validResources cannot be empty")
    }

    return true, nil
}

// validateSettings validates the settings.
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"))
}

Finalmente, reemplazas la función validate (en validate.go):

La función validate en validate.go.
func validate(payload []byte) ([]byte, error) {
    // Unmarshal the payload into a RawValidationRequest instance
    validationRequest := RawValidationRequest{}
    err := json.Unmarshal(payload, &validationRequest)
    if err != nil {
        // If the payload is not valid, reject the request
        return kubewarden.RejectRequest(
            kubewarden.Message(err.Error()),
            kubewarden.Code(400))
    }

    request := validationRequest.Request
    settings := validationRequest.Settings

    // Validate the payload
    if slices.Contains(settings.ValidUsers, request.User) &&
        slices.Contains(settings.ValidActions, request.Action) &&
        slices.Contains(settings.ValidResources, request.Resource) {
        return kubewarden.AcceptRequest()
    }

    return kubewarden.RejectRequest(
        kubewarden.Message("The request cannot be accepted."),
        kubewarden.Code(400))
}

Puedes configurar una prueba en e2e.bats:

e2e.bats
#!/usr/bin/env bats

@test "accept" {
  run kwctl run annotated-policy.wasm -r test_data/request.json -s test_data/settings.json

  # this prints the output when one the checks below fails
  echo "output = ${output}"

  # request allowed
  [ "$status" -eq 0 ]
  [ $(expr "$output" : '.*allowed.*true') -ne 0 ]
}

Luego, las salidas de make, make test y make e2e son:

Salidas
make && make test && make e2e-tests
docker run \
    --rm \
    -e GOFLAGS="-buildvcs=false" \
    -v /home/jhk/projects/suse/tmp/fab-goraw:/src \
    -w /src tinygo/tinygo:0.30.0 \
    tinygo build -o policy.wasm -target=wasi -no-debug .
go test -v
=== RUN   TestAcceptValidSettings
--- PASS: TestAcceptValidSettings (0.00s)
=== RUN   TestRejectSettingsWithEmptyValidUsers
--- PASS: TestRejectSettingsWithEmptyValidUsers (0.00s)
=== RUN   TestRejectSettingsWithEmptyValidActions
--- PASS: TestRejectSettingsWithEmptyValidActions (0.00s)
=== RUN   TestRejectSettingsWithEmptyValidResources
--- PASS: TestRejectSettingsWithEmptyValidResources (0.00s)
=== RUN   TestValidateRequestAccept
--- PASS: TestValidateRequestAccept (0.00s)
=== RUN   TestValidateRequestReject
--- PASS: TestValidateRequestReject (0.00s)
PASS
ok      github.com/kubewarden/go-policy-template    0.002s
kwctl annotate -m metadata.yml -u README.md -o annotated-policy.wasm policy.wasm
bats e2e.bats
e2e.bats
 ✓ accept

1 test, 0 failures

Mutación

Necesitas cambiar el ejemplo anterior para mutar la solicitud en lugar de rechazarla. Los ajustes deben contener defaultUser, defaultAction y defaultRequest para usar para mutar la solicitud si el usuario, la acción o el recurso no son válidos.

Necesitas actualizar el tipo Settings con los nuevos campos:

// Settings defines the settings of the policy.
type Settings struct {
    ValidUsers      []string `+json:"validUsers"+`
    ValidActions    []string `+json:"validActions"+`
    ValidResources  []string `+json:"validResources"+`
    DefaultUser     string   `+json:"defaultUser"+`
    DefaultAction   string   `+json:"defaultAction"+`
    DefaultResource string   `+json:"defaultResource"+`
}

// Valid returns true if the settings are valid.
func (s *Settings) Valid() (bool, error) {
    if len(s.ValidUsers) == 0 {
        return false, fmt.Errorf("validUsers cannot be empty")
    }

    if len(s.ValidActions) == 0 {
        return false, fmt.Errorf("validActions cannot be empty")
    }

    if len(s.ValidResources) == 0 {
        return false, fmt.Errorf("validResources cannot be empty")
    }

    if s.DefaultUser == "" {
        return false, fmt.Errorf("defaultUser cannot be empty")
    }

    if s.DefaultAction == "" {
        return false, fmt.Errorf("defaultUser cannot be empty")
    }

    if s.DefaultResource == "" {
        return false, fmt.Errorf("defaultResource cannot be empty")
    }

    return true, nil
}

Además, la función validate para introducir la mutación:

func validate(payload []byte) ([]byte, error) {
    // Unmarshal the payload into a RawValidationRequest instance
    validationRequest := RawValidationRequest{}
    err := json.Unmarshal(payload, &validationRequest)
    if err != nil {
        // If the payload is not valid, reject the request
        return kubewarden.RejectRequest(
            kubewarden.Message(err.Error()),
            kubewarden.Code(400))
    }

    request := validationRequest.Request
    settings := validationRequest.Settings

    logger.Info("validating request")

    // Accept the request without mutating it if it is valid
    if slices.Contains(settings.ValidUsers, request.User) &&
        slices.Contains(settings.ValidActions, request.Action) &&
        slices.Contains(settings.ValidResources, request.Resource) {
        return kubewarden.AcceptRequest()
    }

    logger.Info("mutating request")

    // Mutate the request if it is not valid
    if !slices.Contains(settings.ValidUsers, request.User) {
        request.User = settings.DefaultUser
    }

    if !slices.Contains(settings.ValidActions, request.Action) {
        request.Action = settings.DefaultAction
    }

    if !slices.Contains(settings.ValidResources, request.Resource) {
        request.Resource = settings.DefaultResource
    }

    return kubewarden.MutateRequest(request)
}