El objetivo de este ejemplo es poder demostrar cómo es posible utilizar SPIRE en un Kubernetes environment para proveer de forma segura de identidades a servicios y utilizarlos para realizar mTls entre los mismos
El escenario consta de 4 namespaces spire
, postgres-ns
, api-ns
y client-ns
.
El server esta configurado con k8s_psat
node attestor para validar la identidad del agent y el agent por su parte con k8s
workload attestor, que es utilizado para validar la identidad del servicio que estamos attestando.
Customer-db contiene un postgresql database configurado en ssl mode, para poder utilizar certificados para autenticar el usuario que está llamando la base de datos, y el postgres-sidecar es un spiffe-helper que es el q attesta contra el spire-agent guarda el SVID en disco y utlizando reload.sh para notificar a la bd que rote los certificados.
api aquí es donde todo se conecta, este pod contiene una REST API que se attesta contra el agent utilizando la WorkloadAPI, guarda el X.509 SVID en disco y lo presenta al postgresql para identificar al usuario (el SVID tiene un dns con el nombre del usuario al que representa), A su vez en el pod hay un Envoy, que se encuentra configurado para utilizar SDS endpoint y obtener SVIDs desde el agent, y un listener que valida al caller utilizando mTLS y redirecciona a la API
Por ultimo tenemos al pod del cliente, el cual contiene un client de la REST API, el cual llama al Envoy, también configurado con SDS, que se encuentra en el mismo pod y realiza un mTLS contra el Envoy en la API.
El siguiente es un diagrama simplificado mostrando el K8S cluster con sus nodos.
- spire
- spire-server-0: SPIRE Server statefulSet
- spire-agent-xxxx: agents daemonseat
- postgres-ns
- customer-db-0: customer database pod
- postgres: postgresql database, configurada para aceptar SSL authentication
- postgres-sidecar: spiffe-helper que attesta contra SPIRE agent y guarda SVID en disco y se comunica con postgres para recargar certificados
- customer-db-0: customer database pod
- api-ns
- api-xxxx-xxx: api pod
- api: REST API que attesta contrar el agent y presenta el X.509 SVID al postgres
- api-proxy: Envoy container configurado para obtener SVIDs desde SPIRE Agent SDS y un mTLS listener para acceder a la api
- api-xxxx-xxx: api pod
- client-ns
- client-xxxx-xxxx: client pod
- client: simple REST API client
- client-proxy: Envoy container configurado para obtener SVIDs desde SPIRE Agent SDS, y conectar con api-proxy utilizando mTLS
- client-xxxx-xxxx: client pod
Por ahora solo hemos hablado de la configuración de los pods pero no de las entries utilizadas para poder attestar los servicios. Entries son creadas utilizando entries.json
Esta entry es la que nos permite configurar un alias al agent, en este ejemplo estamos utilizando k8_psat node attestor
spifeID: spiffe://example.org/spire-agent-node
alias del agentparentID: spiffe://example.org/spire/server
todos losnodes
tienen que tener como padre al serverselector: k8s_psat:agent_ns:spire
el agent debe de estar en el namespacespire
selector: k8s_psat:agent_sa:spire-agent
el agent debe de utilizar la service accountspire-agent
selector: k8s_psat:cluster:spire-cluster
: el agent debe de estar en el clusterspire-cluster
node
: está entry es utilizada para node attestation
parentID: spiffe://example.org/spire-agent-node
: SPIFFE ID del agent que propaga esta identidadspiffeID: spiffe://example.org/postgres-sidecar
: SPIFFE ID del SVIDselector: k8s:container-name:postgres-sidecar
: workload debe de estar dentro del containerpostgres-sidecar
selector: k8s:ns:postgres-ns
: el workload debe de estar dentro del namespacepostgres-ns
ttl 60
: SVIDs creados por esta entry expiran en 60 segundos.
parentID spiffe://example.org/spire-agent-node
: SPIFFE ID del agent que propaga esta identidadspiffeID spiffe://example.org/api
: SPIFFE ID del SVIDselector k8s:container-name:api
: el workload debe de estar dentro del containerapi
selector k8s:ns:api-ns
: el workload debe de estar dentro del namespaceapi-ns
dns symuser
: cuando un SVID va a ser utilizado para presentar a Postgres es requerido que el mismo tenga el nombre del usuario que se está autenticando en como un DNSttl 60
: SVIDs creados por esta entry expiran en 60 segundos.
parentID spiffe://example.org/spire-agent-node
: SPIFFE ID del agent que propaga esta identidadspiffeID spiffe://example.org/api-proxy
: SPIFFE ID del SVIDselector k8s:container-name:api-proxy
: el workload debe de estar dentro del containerapi-proxy
selector k8s:ns:api-ns
: el workload debe de estar dentro del namespaceapi-ns
ttl 60
: SVIDs creados por esta entry expiran en 60 segundos.
parentID spiffe://example.org/spire-agent-node
SPIFFE ID del agent que propaga esta identidadspiffeID spiffe://example.org/client-proxy
: SPIFFE ID del SVIDselector k8s:container-name:client-proxy
: el workload debe de estar dentro del containerclient-proxy
selector k8s:ns:client-ns
: el workload debe de estar dentro del namespaceclient-ns
ttl 60
: SVIDs creados por esta entry expiran en 60 segundos.
Para este ejemplo se provee de scripts que levantan un kind
configurado con certificados y se deploya un cluster.
1-cluster-create.sh
: descargar dependencias y crear cluster (en kind). En caso de que la computadora no disponga de unkind
okubectl
instalado los mismo se descargan en la carpeta./bin
, si existen se crea un link a los mismos para poder utilizarlos.2-build-images.sh
: buildea las imagenes requeridas (spiffe-helper, api y client)3-deploy.sh
: agrega las imágenes a kind, levanta todos los namespaces y crea las entries requeridas.4-call-client.sh
: hace una llamada al CLI para poder obtener los customer en la bd.5-make-db-fails.sh
: cambia el DNS del certificado para provocar que el SVID generado no apunte a un usuario válido. Una vez que se ejecuta se puede llamar a4-call-client.sh
para validar que falle, obteniendo un status500
6-make-envoy-mtls-fails.sh
: cambia un selector de la entry del API proxy, esto hace que no se pueda actualizar y una vez el certificado expira empezamos a obtener un status503
.7-restore-entries.sh
: restaura entries. Una vez propagados los cambios se puede correr4-call-client.sh
para validar un success.8-cluster-delete.sh
: borra el cluster creado.