Como escrever um modelo de restrição

Este tópico mostra como gravar um modelo de restrição customizado e usá-lo para estender o Policy Controller.

Visão geral

Modelos de restrição podem ser usados para estender o Policy Controller. No caso em que você não consegue encontrar um modelo pré-escrito que atenda às suas necessidades, é possível escrever seu próprio.

As políticas do Policy Controller são descritas usando o OPA Constraint Framework e escritas em Rego. Uma política pode avaliar qualquer campo de um objeto Kubernetes.

Escrever políticas usando o Rego é uma habilidade especializada. Por esse motivo, uma biblioteca de modelos de restrição comuns é instalada por padrão. A maioria dos usuários pode chamar esses modelos de restrição ao criar restrições. Se você tiver necessidades especializadas, poderá criar seus próprios modelos de restrição.

Os modelos de restrição permitem separar a lógica de uma política dos requisitos específicos, para reutilização e delegação. Você pode criar restrições usando modelos de restrição desenvolvidos por terceiros, como projetos de código aberto, fornecedores de software ou especialistas em regulamentação.

Antes de começar

Modelo de restrição de exemplo

Abaixo está um modelo de restrição de exemplo que nega todos os recursos com o nome que corresponde a um valor fornecido pelo criador da restrição. O restante desta página discutirá o conteúdo do modelo, destacando conceitos importantes ao longo do caminho.

Modelo de restrição

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8sdenyname
spec:
  crd:
    spec:
      names:
        kind: K8sDenyName
      validation:
        # Schema for the `parameters` field
        openAPIV3Schema:
          properties:
            invalidName:
              type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sdenynames
        violation[{"msg": msg}] {
          input.review.object.metadata.name == input.parameters.invalidName
          msg := sprintf("The name %v is not allowed", [input.parameters.invalidName])
        }

Restrição

Aqui está um exemplo de restrição que um usuário pode implementar para negar todos os recursos chamados "violação de política":

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDenyName
metadata:
  name: no-policy-violation
spec:
  parameters:
    invalidName: "policy-violation"

Partes de um modelo de restrição

Os modelos de restrição têm duas partes importantes:

  • O esquema da restrição que você quer que os usuários criem. O esquema de um modelo de restrição é armazenado no campo crd.

  • O código-fonte Rego que é executado quando a restrição é avaliada. O código fonte Rego para um modelo é armazenado no campo targets.

O campo CRD

O campo CRD é um modelo para criar a Definição de Recurso Personalizado do Kubernetes que define o recurso de restrição para o servidor da API do Kubernetes. Você só precisa preencher os seguintes campos:

  • spec.crd.spec.names.kind é o tipo de restrição. Quando em minúsculas, o valor desse campo precisa ser igual a metadata.name.
  • spec.crd.spec.validation.openAPIV3Schema é o esquema para o campo spec.parameters do recurso de restrição (o restante do esquema da restrição é definido automaticamente pelo Anthos Config Management). Ele segue as mesmas convenções de um recurso regular de CRD. Sua definição está documentada aqui.

Prefixar o modelo de restrição com o nome "K8s" é uma convenção que permite evitar colisões com outros tipos de modelos de restrição, como os modelos de Forseti direcionados aos recursos do GCP.

O Código Fonte Rego

Local

O código-fonte Rego é armazenado no campo spec.targets, em que targets é uma matriz de objetos no formato {"target": "admission.k8s.gatekeeper.sh", "rego": <REGO SOURCE CODE>, "libs": <LIST OF REGO LIBRARIES>}. Atualmente, apenas uma entrada em targets é permitida.

  • target diz ao Anthos Config Management quais sistema estamos vendo (neste caso, Kubernetes)
  • rego é o código fonte da restrição
  • libs é uma lista opcional de bibliotecas de código Rego que serão disponibilizadas para o modelo de restrição. Ele foi criado para facilitar o uso de bibliotecas compartilhadas e está fora do escopo deste tutorial.

Código-fonte

Vamos dar uma olhada no Rego para a restrição acima:

package k8sdenynames

violation[{"msg": msg}] {
   input.review.object.metadata.name == input.parameters.invalidName
   msg := sprintf("The name %v is not allowed", [input.parameters.invalidName])
}

Há alguns itens a serem observados aqui:

  • package k8sdenynames é requerido pelo OPA (ambiente de execução do Rego). O valor é ignorado.
  • A regra Rego que o Policy Controller chama para verificar se há alguma violação é chamada violation. Se essa regra tiver correspondências, ocorreu uma violação da restrição.
  • A regra violation tem a assinatura violation[{"msg": "violation message for the user"}], em que o valor de "msg" é a mensagem de violação que será retornada ao usuário
  • Os parâmetros fornecidos à restrição são disponibilizados sob a palavra-chave input.parameters.
  • A solicitação em teste é armazenada com a palavra-chave input.review

input.review tem os seguintes campos:

  • uid é o código exclusivo para essa solicitação específica, não disponível durante a auditoria
  • kind é a informação de tipo para o objeto em teste. O formato é o seguinte:
    • kind o tipo de recurso
    • group o grupo de recursos
    • version a versão do recurso
  • name é o nome do recurso Pode estar vazio se o usuário confiar no servidor da API para gerar o nome em uma solicitação CREATE.
  • namespace é o espaço para namespace do recurso (não fornecido para recursos com escopo no cluster)
  • operation é a operação solicitada (por exemplo, CREATE ou UPDATE), não disponível durante a auditoria.
  • userInfo é a informação do usuário solicitante, não disponível durante a auditoria
    • username é o usuário que está fazendo a solicitação
    • uid é o UID do usuário
    • groups é uma lista de grupos dos quais o usuário é membro
    • extra é qualquer informação extra fornecida pelo Kubernetes
  • object é o objeto que o usuário está tentando modificar/criar
  • oldObject é o estado original do objeto, disponível apenas nas operações UPDATE
  • dryRun é se esta solicitação foi chamada ou não com kubectl --dry-run, não disponível durante a auditoria

Como escrever modelos de restrição referencial

Modelos de restrição referencial são modelos que permitem ao usuário restringir um objeto em relação a outros objetos. Um exemplo disso pode ser "não permita que um pod seja criado antes que se saiba que existe uma entrada correspondente". Outro exemplo pode ser "não permita que dois serviços tenham o mesmo nome de host".

O Policy Controller permite que você escreva restrições referenciais observando o servidor de API para conseguir um conjunto de recursos fornecidos pelo usuário. Quando um recurso é modificado, o Policy Controller o armazena em cache localmente, para que possa ser facilmente referenciado pelo código fonte Rego. O Policy Controller disponibiliza esse cache com a palavra-chave data.inventory.

Os recursos com escopo definido em cluster são armazenados em cache no seguinte local:

data.inventory.cluster[<groupVersion>][<kind>][<name>]

Por exemplo, um nó chamado my-favorite-node pode ser encontrado em

data.inventory.cluster["v1"]["Node"]["my-favorite-node"]

Recursos com namespace são armazenados em cache aqui:

data.inventory.namespace[<namespace>][<groupVersion>][<kind>][<name>]

Por exemplo, um ConfigMap chamado production-variables no namespace shipping-prod pode ser encontrado em

data.inventory.namespace["shipping-prod"]["v1"]["ConfigMap"]["production-variables"]

O conteúdo completo do objeto é armazenado nesse local de cache e pode ser referenciado no seu Rego da maneira que você achar melhor.

Mais informações sobre Rego

As informações acima fornecem os recursos exclusivos do Policy Controller que facilitam a gravação de restrições nos recursos do Kubernetes no Rego. Um tutorial completo sobre como escrever no Rego está fora do escopo deste guia. No entanto, o site do Open Policy Agent tem documentação sobre a sintaxe e os recursos da linguagem própria do Rego.

Como instalar seu modelo de restrição

Depois de criar seu modelo de restrição, basta kubectl apply e o Policy Controller cuidará da ingestão. Verifique o campo status do seu modelo de restrição para garantir que não haja erros ao instancia-lo. Na ingestão bem-sucedida, o campo status precisa mostrar created: true e o observedGeneration anotado no campo status precisa ser igual ao campo metadata.generation.

Depois que o modelo é ingerido, você pode aplicar restrições, conforme descrito em Como criar restrições.

Como remover um modelo de restrição

Primeiro, verifique se nenhuma restrição que você quer preservar está usando o modelo de restrição:

kubectl get [TEMPLATE-NAME]

No caso de um conflito de nomenclatura entre o nome do modelo de restrição e um objeto diferente no cluster, você pode usar o seguinte comando:

kubectl get [TEMPLATE-NAME].constraints.gatekeeper.sh

Remova o modelo de restrição:

kubectl delete constrainttemplate [CONSTRAINT-TEMPLATE-NAME]

Quando você remove um modelo de restrição, não é mais possível criar restrições que o referenciem.

A seguir