MongoDB utilitza el control d'accés basat en rols (RBAC) per controlar l'accés i els privilegis a les bases de dades

Introducció

El control d'accés, també conegut com a autorització és una tècnica de seguretat que consisteix a determinar qui pot accedir a quins recursos.

Autenticació

En molts sistemes de gestió de bases de dades, un usuari s'identifiquen només amb un nom d'usuari.

En canvi, a MongoDB un usuari no només s'identifica pels seu nom d'usuari, sinó també per la base de dades en què es van crea (que es com a base de dades d'autenticació d'aquest usuari).

Això vol dir que a MongoDB és possible tenir diversos usuaris amb el mateix nom d'usuari sempre que es creïn en bases de dades d'autenticació diferents. Per autenticar-te com a usuari, has de proporcionar no només un nom d'usuari i una contrasenya, sinó també el nom de la base de dades d'autenticació associada a aquest usuari.

Es podria suposar que els usuaris creats en una base de dades d'autenticació determinada tindrien privilegis d'accés disponibles només per a aquesta base de dades en particular, però aquest no és el cas. Cada usuari, independentment de la base de dades d'autenticació en què es va crear, pot tenir privilegis assignats a diferents bases de dades.

Autorització

En un control d'accés basat en rols, els usuaris no tenen permís per dur a terme accions directament sobre els recursos, com ara inserir un document nou a la base de dades o consultar una col·lecció concreta, sinó que les regles que permeten accions sobre recursos concrets s'assignen a rols.

Els rols es defineixen amb un conjunt d'un o més privilegis. Cada privilegi consisteix en una acció (com ara crear nous documents, recuperar dades d'un document o crear i suprimir usuaris) i el recurs sobre el qual es pot realitzar aquesta acció (com ara una base de dades anomenada reports o una col·lecció anomenada orders).

Els rols s'identifiquen amb la combinació del nom del rol i la base de dades, ja que cada rol, excepte els creats a la base de dades admin, només pot incloure privilegis que s'apliquen a la seva pròpia base de dades. En concedir a un usuari rols definits en una base de dades diferent de la seva base de dades d'autenticació, es pot donar permís a un usuari per actuar en més d'una base de dades. Els rols es poden concedir quan creas un usuari o en qualsevol moment després. La revocació de la pertinença al rol també es pot fer a voluntat, de manera que és senzill desvincular la gestió d'usuaris de la gestió dels drets d'accés.

MongoDB proporciona un conjunt de rols integrats que descriuen privilegis que s'utilitzen habitualment en sistemes de bases de dades, com ara read per concedir accés només de lectura, readWrite per atorgar tant permisos de lectura com d'escriptura o dbOwner per concedir privilegis administratius complets sobre una base de dades determinada. Per a escenaris més específics, també es poden crear rols definits per l'usuari amb conjunts personalitzats de privilegis.

El control d'accés basat en rols permet assignar als usuaris només el nivell mínim i precís de permisos d'accés que necessiten per treballar en les seves tasques respectives. Aquesta és una pràctica de seguretat important coneguda com a principi de privilegis mínims .

Entorn de treball

El control d'accés de MongoDB no està habilitat per defecte

Arrenca la base de dades i crea una sessió:

> start-process -NoNewWindow mongodb
> mongosh

Crea l'usuari administrador root:

> use admin
switched to db admin
admin> db.createUser(
  {
    user: "root",
    pwd: passwordPrompt(),
    roles: [
      {
        role: "userAdminAnyDatabase",
        db: "admin"
      },
      "readWriteAnyDatabase"
    ]
  }
)
Enter password
********{ ok: 1 }

Para el servidor i surt del shell:

> db.adminCommand({ shutdown: 1 })
> exit

Torna a executar mongod, però aquest cop amb el flag --auth:

> start-process -NoNewWindow mongod --auth

Crea una nova sessió amb l'usuari root:

> mongosh.exe -u root
Enter password: ********
...
test>

Pots veure que ara ja no t'avisa de que RBAC està deshabilitat.

Rols predefinits

Per explicar com funciona el control d'accés basat en rols (RBAC, per abreujar) a la pràctica utilitzarem d'exemple una empresa de vendes que utilitza dues bases de dades.

  1. La primera base de dades (anomenada sales) emmagatzemarà dades sobre les comandes dels clients a la botiga de l'empresa amb dues col·leccions separades: customersper a les dades personals dels seus clients i orders per als detalls de la comanda.

  2. La segona base de dades (aquesta anomenada reports) emmagatzemarà informes agregats de vendes mensuals. Aquesta base de dades contindrà una única col·lecció anomenada reports.

L'empresa només té dos empleats, la Laura i la Roser.

I aquests són els permisos que crearas a continuació:

Usuari sales reports admin
root readWriteAnyDatabase, admin:userAdminAnyDatabase
laura readWrite
roser read readWrite

Canvia a la base de dades sales:

> use sales
switched to db sales

Inserta un client a la col.lecció customers:

sales> db.customers.insertOne({name: "David"})
{
  acknowledged: true,
  insertedId: ObjectId('67882f9242d68a24b1cb0ce3')
}

Inserta una ordre a la col.lecció orders:

sales> db.orders.insert({total: 100})
{
  acknowledged: true,
  insertedIds: { '0': ObjectId('67882fd442d68a24b1cb0ce4') }
}

Laura

A continuació crea l'usuari "Laura" que treballa al departament de vendes i necessita accés complet a les dues col·leccions de la base de dades sales, però no necessita treballar amb la base de dades reports.

sales> db.createUser( {
    user: "laura", 
    pwd: passwordPrompt(),
    roles: [ {role : "readWrite", db: "sales" }]
})
Enter password
********{ ok: 1 }

Pots confirmar que s'ha afegit la "Laura" a la llista d'usuaris de la base de dades sales:

sales> show users
[
  {
    _id: 'sales.laura',
    userId: UUID('f97f35ec-b6c9-4175-adef-7ddd1b3f47da'),
    user: 'laura',
    db: 'sales',
    roles: [ { role: 'readWrite', db: 'sales' } ],
    mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ]
  }
]

Canvia a la base de dades reports:

sales> use reports
switched to db reports

Insereix un document a la col.lecció reports:

> db.reports.insertOne({orders: 1})
{
  acknowledged: true,
  insertedId: ObjectId('678833800c36be4e5fcb0ce2')
}

Tanca la sessió i crea una nova sessió amb l'usuari laura:

> mongosh -u laura sales
...

Executa l'order show dbs per llistar les bases de dades disponibles:

sales> show dbs
sales  80.00 KiB

A diferència del vostre compte d'administrador, només apareixerà una base de dades per a la laura , ja que només li has concedit accés a la base de dades sales

Ara comproveu si la laura pot recuperar objectes de les dues col·leccions a la base de dades sales:

sales> db.customers.find()
[ { _id: ObjectId('67882f9242d68a24b1cb0ce3'), name: 'David' } ]
sales> db.orders.find()
[ { _id: ObjectId('67882fd442d68a24b1cb0ce4'), total: 100 } ]

Per assegurar-te que els drets d'accés a la base de dades sales s'han configurat correctament, pots comprovar si la laura també pot inserir nous documents:

sales> db.customers.insertOne({name: "Maria"})
{
  acknowledged: true,
  insertedId: ObjectId('6789044acdca8d8d69cb0ce2')
}

Com has concedit a la laura el rol readWrite, està autoritzats a escriure nous documents en aquesta base de dades.

Finalment, verifica que la laura no pot llegir ni escriure cap dada a la based de dades resports, ja que no li vas concedir accés a través dels rols assignats.

sales> use reports
switched to db reports
reports> db.reports.find()
MongoServerError[Unauthorized]: not authorized on reports to execute command { find: "reports", filter: {}, lsid: { id: UUID("461bf330-c3d1-4344-a350-d89e646843eb") }, $db: "reports" }

El missatge d'error Unauthorized indica que la laura no té prou drets d'accés per interactuar amb les dades de la base de dades reports.

Roser

Ara has de crear el compte per a la roser, l'analista de vendes de l'empresa.

La roser necessita accés d'escriptura a la base de dades reports per crear informes, així co només accés de lectura a la base de dades sales per recuperar les dades.

Canvia a la base de dades admin i autentica't com root:

sales> use admin
switched to db admin
admin> db.auth("root")
Enter password
********{ ok: 1 }

Crea l'usuari roser en la base de dades reports:

admin> use reports
switched to db reports
reports> db.createUser({
... user: "roser",
... pwd: passwordPrompt(),
... roles: [ { role: "readWrite", db: "reports"}, {role: "read", db: "sales"} ]
... })
Enter password
********{ ok: 1 }

Autentica't com l'usuari roser:

reports> db.auth("roser")
Enter password
********{ ok: 1 }

Executa l'ordre show dbs per llistar les bases de dades disponibles per a roser:

reports> show dbs
reports   40.00 KiB
sales    112.00 KiB

Com que la roser pot utilitzar les bases de dades sales i reports, aquestes dues bases de dades es mostraran a la sortida.

Comprova si la roser pot recuperar objectes de la base de dades sales:

reports> use sales
switched to db sales
sales> db.orders.find()
[ { _id: ObjectId('67882fd442d68a24b1cb0ce4'), total: 100 } ]

A continuació, pots provar d'inserir un document nou a la col·lecció orders:

sales> db.orders.insertOne({total: 50})
MongoServerError[Unauthorized]: not authorized on sales to execute command { insert: "orders", documents: [ { total: 50, _id: ObjectId('67890a7d86d96a7e1dcb0ce2') } ], ordered: true, lsid: { id: UUID("f2005137-cf06-411a-8cae-5bd2e3d878c1") }, $db: "sales" }

Com que has assignat a roser només un rol read per a aquesta base de dades, l' ordre insertOne fallarà amb un missatge d'error.

A continuació, confirma si la roser pot llegir i escriure dades a la base de dades reports:

sales> use reports
switched to db reports
reports> db.reports.find()
[ { _id: ObjectId('678833800c36be4e5fcb0ce2'), orders: 1 } ]
reports> db.reports.insertOne({orders:2})
{
  acknowledged: true,
  insertedId: ObjectId('67890b2086d96a7e1dcb0ce3')
}

Concessió i revocació de rols per a usuaris existents

A la pràctica, els administradors de bases de dades sovint necessiten revocar o concedir nous privilegis als usuaris que ja s'han creat al sistema.

A continuació, autentica't com l'usuari root,

reports> use admin switched to db admin admin> db.auth("root") Enter password ********


Concedeix permís de només lectura a la `laura` de la base de dades `reports`:

```mongodb
admin> use sales
switched to db sales
sales> db.grantRolesToUser("laura", [{role: "read", db: "reports"}])
{ ok: 1 }

Verifica els rols de la laura:

sales> show users
[
  {
    _id: 'sales.laura',
    userId: UUID('f97f35ec-b6c9-4175-adef-7ddd1b3f47da'),
    user: 'laura',
    db: 'sales',
    roles: [
      { role: 'readWrite', db: 'sales' },
      { role: 'read', db: 'reports' }
    ],
    mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ]
  }
]

Autentica't amb l'usuari laura i verifica que pot accedir a la base de dades reports després del canvi:

sales> db.auth("laura")
Enter password
********{ ok: 1 }
sales> use reports
switched to db reports
reports> db.reports.find()
[
  { _id: ObjectId('678833800c36be4e5fcb0ce2'), orders: 1 },
  { _id: ObjectId('67890b2086d96a7e1dcb0ce3'), orders: 2 }
]

Si al cap d'un temps vols revocar la capacitat de l'usuari laura per accedir als informes, amb l'usuari root elimina el permís corresponent:

...
sales> db.revokeRolesFromUser("laura",[ {role:"read", db:"reports"} ])
{ ok: 1 }

Verifica que la laura ja no pot llegir documents de la base de dades reports:

...
reports> db.reports.find()
MongoServerError[Unauthorized]: not authorized on reports to execute command { find: "reports", filter: {}, lsid: { id: UUID("55abd9f8-d8b1-479c-a7b2-cae6a4163505") }, $db: "reports" }

Rols definits per l'usuari

Mitjançant el mètode createRole, pots crear un rol segons les teves necessitats.

Afegeix uns quants productes a la base de dades sales:

sales> db.products.insertMany( [{"name":"orange"}, {"name": "apple"}, {"name":"banana"}] )
...

Canvia a l'usuari root i crea un rol customer a la base de dades sales que pugui buscar i llegir de la col.lecció products:

sales> db.createRole({
... role: "customer",
... privileges: [{
...    resource: { db: "sales", collection: "products"},
...    actions: [ "find"]
... }],
... roles: []
... })
{ ok: 1 }

Quan afegeixes un rol, crees el rol en una base de dades específica: MongoDB utilitza la combinació de la base de dades i el nom del rol per definir de manera única un rol.

Més informació a : https://www.mongodb.com/docs/manual/core/security-user-defined-roles/

En aquest enllaç tens la llista d'accions: Privilege Actions

Si mires la informació dels rols de la base de dades sales pots veure que la propietat isBuiltin indica que el rol customer està definit per l'usuari.

sales> db.getRoles()
{
  roles: [
    {
      _id: 'sales.customer',
      role: 'customer',
      db: 'sales',
      roles: [],
      isBuiltin: false,
      inheritedRoles: []
    }
  ],
  ok: 1
}

Aquest rol el faras servir per afegir clients que puguin consultar la llista de productes de la nostra empresa:

sales> db.customers.find()
[
  { _id: ObjectId('67882f9242d68a24b1cb0ce3'), name: 'David' },
  { _id: ObjectId('6789044acdca8d8d69cb0ce2'), name: 'Maria' }
]

Per exemple, afegeix la maria com usuari de la base de dades sales:

sales> db.createUser( {
... user: "maria",
... pwd: passwordPrompt(),
... roles: [ {role: "customer", db: "sales"} ]
... })
Enter password
********{ ok: 1 }
sales>

A continuació modifica el document de la Maria per registrar que té un usuari a la base de dades:

sales> db.customers.updateOne( {name: "Maria"}, { $set: {username: "maria"}} )
...

Ara la Maria pots consultar la llista del nostre productes, però com pot crear una ordre de compra?

Crea un rol maria amb que pugui realitzar accions find i insert a la col.lecció orders_maria:

db.createRole({ ... role: "maria", ... privileges: [{ ... resource: {db: "sales", collection: "orders_maria"}, ... actions: [ "find", "insert" ] ... }], ... roles: ["customer"] ... })


Afegeix el rol `maria` a l'usuari `maria`:

sales> db.grantRolesToUser("maria", [{role: "maria", db: "sales"}])
{ ok: 1 }

Com que el rol `maria` hereta els privilegis de `customer` pots revocar el rol `customer` a l'usuari `maria`:

```mongodb
sales> db.revokeRolesFromUser("maria", [{ role:"customer", db: "sales"}])
{ ok: 1 }

Autentica't com l'usuari maria:

sales> db.auth("maria")
Enter password
********{ ok: 1 }

Verifica que l'usuari maria pot buscar productes i inserir ordres a la col.lecció orders_maria:

sales> db.products.find()
[
  { _id: ObjectId('67893a9520c44545d1cb0ce2'), name: 'orange' },
  { _id: ObjectId('67893a9520c44545d1cb0ce3'), name: 'apple' },
  { _id: ObjectId('67893a9520c44545d1cb0ce4'), name: 'banana' }
]
sales> db.orders_maria.insertOne({product: "orange", quantity: 5, price: 1.23 })
{
  acknowledged: true,
  insertedId: ObjectId('678946b720c44545d1cb0ce5')
}

Verifica que l'usuari maria no pots inserir documents a la co.lecció orders:

sales> db.orders.insertOne({product: "orange", quantity: 5, price: 1.23 })
MongoServerError[Unauthorized]: not authorized on sales to execute command { insert: "orders", documents: [ { product: "orange", quantity: 5, price: 1.23, _id: ObjectId('6789477120c44545d1cb0ce6') } ], ordered: true, lsid: { id: UUID("51dc4213-8d64-4c2f-b5a2-8dd5a9d838a4") }, $db: "sales" }

Immutabilitat

Si et fixes, els permisos d'un usuari customer no permeten les accions update o remove en la seva col.lecció d'ordres.

Això és important perquè com podem seguir totes les ordres de tots els clients?

Hem de permetre que els usuaris amb el rol de customer insereixin ordres a la col.lecció ordres, però res més.

TODO

TODO