Usuarios y roles con el mínimo privilegio

25 minIntermedio

En esta guía aprenderás a crear un rol que lee y escribe en una colección sin poder borrar y a dárselo al usuario de una aplicación.

Una aplicación que solo crea y actualiza pedidos no necesita borrarlos ni leer otras colecciones. Con un rol a medida le das solo esas acciones sobre esa colección, así que si su contraseña se filtra, quien la use no puede hacer más de lo que podía hacer la aplicación.

Lo que vas a construir

Un rol ordersWriter con las acciones find, insert y update sobre la colección tienda.pedidos, y un usuario tienda-pedidos que solo tiene ese rol.

Requisitos

  • Docker y mongosh, para arrancar TeselaDB con TLS y usuarios en tu máquina.

Prepara el entorno

Shell
docker run -d --name teseladb -p 27017:27017 viuzi/teseladb:latest \
  start --dbpath /data --bind_ip 0.0.0.0
until docker logs teseladb 2>&1 | grep -q '^ready'; do sleep 1; done
ROOT_PASSWORD=$(docker exec teseladb cat /data/root-credentials.txt | sed -n 's/^password=//p')
docker cp teseladb:/data/pki/ca.pem ./ca.pem
export TESELADB_URI="mongodb://root:$ROOT_PASSWORD@127.0.0.1:27017/?tls=true&tlsCAFile=./ca.pem&authSource=admin"
mongosh "$TESELADB_URI" --quiet --eval 'db.runCommand({ ping: 1 }).ok'
mongosh "$TESELADB_URI"

El contenedor arranca un nodo en modo seguro, con TLS y usuarios, y su primer arranque crea la autoridad de certificación y el usuario root con una contraseña al azar. Las órdenes siguientes esperan a la línea ready del nodo, sacan esa contraseña y la CA del contenedor y dejan en TESELADB_URI una cadena con TLS como root. Cuando mongosh responde 1, la última línea abre una sesión de root para crear el rol y el usuario.

1. Crea el rol

Cada privilegio une un recurso, aquí una colección, con las acciones permitidas sobre él:

JavaScript
db.getSiblingDB("admin").createRole({
  role: "ordersWriter",
  privileges: [{ resource: { db: "tienda", collection: "pedidos" }, actions: ["find", "insert", "update"] }],
  roles: [],
});

2. Crea el usuario

El usuario solo tiene el rol ordersWriter. passwordPrompt() pide la contraseña sin que quede en el historial de mongosh:

JavaScript
db.getSiblingDB("admin").createUser({ user: "tienda-pedidos", pwd: passwordPrompt(), roles: ["ordersWriter"] });

Probarlo

En otra terminal, desde la misma carpeta donde quedó ca.pem, conéctate como tienda-pedidos para ver lo que puede hacer la aplicación con ese rol, porque root puede hacerlo todo y la prueba no diría nada. mongosh te pide la contraseña que le diste en el paso 2:

Shell
mongosh "mongodb://tienda-pedidos@127.0.0.1:27017/?tls=true&tlsCAFile=./ca.pem&authSource=admin"

Conectado como tienda-pedidos, prueba una inserción y un borrado en tienda.pedidos, sabiendo que el rol lleva la acción insert pero ninguna que borre documentos, así que solo puede funcionar la primera de las dos escrituras de abajo:

JavaScript
const tienda = db.getSiblingDB("tienda");
tienda.pedidos.insertOne({ total: 30 });
tienda.pedidos.deleteOne({});

La inserción funciona y el borrado falla con Unauthorized, el mismo error que recibes si intentas leer otra colección de tienda.

Limpiar

Si era una prueba, borra el usuario y el rol conectado como root. Al final borra también el contenedor y la CA con las dos últimas órdenes: el nodo guarda los datos dentro, sin volumen, así que con él desaparecen el rol y el usuario y queda libre el puerto 27017:

JavaScript
db.getSiblingDB("admin").dropUser("tienda-pedidos");
db.getSiblingDB("admin").dropRole("ordersWriter");
Shell
docker rm -f teseladb
rm ca.pem

Siguientes pasos