Pare de sofrer com REGO, vá de YAML

A criação de políticas de Segurança no Kubernetes é uma busca incessante da área de Segurança para evitar que incidentes exponham os negócios a riscos, como o de vazamento de dados.Até aí, a gente sabe que o negócio é aplicar políticas de segurança nos clusters Kubernetes para impedir ao máximo qualquer exposição a riscos e, no mercado, há várias alternativas de Security Policies para isso: OPA, Gatekeeper, Syra, AWS Opa, Azure Policy e etc.No entanto, o desafio para nós, kuberneteiros de plantão, está em implementar e manter essas políticas na linguagem em que estão escritas, REGO. Por estarmos acostumados a trabalhar com YAML, ferramentas que usam uma nova linguagem, nesse caso REGO, só dificultam a nossa vida! É como dar um passo para trás no que que estamos fazendo para aprender uma nova linguagem e, daí, aplicá-la nas políticas de segurança do cluster K8s.Cansado de sofrer com isso, fui atrás de uma saída e achei o Kyverno. O Kyverno é um controller de Kubernetes capaz de criar a sua própria política de segurança com YAML.Ainda, se mudar de cloud provider, as suas regras já farão parte de seu CI/CD ou de um backup velero ou, no pior cenário, nos YAMLS files do seu diretório escondido no seu drive. Em resumo, é fácil de escrever, entender, ficam num único lugar e podem migrar de cloud provider ou, até mesmo, para on-premise, evitando o lock-in com as operadoras cloud.Você consegue quase tudo com o Kyverno, veja só:

  • Bloquear remoção de resources, principalmente, o próprio Kyverno;

  • Rastrear o que estão executando que possa infringir na segurança dos nós;

  • Automatizar e policiar a criação de labels pra cobrar $$ dos times, quando for um K8s multi tenant;

  • Automatizar a criação de Network Policy e quem pode mexer nelas;

  • Bloquear o uso de recursos específicos, por exemplo, só quero que subam Ingress usando meu ingress-nginx ou o meu kong e o traefik são só do time X;

  • Automagicamente saber que qualquer deploy tem que ter um mínimo de:. recursos de cpu/memória. labels. réplicas

  • Distribuir secrets e configmaps para qualquer namespace criado;

  • Restringir uso de services LoadBalancer e/ou NodePort;

  • Impossibilitar a galera de subir coisas no namespace Default;

  • Forçar todo recurso a usar tag nas imagens e sempre do mesmo registry;

  • Aplicar NetworkPolicy quando um novo namespace é criado;

  • Criar um namespace, gerar todas as políticas e gerar um report do que é aplicado.

Ainda, dá para gerar relatórios de infração, inspeção e aplicação — tudo em YAML — para você extrair com aquele “-o json” e brincar de converter pra HTML. Tem CLI pra quem gosta também.Instalando o Kyverno

Quer instalar o Kyverno, brincar com políticas iniciais e ver como funciona?

A instalação não é intrusiva! Nada vai parar, nenhum erro vai ser gerado pela instalação e não há regra default que force qualquer política. Isso é, pode aplicar o Kyverno no seu cluster de produção, pois ele vai gerar no máximo policies reports indicando onde existe problema a ser tratado, com base nas regras default que não interferem, só aferem. Quer instalar o Kyverno, brincar com políticas iniciais e ver como funciona?Apenas atente-se para o fato de que quanto mais workloads você tem em seu cluster, mais recursos de cpu e memória seu Kyverno vai precisar para analisar, gerar e manter políticas e reports. Se for seu caso, aumente esses valores no deploy do Kyverno.Vamos ao que interessa!Você vai precisar:

  • * kubectl

  • * helm => 3.23

  • * 1 cluster k8s

  • * git

* kyverno cli, https://kyverno.io/docs/kyverno-cli/ (opcional, só se quiser mesmo, para validar politicas, etc)$ helm repo add kyverno https://kyverno.github.io/kyverno/

$ helm repo update
$ helm install kyverno -n kyverno kyverno/kyverno --create-namespace

Verifique se o pod do Kyverno está instalado:

$ kubectl get pod -n kyverno
NAME                           READY   STATUS    RESTARTS   AGE
pod/kyverno-6868bc56fb-gfcqn   1/1     Running   0          114s

$ kubectl get cpol
NAME                             BACKGROUND   ACTION
disallow-add-capabilities        true         audit
disallow-host-namespaces         true         audit
disallow-host-path               true         audit
disallow-host-ports              true         audit
Disallow-privileged-containers   true         audit
disallow-selinux                 true         audit
require-default-proc-mount       true         audit
restrict-apparmor-profiles       true         audit
restrict-sysctls                 true         audit

$ kubectl get polr -AO “get pod” 
#aguarde até o pod estar “running”

A partir desse momento, o “get cpol vai trazer as políticas aplicadas ao cluster, no âmbito de cluster.O “get polr -A” trará os reports por namespaces, se você tiver namespaces criados e com workloads em execução.Veja que todas as políticas criadas por default estão em audit na coluna action, ou seja, só auditam os workloads. A partir do momento em que se deseja que se apliquem e impeçam alguma execução ou configuração, basta alterar a regra validationFailureAction: enforce e isso impedirá o próximo pod de seguir “quebrando a política”.

$ kubectl edit cpol disallow-add-capabilities
(...)
spec:
  validationFailureAction: audit    #ou enforce para forçar a regra
(...)Primeiro exemplo:Bloqueio de workloads sem label específica:$ cat require-pod-ns-svc-ing-label-required.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-certain-labels
spec:
  validationFailureAction: audit
  rules:
  - name: validate-name-labels
    exclude:
      resources:
        namespaces:
        - kube-system
        - app1
        - workloads-x
    match:
      resources:
        kinds:
        - Pod
        - Namespace
        - Service
        - Ingress
    validate:
      message: "The label `app.kubernetes.io/name` and is required."
      pattern:
        metadata:
          labels:
            app.kubernetes.io/name: "?*"
  - name: validate-component-labels
    exclude:
      resources:
        namespaces:
        - kube-system
        - app1
        - workloads-x
    match:
      resources:
        kinds:
        - Pod
        - Namespace
        - Service
        - Ingress
    validate:
      message: "The label `app.kubernetes.io/component` is required."
      pattern:
        metadata:
          labels:
            app.kubernetes.io/component: "?*"

Aplique essa política:

$ kubectl apply -f require-pod-ns-svc-ing-label-required.yaml

Verifique nos logs do pod do Kyverno e nos objetos de ClusterPolicy:

$ kubectl get cpol
NAME                             BACKGROUND   ACTION
disallow-add-capabilities        true         audit
disallow-host-namespaces         true         audit
disallow-host-path               true         audit
disallow-host-ports              true         audit
disallow-privileged-containers   true         audit
disallow-selinux                 true         audit
require-default-proc-mount       true         audit
restrict-apparmor-profiles       true         audit
restrict-sysctls                 true         audit
require-certain-labels           true         audit

Aqui, já se observa que nossa regra exigindo as labels informadas sejam aplicadas a qualquer workload em qualquer namespace, com exceção dos namespaces app1, kube-system e workloads-x.

Crie um deploy de teste:

$ cat web.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
    app.kubernetes.io/name: web
  name: web
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
  strategy: {}
  template:
    metadata:
      labels:
        app: web
        app.kubernetes.io/name: web
    spec:
      containers:
      - image: nginx
        name: nginx
        resources: {}
status: {}
$ kubectl apply -f web.yaml
deployment.apps/web created

Perceba aqui que só criei uma label app.kubernetes.io/name: web, o que não satisfaz na íntegra minha política recém-criada.

Para visualizar as auditorias FAIL dos reports, execute:

$ kubectl get polr -n default -o yaml | grep “status: fail” -B14
---
scored: true
status: pass
- message: 'validation error: The label`app.kubernetes.io/component`  
  is required. Rule validate-certain-labels[0] failed at path 
  /metadata/labels/app.kubernetes.io/component/.
  Rule validate-certain-labels[1] failed at path /metadata/labels  
  /app.kubernetes.io/component/.'
policy: require-certain-labels
resources:
- apiVersion: apps/v1
  kind: Deployment
  name: web
  namespace: default
  uid: cbe8ecc1-71f8-4b2c-819d-59417039fc77
rule: validate-ccomponent-labels
scored: true
status: fail

(...)O policyReport vai mostrar o service kubernetes e o namespace default com a mesma falta de labels que indicamos na política. Se essa política estiver enforce, então será obrigatória a adição dessas labels nesses resources e o pod não será criado até ser resolvida essa pendência.

Existem dezenas de outras políticas que podem ser aplicadas no quesito cluster, o que não te impede de aplicá-las somente a um namespace específico, usando o kind: ClusterPolicy ou para namespace, usando o Kind: Policy. Elas estão disponíveis em https://github.com/kyverno/policies.Uma que gosto bastante é a que limita a utilização de recursos e obriga todo pod a obedecer uma regra pré-definida de resources limits/requests:

$ cat require-and-set-requests-limits.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-requests-limits
  annotations:
    policies.kyverno.io/title: Require Limits and Requests 
    policies.kyverno.io/category: Multi-Tenancy
spec:
  validationFailureAction: audit
  rules:
  - name: validate-resources
    exclude:
      resources:
        namespaces:
        - linkerd
        - kube-system
    match:
      resources:
        kinds:
        - Pod
    validate:
      message: "CPU and memory resource requests and limits are required."
      pattern:
        spec:
          containers:
          - resources:
              requests:
                memory: "<=1000Mi"
                cpu: "<=500m"
              limits:
                memory: "<=2000Mi"
                cpu: "<=1000m"
$
$ kubectl apply -f require-and-set-requests-limits.yaml

Com isso, qualquer pod, em qualquer namespace, com exceção do Kube-system, terá que declarar a utilização de resources e, ainda, respeitar os limites para requests e limits. Como diria meu irmão: “existem mil maneiras de preparar Neston, invente a sua”.

Baseando-se nos modelos e exemplos do repositório e na documentação, fica fácil criarmos as políticas que precisamos aplicar, sem precisar aprender uma nova linguagem ou nos debruçar sobre vários recursos do K8s para troubleshooting ou evolução.

É isso aí, galera! Recomendo que corram pra se atualizar. Kyverno é uma maravilha e a curva de aprendizado é de algumas horas.

Qualquer dúvida, sugestão ou crítica, podem me chamar: adonai@getup.io.

Social

Fale conosco

Almeda Campinas 802, CJ 12, Jardim Paulista,

São Paulo - SP, 01404-001

Faça parte do time

Nossos conteúdos

Social

Fale conosco

Almeda Campinas 802, CJ 12, Jardim Paulista,

São Paulo - SP, 01404-001

Faça parte do time

Nossos conteúdos

Social

Fale conosco

Almeda Campinas 802, CJ 12, Jardim Paulista,

São Paulo - SP, 01404-001

Faça parte do time

Nossos conteúdos