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

Por favor, consulta Introducción a WASI para obtener una visión general del modo de ejecución de WASI.

Marcas la política como raw utilizando el campo policyType en la configuración metadata.yml. Consulta la especificación de metadatos para más información.

Validación

Como ejemplo, puedes escribir una política 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 política utilizando la plantilla de política WASI de go.

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

Deberías declarar un tipo RawValidationRequest personalizado que contenga Request y Settings, en lugar de usar el tipo ValidationRequest proporcionado por el kw_sdk.go:

// 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 defines el tipo Settings y la función validateSettings en el archivo settings.go:

// Settings represents the settings of the policy.
type Settings struct {
    ValidUsers     []string `+json:"validUsers"+`
    ValidActions   []string `+json:"validActions"+`
    ValidResources []string `+json:"validResources"+`
}

func validateSettings(input []byte) []byte {
    var response SettingsValidationResponse

    settings := &Settings{}
    if err := json.Unmarshal(input, &settings); err != nil {
        response = RejectSettings(Message(fmt.Sprintf("cannot unmarshal settings: %v", err)))
    } else {
        response = validateCliSettings(settings)
    }

    responseBytes, err := json.Marshal(&response)
    if err != nil {
        log.Fatalf("can not marshal validation response: %v", err)
    }
    return responseBytes
}

func validateCliSettings(settings *Settings) SettingsValidationResponse {
    if len(settings.ValidUsers) == 0 {
        return RejectSettings(Message(
            "At least one valid user must be specified"))
    }

    if len(settings.ValidActions) == 0 {
        return RejectSettings(Message(
            "At least one valid action must be specified"))
    }

    if len(settings.ValidResources) == 0 {
        return RejectSettings(Message(
            "At least one valid resource must be specified"))
    }

    return AcceptSettings()
}

Finalmente, actualizas la lógica de validación en validate.go:

func validate(input []byte) []byte {
    var validationRequest RawValidationRequest
    validationRequest.Settings = Settings{}
    decoder := json.NewDecoder(strings.NewReader(string(input)))
    decoder.DisallowUnknownFields()
    err := decoder.Decode(&validationRequest)
    if err != nil {
        return marshalValidationResponseOrFail(
            RejectRequest(
                Message(fmt.Sprintf("Error deserializing validation request: %v", err)),
                Code(400)))
    }

    return marshalValidationResponseOrFail(
        validateRequest(validationRequest.Settings, validationRequest.Request))
}

func marshalValidationResponseOrFail(response ValidationResponse) []byte {
    responseBytes, err := json.Marshal(&response)
    if err != nil {
        log.Fatalf("cannot marshal validation response: %v", err)
    }
    return responseBytes
}

func validateRequest(settings Settings, request Request) ValidationResponse {
    if slices.Contains(settings.ValidUsers, request.User) &&
        slices.Contains(settings.ValidActions, request.Action) &&
        slices.Contains(settings.ValidResources, request.Resource) {
        return AcceptRequest()
    }

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

Mutación

Puedes cambiar el ejemplo anterior para mutar la solicitud en lugar de rechazarla.

En este caso, los ajustes deben contener defaultUser, defaultAction y defaultRequest para usarlos en la mutación de 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 represents 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"+`
}

func validateCliSettings(settings *Settings) SettingsValidationResponse {
    if len(settings.ValidUsers) == 0 {
        return RejectSettings(Message(
            "At least one valid user must be specified"))
    }

    if len(settings.ValidActions) == 0 {
        return RejectSettings(Message(
            "At least one valid action must be specified"))
    }

    if len(settings.ValidResources) == 0 {
        return RejectSettings(Message(
            "At least one valid resource must be specified"))
    }

    if settings.DefaultUser == "" {
        return RejectSettings(Message(
            "Default user must be specified"))
    }

    if settings.DefaultAction == "" {
        return RejectSettings(Message(
            "Default action must be specified"))
    }

    if settings.DefaultResource == "" {
        return RejectSettings(Message(
            "Default resource must be specified"))
    }

    return AcceptSettings()
}

También necesitas actualizar la estructura ValidationResponse y la función MutateRequest en kw_sdk.go para eliminar los tipos específicos de Kubernetes y usar tipos Admission Controller en su lugar:

// ValidationResponse defines the response given when validating a request
type ValidationResponse struct {
    Accepted bool `+json:"accepted"+`
    // Optional - ignored if accepted
    Message *string `+json:"message,omitempty"+`
    // Optional - ignored if accepted
    Code *uint16 `+json:"code,omitempty"+`
    // Optional - used only by mutating policies
    MutatedObject *Request `+json:"mutated_object,omitempty"+`
}

// MutateRequest accepts the request. The given `+mutatedObject+` is how
// the evaluated object must look once accepted
func MutateRequest(mutatedObject *Request) ValidationResponse {
    return ValidationResponse{
        Accepted:      true,
        MutatedObject: mutatedObject,
    }
}

Ahora puedes actualizar la función validate para mutar la solicitud si no es válida:

func validateRequest(settings Settings, request Request) ValidationResponse {
    if slices.Contains(settings.ValidUsers, request.User) &&
        slices.Contains(settings.ValidActions, request.Action) &&
        slices.Contains(settings.ValidResources, request.Resource) {
        return AcceptRequest()
    }

    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 MutateRequest(&request)
}