Nosso SRE/Kubernetes Admin, Adonai Costa, destaca o uso do Kyverno, um controller de Kubernetes, para aplicar políticas de segurança de forma simples e eficiente. O Kyverno permite criar e manter políticas em YAML, facilitando o processo de implementação e evitando o aprendizado de uma nova linguagem (REGO).
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ó:
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 -A
O “get pod” vai trazer seu pod; aguarde até 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.
Já assinou a nossa Newsletter?
O Kubernetes já faz muito por você e a Getup faz o resto. Oferecemos apoio técnico e estratégico para encontrar oportunidades na sua operação.