Ejemplo 2: Control de acceso multiusuario y RBAC definido por el usuario con OPA y Rego - AWS Guía prescriptiva

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

Ejemplo 2: Control de acceso multiusuario y RBAC definido por el usuario con OPA y Rego

En este ejemplo, se utilizan OPA y Rego para demostrar cómo se puede implementar el control de acceso en una API para una aplicación multiusuario con funciones personalizadas definidas por los usuarios arrendatarios. También demuestra cómo se puede restringir el acceso en función del inquilino. Este modelo muestra cómo OPA puede tomar decisiones detalladas sobre permisos basándose en la información que se proporciona en un puesto de alto nivel.

RBAC definido por el usuario con OPA y Rego

Las funciones de los inquilinos se almacenan en datos externos (datos RBAC) que se utilizan para tomar decisiones de acceso a la OPA:

{ "roles": { "tenant_a": { "all_access_role": ["viewData", "updateData"] }, "tenant_b": { "update_data_role": ["updateData"], "view_data_role": ["viewData"] } } }

Estas funciones, cuando las define un usuario arrendatario, deben almacenarse en una fuente de datos externa o en un proveedor de identidad (IdP) que pueda actuar como fuente de verdad al asignar las funciones definidas por el inquilino a los permisos y al propio inquilino. 

En este ejemplo, se utilizan dos políticas de la OPA para tomar decisiones de autorización y para examinar cómo estas políticas imponen el aislamiento de los inquilinos. Estas políticas utilizan los datos del RBAC definidos anteriormente.

default allowViewData = false allowViewData = true { input.method == "GET" input.path = ["viewData", tenant_id] input.tenant_id == tenant_id role_permissions := data.roles[input.tenant_id][input.role][_] contains(role_permissions, "viewData") }

Para mostrar cómo funcionará esta regla, considere una consulta OPA que tenga la siguiente entrada:

{ "tenant_id": "tenant_a", "role": "all_access_role", "path": ["viewData", "tenant_a"], "method": "GET" }

La decisión de autorización para esta llamada a la API se toma de la siguiente manera, combinando los datos del RBAC, las políticas de la OPA y la entrada de la consulta de la OPA:

  1. Un usuario de Tenant A realiza una llamada a la API a. /viewData/tenant_a

  2. El microservicio de datos recibe la llamada y consulta la allowViewData regla, pasando la entrada que se muestra en el ejemplo de entrada de consulta de OPA.

  3. La OPA utiliza la regla consultada en las políticas de la OPA para evaluar la información proporcionada. La OPA también utiliza los datos del RBAC para evaluar la entrada. La OPA hace lo siguiente:

    1. Verifica que el método utilizado para realizar la llamada a la API seaGET.

    2. Verifica que la ruta solicitada sea. viewData

    3. Comprueba que tenant_id la ruta es igual a la input.tenant_id asociada al usuario. Esto garantiza que se mantenga el aislamiento del inquilino. No se puede autorizar a otro inquilino, incluso con una función idéntica, a realizar esta llamada a la API.

    4. Extrae una lista de permisos de rol de los datos externos de los roles y los asigna a la variable. role_permissions Esta lista se recupera mediante el rol definido por el inquilino que está asociado al usuario en input.role.

    5. Comprueba role_permissions si contiene el permiso viewData.

  4. La OPA devuelve la siguiente decisión al microservicio de datos:

{ "allowViewData": true }

Este proceso muestra cómo la RBAC y el conocimiento de los inquilinos pueden contribuir a tomar una decisión de autorización con la OPA. Para ilustrar mejor este punto, considere la posibilidad de hacer una llamada a la API /viewData/tenant_b con la siguiente entrada de consulta:

{ "tenant_id": "tenant_b", "role": "view_data_role", "path": ["viewData", "tenant_b"], "method": "GET" }

Esta regla devolvería el mismo resultado que la entrada de la consulta OPA, aunque es para un inquilino diferente que tiene un rol diferente. Esto se debe a que esta llamada es para /tenant_b y los datos incluidos view_data_role en el RBAC todavía tienen el viewData permiso asociado. Para aplicar el mismo tipo de control de acceso/updateData, puedes usar una regla OPA similar:

default allowUpdateData = false allowUpdateData = true { input.method == "POST" input.path = ["updateData", tenant_id] input.tenant_id == tenant_id role_permissions := data.roles[input.tenant_id][input.role][_] contains(role_permissions, "updateData") }

Esta regla es funcionalmente igual a la allowViewData regla, pero verifica una ruta y un método de entrada diferentes. La regla sigue garantizando el aislamiento del inquilino y comprueba que el rol definido por el inquilino concede el permiso al que llama a la API. Para ver cómo se puede aplicar esto, examine la siguiente entrada de consulta para ver si hay una llamada a la API a: /updateData/tenant_b

{ "tenant_id": "tenant_b", "role": "view_data_role", "path": ["updateData", "tenant_b"], "method": "POST" }

Esta entrada de consulta, cuando se evalúa con la allowUpdateData regla, devuelve la siguiente decisión de autorización:

{ "allowUpdateData": false }

Esta llamada no se autorizará. Si bien la persona que llama a la API está asociada a la correcta tenant_id y llama a la API mediante un método aprobado, input.role es el definido por el view_data_role inquilino. view_data_roleNo tiene el updateData permiso; por lo tanto, la llamada a /updateData no está autorizada. Esta llamada se habría realizado correctamente para un tenant_b usuario que tiene elupdate_data_role.