¿Qué tal si pudieramos evitar que lleguen malas configuraciones a nuestro clúster de Kuberntes?
Kubernetes nos permiete extender su API con los Admission Webhooks los cuales reciben las peticiones de creación, actualización o eliminación de recursos antes de ser procesadas.
El "Senior" de nuestro equipo comenta que a varios compañeros se les olvida
colocar la etiqueta equipo
en las cargas de trabajo y configuraciones que
aplican a Kubernetes. Esta label es importante porque ayuda en varios procesos
internos de la empresa.
Nosotros como desarrolladores sabemos que Kubernetes tiene un API y tal vez podríamos crear algo que funcione como middleware que valide cada uno de los objetos que llegan a el. Así podríamos filtrar solo los objetos que cumplen con las normas.
Utilizaremos a pepr para escribir fácilmente nuestros Admission Webhooks en TypeScript.
- Inicializamos nuestra aplicación
npx pepr init
- Creamos un nuevo capability que será nuestro WebHook llamado
bouncer
.
// capabilities/bouncer.ts
import { Capability, a } from "pepr";
export const Bouncer = new Capability({
name: "bouncer",
description:
"Verificando que solo los recursos cool ingresen al Kubernetes API",
});
const { When } = Bouncer;
- Por cada tipo de Kubernetes Object (en este caso un
ConfigMap
) que querramos validar haremos lo siguiente
When(a.ConfigMap)
.IsCreatedOrUpdated()
.Validate(request => {
if (request.HasLabel("equipo")) {
return request.Approve();
}
return request.Deny(`${request.Raw.kind} debe tener label 'equipo'`);
});
- ¿Que tal si agregamos al usuario que lo creó o modificó?
When(a.ConfigMap)
.IsCreatedOrUpdated()
.Mutate(request => {
const { username } = request.Request.userInfo;
request.Merge({
metadata: {
labels: {
bouncer: "estuvo-aqui",
},
annotations: {
"creado-por": username,
},
},
});
})
.Validate(request => {
// ... código de validación de la sección anterior...
});
- "Montamos" nuestro WebHook al entrypoint de nuestra aplicación
pepr.ts
import { PeprModule } from "pepr";
import cfg from "./package.json";
import { Bouncer } from "./capabilities/bouncer";
new PeprModule(cfg, [Bouncer]);
- Construimos nuestra aplicación
npx pepr build
- La desplegamos en nuestro clúster
npx pepr deploy
# O de manera alterantiva podemos usar el manifest directamente
# kubectl apply -f dist/pepr-*.yaml
- Verificamos que esté lista
$ kubectl get deploy -n pepr-system
NAME READY UP-TO-DATE AVAILABLE AGE
pepr-4b8c9479-f7c6-5f20-894c-37353c5deece 2/2 2 2 61s
Apliquemos un ConfigMap
que no cumple con los requerimientos
$ kubectl apply -f manifests/bouncer.samples.yaml
namespace/bouncer-demo created
Error from server: error when creating "manifests/bouncer.samples.yaml": admission webhook [...]
denied the request: ConfigMap debe tener label 'equipo'
Descomentamos los labels que hacen falta y volvemos a intentar
# sed 's/# //g' manifests/bouncer.samples.yaml
$ kubectl apply -f manifests/bouncer.samples.yaml
namespace/bouncer-demo unchanged
configmap/ejemplo created
¡Funciona! Ahora podemos agregar los demás objetos que queremos validar.