Русский
Русский
English
Статистика
Реклама

Rbac

RBAC? ABAC?.. PERM! Новый подход к авторизации в облачных веб-службах и приложениях

28.01.2021 22:20:35 | Автор: admin

Данная статья, преследует цель, рассказать о новом походе к авторизации в облачных решениях, в основе которого лежит использование интерпретируемого языка определения политики управления доступом который называется языком моделирования PERM (PML). Данный язык можно использовать для выражения различных моделей управления доступом, таких как список управления доступом (ACL), управление доступом на основе ролей (RBAC), управление доступом на основе атрибутов (ABAC) и других. А также рассказать о практическом воплощении этого подхода в виде конкретной реализации кросс-языковой библиотеки авторизации Casbin


Прежде чем приступать к рассказу, хотел выразить благодарность ключевому создателю как самого подхода, так и данной библиотеки Casbin, который является одним из исследователей, работающим в Microsoft Research Asia Yang Luo, он так же известен и как создатель еще одной, довольно известной библиотеки Npcap, которая с 2019 года работает под капотом утилиты Wireshark.


Основы авторизации


В своей сути, любой, даже сложный процесс авторизации можно схематично разбить на три компонента:


  • Субъект доступа это человек, служба, группа пользователей, кто пытается получить доступ.
  • Объект доступа это тот объект, к которому Субъект пытается получить доступ.
  • Авторизатор это компонент, который принимает решение, разрешить, либо запретить доступ Субъекта к Объекту.

Это можно изобразить в виде такой функциональной схемы:


Рис.1. Принципиальная схема авторизации


Рис.1. Принципиальная схема авторизации.


  1. Субъект доступа обращается к Объекту доступа.
  2. Объект доступа обращается к Авторизатору, и запрашивает бинарный ответ, разрешен ли этому Субъекту доступ к данному Объекту или нет.
  3. Авторизатор по заложенной в него логике проверяет возможность доступа Субъекту к Объекту, и отвечает разрешен ли доступ или запрещен. Наиболее распространенная логическая модель по которой идет проверка доступа это модель авторизации на базе ролей (RBAC).
  4. Объект доступа на основании ответа Авторизатор разрешает, либо запрещает доступ к себе Субъекта.

Casbin


Casbin это библиотека авторизации, которая позволяет использовать и комбинировать различные модели управления доступом, такие как ACL, RBAC, ABAC и д.р. С точки зрения Принципиальной схемы авторизации показанной на Рис.1 Casbin выполняет роль Авторизатора.


Рис.2. Принципиальная схема процесса авторизации с помощью Casbin


Рис.2. Принципиальная схема процесса авторизации с помощью Casbin.


Ключевым элементом механизма авторизации с помощью Casbin является Модель политики авторизации. Эта модель может описываться в текстовом файл *.CONF с помощью метамодели PERM (Policy, Effect, Request, Matchers), ну а по сути представляет из себя коллекцию строк с определенным содержанием.


Модель политики определяет состав кортежа с данными, который поступает в виде запроса на авторизацию, и описывает структуру хранения политик авторизации в хранилище политик авторизации, также определяет логику по которой происходит авторизации. Хранилищем может выступать как файл в формате *.csv, так и таблица в базе данных, впрочем как и любое другое хранилище, если для него реализован соответствующий адаптер.


Как же было сказано, PERM это гибкая метамодель для построения моделей авторизации. Аббревиатура расшифровывающаяся как (Policy Политика, Effect Эффект, Request Запрос, Matchers Сопостовители). Конкретный экземпляр PERM описанный в файле CONF, описывает, как эти 4 элемента взаимодействуют друг с другом, определяют логику авторизации.


Пример 1. Список контроля доступа (ACL)


Лучше всего понять модель PERM на конкретном примере.


Представим, что мы создали простую CRM систему, которая хранит список клиентов, и хотим внедрить простую систему контроля доступа, чтобы контролировать кто и что может делать с ресурсом Клиент (client). Для этой задачи подходит модель авторизации которая называется Список Контроля Доступа (Access Control List ACL).


Эту модель можно выразить в виде системных требований, представленных в следующей таблице, где определено, какие действия разрешены или запрещены пользователям с ресурсом client:


Пользователь/Действие Создание (client.create) Чтение (client.read) Изменение (client.modify) Удаление (client.delete)
Алиса (alice) да да да да
Боб (bob) нет да нет да
Петр (peter) да да да нет

Теперь эту модель мы опишем в конфиг файле с именем client_acl_model.conf на основе метамодели PERM, и дальше разберем каждую секцию


[request_definition]r = sub, obj, act[policy_definition]p = sub, obj, act[policy_effect]e = some(where (p.eft == allow))[matchers]m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

Каждая секция модели описывается в формате ключ = значение. Каждой секции соответствует свой ключ.


В первую очередь в секции [request_definition] идет определение структуры запроса (r). Здесь мы указываем, что каждый запрос будет представлять из себя кортеж из трех элементов, в котором первый элемент будет связан с атрибутом по имени sub (субъект), второй obj (объект), и третий action (действие). Пример правильного запроса, основанного на этом определении, является массив значений: ["alice","client","read"] (может ли alice read client?).


Дальше идет секция [policy_definition] для определения структуры хранения политики. Как правило, она повторяет структуру запроса. Дополнительно, все правила политики имеют дополнительный предопределенный атрибут eft, и он может принимать только значение allow (разрешено) или deny (запрещено). В данном случае, при определении достаточно простой модели политики ACL мы можем его не указывать, это будет избыточно.


Следующая секция [policy_effect], определяет, следует ли разрешить или запретить доступ, если запросу соответствует несколько политик авторизации. В данной модели мы используем следующий эффект политики для нашей CRM e = some(where (p.eft == allow)), что означает: если есть какое-либо подходящее разрешающее правило политики типа allow (например,eft == "allow"), конечным эффектом будет allow (разрешить). Это конструкция также означает, что если совпадений нет или все совпадения относятся к типу deny (запрещено), конечный эффект будет deny.


В разделе [matchers] мы указываем логическое выражение, где определяем правила соответствия запроса (r) заданному правилу политики (p).
В данном случае мы указываем, что первому атрибуту запроса r.sub должен соответствовать первый атрибут политики p.sub и (&&) соответственно второму атрибуту запроса должен соответствовать второй атрибут политики r.obj == p.obj, и третьему атрибуту запроса должен соответствовать третий атрибут в политики r.act == p.act.


Модель политики авторизации мы определили. Следующий шаг необходимо определить правила политики на основе системных требований и определения политики указанных в разделе [policy_definition] конфигурационного файла модели. Мы можем поместить эти правила в базу данных или, в нашем случае, в файл * .csv с именем client_acl_policy.csv:


p, alice, client, createp, alice, client, readp, alice, client, modifyp, alice, client, deletep, bob, client, readp, peter, client, createp, peter, client, readp, peter, client, modify

Следует обратить внимание, что, прежде всего, поскольку мы не указываем значение атрибута eft ни для одного из вышеперечисленных правил, все наши правила по умолчанию имеют тип allow (разрешено). Во-вторых, мы не должны определять какие-либо deny правила для нашей системы.
Так как именно такую логику обработки правил мы заложили в нашем конфигурационном файле модели.


Следующий шаг объединить модель политики, правила политики и саму библиотеку Casbin для создания в нашей CRM системы контроля доступа.


Для примера, я буду использовать код на C#, но в принципе он достаточно прост, и интуитивно понятен, даже для тех, кто его не знает.
Для платформы .net Casbin представлен в виде класса Enforcer, который имеет множество конструкторов, но в простом виде, он принимает в конструктор две строковых переменных, указывающих путь к файлам с моделью и правилами политики.


// Создадим новый экземпляр класса Enforcervar e = new Enforcer("path/to/client_acl_model.conf", "path/to/client_acl_policy.csv");// Определим переменные, которые укажем в запросе авторизацииvar sub = "alice";var obj = "client";var act = "read";// Выполняем проверку  if (e.Enforce(sub, obj, act)) {    // доступ alice к чтению объекта client разрешен} else {    // отклонить запрос, показать ошибку}

Пример 2. Контроль доступа на основе ролей (RBAC)


Наша система авторизации отлично работает для простых сценариев, по ходу увеличения числа пользователей, утомительно каждому из них назначать разрешения, особенно если этих разрешений несколько. Поэтому мы разработали новую версию системы контроля доступа, на базе ролей, показанной на следующей схеме.


Рис.3. Схема ролей доступа (RBAC)


Рис.3. Схема ролей доступа (RBAC).


Мы назначаем разные роли разным пользователям. Пользователю bob назначена роль читателя (reader), peter является автором (author), а alice теперь админ CRM (admin).
Затем для каждой роли мы определяем разрешения (вместо того, чтобы спрашивать, какой пользователь что может делать?, как это описано в модели ACL, теперь мы задаемся вопросом какая роль что может делать?). Мы также наследуем одну роль от другой, тем самым наши роли поддерживают транзитивность. На приведенной выше диаграмме мы от читателя наследуем автора, а от автора наследуем администратора, и каждый наследник обладает теми же разрешениями что и родитель и плюс своими собственными.


Исходя из этого дизайна, конфигурационный файл client_rbac_model.conf для нашей новой модели политики будет выглядеть так:


[request_definition]r = sub, obj, act[policy_definition]p = sub, obj, act[role_definition]g = _, _[policy_effect]e = some(where (p.eft == allow))[matchers]m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

Здесь мы видим, что у нас появилась новая секция [role_definition] для определения ролей. Выражение g = _, _ говорит о том, что в секции [matchers] мы будем сопоставлять два значения r.sub и p.sub, но их может быть и больше, если допустим наше приложение будет поддерживать мультитенантность, и это необходимо будет учитывать при авторизации. Но об этом дальше.


Еще одно отличие, от модели ACL мы изменили в секции [matchers] эту часть r.sub == p.sub на выражение g (r.sub, p.sub), что можно прочитать как: если r.sub имеет роль (или наследуется от) p.sub.


Содержимое файла client_rbac_policy.csv с правилами политики для такой модели будут выглядеть так:


p, reader, client, readp, author, client, modifyp, author, client, createp, admin, client, deleteg, bob, readerg, peter, authorg, alice, adming, author, readerg, admin, author

Пример использования в коде приложения не отличается от предыдущего примера c ACL, за исключением путей к файлам модели и правил политики:


var e = new Enforcer("path/to/client_rbac_model.conf", "path/to/client_rbac_policy.csv");var sub = "alice";var obj = "client";var act = "read";// Выполняем проверку  if (e.Enforce(sub, obj, act)) {    // доступ alice к чтению объекта client разрешен} else {    // отклонить запрос, показать ошибку}

Пример 3. Контроль доступа на основе ролей с поддержкой мультитенатности (RBAC with domains/tenants)


По мере дальнейшего развития нашего приложения CRM, им заинтересовались другие компании, и мы добавили в нашу таблицу с клиентами новый столбец компания, в который записываем имя копании, которой принадлежит этот клиент, и на основании этого значения отображаем каждой компании только принадлежащих ей клиентов, скрывая клиентов других компаний. Bob ушел в другую компанию, и там стал администратором нашей CRM.


Для поддержки мультитенатности, мы просто добавляем еще один атрибут в кортеж запроса на авторизацию, и в структуру описания правила политики в конфигурационном файле модели client_rbac_with_domain_model.conf, и в дальнейшем при определении роли, и при указании правил сопоставления, учитываем этот атрибут.


[request_definition]r = sub, dom, obj, act[policy_definition]p = sub, dom, obj, act[role_definition]g = _, _, _[policy_effect]e = some(where (p.eft == allow))[matchers]m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act

В сравнении с моделью описанной в примере 2 (RBAC) здесь мы видим что в секции [request_definition] и [policy_definition] у нас добавился новый атрибут dom, в который передается название компании, к которой принадлежит субъект, который хочет авторизоваться.
Так же мы изменили секцию [role_definition], добавив еще одно измерение для выяснения принадлежности роли субъекту с учетом компании, к которой он принадлежит, g = _, _ заменили на g = _, _, _.
И в разделе [matchers] часть выражения g(r.sub, p.sub) заменили на g(r.sub, p.sub, r.dom) && r.dom == p.dom, что можно прочитать как
если r.sub имеет роль (или наследуется от) p.sub c учетом значения атрибута запроса r.dom, и атрибуту запроса r.dom, соответствует атрибут политики p.dom.


Содержимое файла client_rbac_with_domain_policy.csv с правилами политики для такой модели может выглядеть так:


p, reader, company1, client, readp, author, company1, client, modifyp, author, company1, client, createp, admin, company1, client, deletep, reader, company2, client, readp, author, company2, client, modifyp, author, company2, client, createp, admin, company2, client, deleteg, author, reader, company1g, admin, author, company1g, author, reader, company2g, admin, author, company2g, alice, admin, company1g, peter, author, company1g, bob, admin, company2

Пример 4. Контроль доступа на базе модели RESTFul


Данная модель поддерживает возможность реализовывать логику авторизации, когда в качестве объектов доступа выступают ресурсы RestAPI интефрейса в формате URI в виде /res/*,/res/:id, а в качестве действий такие методы HTTP как GET,POST,PUT,DELETE.
Данная возможность обеспечивается благодаря использованию встроенных и пользовательских функций и регулярных выражений в разделе [matchers].
Конфигурационный файл в такой модели будет иметь следующий вид (здесь я использую официальный пример из Casbin):


[request_definition]r = sub, obj, act[policy_definition]p = sub, obj, act[policy_effect]e = some(where (p.eft == allow))[matchers]m = r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)

а файл с определением правил политики:


p, alice, /alice_data/*, GETp, alice, /alice_data/resource1, POSTp, bob, /alice_data/resource2, GETp, bob, /bob_data/*, POSTp, cathy, /cathy_data, (GET)|(POST)

Пример 5. Контроль доступа на базе атрибутов (ABAC)


Идея, заложенная в модели ABAC достаточно проста, и заключается в том, что политики авторизации строятся не на ролях, а на атрибутах субъектов, объектов доступа, атрибутов операций и атрибутов среды (атрибуты ABAC). Наиболее распространен достаточно сложный стандарт языка управления доступом ABAC под названием XACML. По сравнению с ним, модель ABAC реализованная в Casbin достаточно проста: есть возможность использовать структуры или экземпляры классов, вместо строковых значений атрибутов модели.
Здесь, как и в предыдущем примере, я буду использовать официальный пример из документации Casbin.
Вот пример модели политики на базе ABAC:


[request_definition]r = sub, obj, act[policy_definition]p = sub, obj, act[policy_effect]e = some(where (p.eft == allow))[matchers]m = r.sub == r.obj.Owner

Этот пример мало отличается описанных выше, за исключением того, что при сопоставлении в разделе [matchers], мы значение атрибута запроса r.sub сопоставляем с со свойством Owner экземпляра класса, который мы передаем в атрибут запроса r.obj, при вызове метода e.Enforce(). В данном случае при сопоставлении, Casbin будет использовать механизм Reflection, чтобы получить из переданного экземпляра класса значение свойства Owner.
Тогда класс, экземпляр которого мы будем передавать в качестве значения для атрибута r.obj, будет иметь следующую структуру:


public class ResourceObject  {    ...    public string Owner { get; set; }}

Можно использовать несколько атрибутов ABAC при сопоставлении, например


[matchers]m = r.sub.Domain == r.obj.Domain

В настоящее время, в качестве источников атрибутов ABAC для сопоставления могут выступать только элементы запроса на авторизацию (r), такие как r.sub, r.obj, r.act и т.д.
Нельзя в качестве источников атрибутов ABAC использовать элементы политики (p), такие как например p.sub, p.obj и т.д., так как нет способа определить класс или структуру в политике Casbin.


Но для более сложных сценариев с использованием ABAC, существует возможность описать правила ABAC в политике Casbin, чтобы не увеличивать многословность логического выражения в секции [matchers] модели.
Это достигается путем введения в модель функциональной конструкции eval() и называется масштабированием модели.


Пример конфигурации модели политики abac_scale_model.conf с использованием масштабирования


[request_definition]r = sub, obj, act[policy_definition]p = sub_rule, obj, act[policy_effect]e = some(where (p.eft == allow))[matchers]m = eval(p.sub_rule) && r.obj == p.obj && r.act == p.act

Здесь в разделе [policy_definition] модели, мы определяем новый атрибут sub_rule, а в секции [matchers] используем конструкцию eval(p.sub_rule). В данном случае p.sub_rule представляет из себя определяемый пользователем тип (класс или структура), который содержит необходимые свойства, которые будут использоваться при определении политики.


Файл abac_scale_policy.conf с правилами политики:


p, r.sub.Age > 18, client1, readp, r.sub.Age < 60, client2, write

Ну и в коде это будет выглядеть так:


public class User  {    public int Age { get; set;}    public string Name { get; set; }}class Program{    static void Main(string[] args)    {        var e = new Enforcer("path/to/abac_scale_model.conf", "path/to/abac_scale_policy.csv");        var sub = new User() { Name = "alice", Age = 19 };        var obj = "client1";        var act = "read";        if (e.Enforce(sub, obj, act)) {            // доступ alice к чтению объекта client1 разрешен        } else {            // отклонить запрос, показать ошибку        }    }}

Резюме


Библиотека Casbin очень гибкое и универсальное решение для авторизации, благодаря использованию интерпретируемого языка определения политики управления доступом называемый языком моделирования PERM (PML). Она позволяет использовать как существующие, наиболее распространенные модели управления доступом, так и комбинировать их, либо создавать новые.


В основе Casbin лежит интерпретатор модели политики (policy model) описанной с помощью языка PERM, который сопоставляет значения переданные в качестве атрибутов запроса на авторизацию, с правилами политики (policy) полученными из хранилища политик безопасности.
Библиотека реализована для множества существующих наиболее распространенных языков программирования.


Источники и полезные ссылки


Подробнее..

ГибкаяавторизацияспомощьюCasbinиPERM.Практический пример

02.02.2021 02:11:38 | Автор: admin

После написания предыдущей статьи по языку PERM и библиотеке Casbin, возникли вопросы. Причем не у одного человека, и я хотел ответить сначала в комментарии, но понял, что объем материала выходит за рамки обычного комментария, поэтому изложу этот ответ в виде отдельной статьи.


Я долго не мог понять ту конкретную конструкцию, которая сидела в голове у вопрошающих, но в конечном итоге после уточняющих вопросов получил ответы, компиляцию которых приведу в цитате.


А как с такими DSL решается задача показать список объектов, которые я могу видеть? Надо же в SQL-запрос это как-то транслировать, не выгребать же все записи с БД.

Есть интерфейс на сайте, показывающий список чего-либо. Скажем статей в админке CMS. Статей с базе десятки тысяч, но обычно пользователь имеет доступ только к десятку. Как достать из БД статьи, которые видны конкретному пользователю? Ну, если мы все правила что кому видно вынули из кода в какой-то DSL?
Другими словами как написать запрос типа

select * from articles a
join roles r on r.userId = currentUserId
where article.owner = currentUserId
OR (r.role in ['admin', 'supevisor']) админ всего
OR (r.domain = currentDomainId AND r.role in ['domain-admin', 'domain-supervisor']) админ домена

У меня такие правила в коде, в виде LINQ-выражений, и я такую задачу решать умею. А возникает такая задача даже чаще, чем проверить есть ли доступ к одному выгруженному с память объекту

Надеюсь, я правильно понял эту конструкцию и в ходе обратного реверсинженеринга, мне удалось вытащить исходные данные для решения данной задачи. Для начала обойдемся без использования мультитенатности (домены), так как они усложняют задачу и соответственно понимание. Пример их использования я приводил в прошлой статье.


Сначала я опишу ту реализацию, которая сидит в голове у вопрошающего, чтобы вы тоже ее понимали, а затем мы эту реализацию трансформируем в решение с использованием Casbin.


Описание задачи


У нас имеется CMS, в которую пользователи через админпанель могут добавлять статьи. В адмике пользователь с ролью user может видеть только свои статьи. Статьидругихлюдейоннеможетвидеть, если только ему не присвоена роль admin или supervisor. Пользователь с ролью supervisor может видеть и редактировать все статьи, а admin имеет все те же права что и supervisor, но кроме этого, еще может и удалять любые статьи.


Структура, схема и содержимое БД:


Структура БД нашей CMS:
Схема таблиц БД


Содержимое таблицы пользователей Users
Содержимое таблицы Users


Пользователям присвоены следующие роли
Содержимое таблицы Roles
Содержимое таблицы Roles


Как мы видим, Piter является администратором, а Bob супервизором. Alice обычный пользователь, может видеть, создавать и редактировать только свои статьи.


Содержимое таблица с статьями Articles
Содержимое таблицы Articles


Исходя из вопроса, выборка осуществляется для администратора (Piter, id=3) таким образом:


select * from articles aleft join roles r on r.userId = 3where a.owner = 3OR (r.role in ('admin', 'supevisor'))

Результат выборки для администратора


Выборка для супервизора (Bob, id=2) таким образом:


select * from articles aleft join roles r on r.userId = 2where a.owner = 2OR (r.role in ('admin', 'supevisor'))

Результат выборки для супервизора


А выборка для пользователя (Alice, id=1) выглядит так:


select * from articles aleft join roles r on r.userId = 1where a.owner = 1OR (r.role in ('admin', 'supevisor'))

Результат выборки для пользователя


Давайте теперь попробуем использовать другой подход к авторизации, на базе библиотеки Casbin.


Подход с использованием Casbin


Для начала давайте определимся что ресурс в подходе PERM это не столько экземпляр сущности, сколько сама сущность.
Т.е. когда мы описываем модель авторизации, под ресурсом в нашем примере подразумевается сама сущность (таблица) Статья. А не конкретная запись из этой таблицы (с Id=1 например).


Дальше необходимо уточнить, что те роли, которые используются в описании этой задачи это не классические роли из подхода RBAC.
Роли RBAC описывают те разрешения, которые можно выполнить с сущностью. Например в классическом RBAC роль user могла бы только читать статьи, роль author могла бы наследовала роль user (т.е. чтение статей), и еще могла бы редактировать создавать новые статьи, а роль admin могла бы наследовать все предыдущие разрешения и плюс еще удалять статьи.
В описанной же нами выше задаче, по сути эти все роли не отличаются друг от друга. И user и supervisor и admin имеют одни и те же права, один набор разрешений каждый носитель любой из ролей может создавать, редактировать или удалять статьи. Разница только в области видимости, user может видеть в админке только свои статьи, и соответственно редактировать их и удалять. А admin и supervisor не только свои, но еще и чужие.
И в этом заключается большой минус модели RBAC, так это статичная модель авторизации, и с ее помощью вообще невозможно выразить бизнес-правила, в которых используются атрибуты, значения которых заранее не известны и вычисляются в процессе работы.
Об этом подробно уже было рассказано в статье Подходы к контролю доступа: RBAC vs. ABAC


А те роли что мы используем (user, admin) это так называемые динамические роли. Термин не официальный, и зачастую каждый подразумеваем под ним свое, они реализуются различными способами, и описанное автором решение которое я привел в начале статьи один из таких подходов.


Выборка значений с учетом "динамических ролей"


Для начала давайте определим модель политики RBAC (rbac_model.conf), ее подробное описание я привел в предыдущей статье:


[request_definition]r = sub, obj, act[policy_definition]p = sub, obj, act[role_definition]g = _, _[policy_effect]e = some(where (p.eft == allow))[matchers]m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

Далее, мы убираем таблицу ролей Roles. Роли у нас теперь будут описаны в хранилище политик. Это может быть как обычный *.csv файл, так и таблица в базе данных. Для простоты я буду использовать cvs файл rbac_policy.csv:


p, user, article, readp, user, article, modifyp, user, article, createp, user, article, deleteg, supervisor, userg, admin, supervisorg, 1, userg, 2, supervisorg, 3, admin

Суть здесь такая, что мы даем роли user права на чтение, модификацию, создание и удаление статей. Затем роль supervisor наследует права роли user. А роль admin наследует права роли supervisor.
Далее пользователю alice(1) мы присваиваем роль user, bob(2) у нас supervisor, а piter(3) admin
В принципе этого достаточно, чтобы решить проблему, которую описывал автор вопроса.


Этот код конечно не для продакшена, а для демонстрации. Для продакшена я советую использовать cross-cutting concern с CQRS+MediatR


    public IList<Article> GetArticlesForAdminPanel(int currentUserId)    {        var e = new Enforcer("CasbinConfig/rbac_model.conf", "CasbinConfig/rbac_policy.csv");        var obj = "article";        var act = "read";        //Сначала проверяем, что пользователь имеет права на чтение статей        if (e.Enforce(currentUserId.ToString(), obj, act))        {            //Получаем список ролей пользователя            var currentUserRoles = e.GetRolesForUser(currentUserId.ToString());            //Проверяем, является ли пользователем админиом или супервизором            var isAdmin = currentUserRoles.Any(x => x == "admin" || x == "supervisor");            //Если админ, вернуть все записи, иначе только те, которые принадлежат пользователю            if (isAdmin) return _context.Articles.ToList();            else return _context.Articles.Where(x => x.OwnerId == currentUserId).ToList();        }        else        {            // отклонить запрос, показать ошибку            throw new Exception("403. У вас нет прав для чтения статей");           }    }

Тадам! Задача решена, ответ на вопрос дан.


Редактирование статей с учетом "динамических ролей"


Теперь же пойдем еще дальше. Мы получили список статей, отобразили в админке, и попытаемся отредактировать какую-нибудь статью. И нам соответственно надо проверить, имеем ли мы права чтобы ее отредактировать, пользователь с ролью user может отредактировать только свои статьи, supervisor и admin могут отредактировать все статьи.


Для этого определяем новую модель, называем ее rbac_with_abac_model.conf:


[request_definition]r = sub, obj, act[policy_definition]p = sub, obj, act[role_definition]g = _, _[policy_effect]e = some(where (p.eft == allow))[matchers]m = (r.sub == r.obj.OwnerId.ToString() || g(r.sub, "supervisor")) && g(r.sub, p.sub) && r.act == p.act

Данная модель не сильно отличается от модели чтения, за исключением секции [matchers], в ней мы конструкцию r.obj == p.obj заменили на (r.sub == r.obj.OwnerId.ToString() || g(r.sub, "supervisor")). Это следует читать как r.sub (id пользователя) должен совпадать с полем r.obj.OwnerId (id владельца обновляемой записи) или r.sub должен входить принадлежать группе "supervisor". Поскольку группа admin наследует все права группы supervisor то и члены группы admin будут соответствовать этому правилу.


Файл с политиками остается прежним, его мы не меняем. Теперь смотрим как это выглядит в коде:


    public void UpdateArticle(int currentUserId, Article newArticle)    {        var e = new Enforcer("CasbinConfig/rbac_with_abac_model.conf", "CasbinConfig/rbac_policy.csv");        var act = "modify";        //Проверяем, что пользователь имеет права на редактирование статьи        if (e.Enforce(currentUserId.ToString(), newArticle, act))        {            //Обновляем, и сохраняем изменения            _context.Articles.Update(newArticle);            _context.SaveChanges();        }        else        {            // отклонить запрос, показать ошибку            throw new Exception("403. Недостаточно прав");        }    }

Здесь стоит обратить внимание на то, что мы в метод e.Enforce передаем вторым параметром объект, который представляет из себя экземпляр класса Article.


Ну и последний шаг попытаемся удалить статью.


Удаление статьи


Бизнес-правило у нас здесь такое, что пользователь с ролью user может удалить свою статью, supervisor не имеет прав удалять чужие статьи, а admin такое право имеет.
Опишем теперь это бизнес-правило в модели политики PERM, в файле delete_model.conf:


[request_definition]r = sub, obj, act[policy_definition]p = sub, obj, act[role_definition]g = _, _[policy_effect]e = some(where (p.eft == allow))[matchers]m = (r.sub == r.obj.OwnerId.ToString() || g(r.sub, "admin")) && g(r.sub, p.sub) && r.act == p.act

Она не сильно отличается от предыдущей политике редактирования, за тем исключением, что удалять чужие статьи мы разрешили только роли admin. Если носитель роли supervisor попытается удалить чужую статью, у него выйдет ошибка превышения полномочий.


Как и в случае с моделью, код мало чем отличается от предыдущего примера на редактирование:


    public void DeleteArticle(int currentUserId, Article deleteArticle)    {        var e = new Enforcer("CasbinConfig/delete_model.conf", "CasbinConfig/rbac_policy.csv");        var act = "delete";        //проверяем, что пользователь имеет права на удаление статьи        if (e.Enforce(currentUserId.ToString(), deleteArticle, act))        {            //Удаляем статью            _context.Articles.Remove(deleteArticle);            _context.SaveChanges();        }        else        {            // отклонить запрос, показать ошибку            throw new Exception("403. Недостаточно прав");        }    }

Резюме


Надеюсь, данный пример продемонстрировал гибкость, универсальность и удобство использования библиотеки Casbin и языка PERM для построения решений по разделению доступа и авторизации.


Еще отмечу, что как сами модели политики, так и правила политики могут храниться в БД. И предусмотрена возможность фильтрации правил, если их большое множество, и это может стать узким местом в высоконагруженных приложениях.


Casbin под капотом использует библиотеку DynamicExpresso.Core для интерпретации простых выражений C# при сопоставлении правил политик с входными значениями, что позволяет эффективно использовать Casbin даже в самых сложных сценариях авторизации.


Не смотря на свою молодость, Casbin активно развивается, используется во множестве проектов, обрастает полезными инструментами и API. Такими например как UI для управления политиками.


Полностью работоспособный и самодостаточный код примера, который я использовал для написания данной статьи я разместил у себя на Github, можете скачать и поиграться, если есть интерес и желание.


Полезные ссылки


Подробнее..

Перевод Анонс иерархических пространств имен для Kubernetes

02.09.2020 12:08:46 | Автор: admin
Прим. перев.: недавно в блоге Kubernetes был представлен проект иерархических пространств имён. Формально он существует с конца прошлого года, но именно теперь авторы сочли уместным анонсировать свой Hierarchical Namespace Controller (HNC) для массовой аудитории. О предназначении и деталях реализации читайте в этом материале, который мы также дополнили переводом дорожной карты HNC.



Безопасно разместить большое число пользователей в одном кластере Kubernetes всегда было непросто. Главная причина в том, что все организации используют Kubernetes по-разному, поэтому единая мультипользовательская модель вряд ли всем подойдет. Вместо этого Kubernetes предлагает компоненты для создания собственного решения, такие как управление доступом на основе ролей (RBAC) и NetworkPolicies; чем лучше эти компоненты, тем легче построить безопасный многопользовательский кластер.

Namespace'ы спешат на помощь


Безусловно, наиболее важными из этих компонентов являются пространства имен. Они выступают основой практически всех политик безопасности и совместного использования control plane в Kubernetes. Например, RBAC, NetworkPolicies и ResourceQuotas по умолчанию поддерживают пространства имен, а объекты вроде Secret, ServiceAccount и Ingress свободно доступны в рамках одного пространства, но полностью изолированы от других.

У namespace'ов есть пара ключевых особенностей, благодаря которым они идеально подходят для применения политик. Во-первых, их можно использовать для представления собственности. Большинство объектов Kubernetes должны входить в какое-либо пространство имен. Используя namespace'ы для представления собственности, вы всегда можете рассчитывать на то, что у этих объектов имеется владелец.

Во-вторых, создавать и использовать пространства имен могут только пользователи с соответствующим правами. Для создания namespace'ов нужны расширенные привилегии, а остальным пользователям требуется явное разрешение на работу с ними то есть на создание, просмотр или изменение объектов в этих пространствах имен. Таким образом, сначала создается namespace с тщательно продуманным набором политик, и только после этого непривилегированные пользователи имеют возможность создавать обычные объекты вроде pod'ов и сервисов.

Ограничения namespace'ов


Увы, на практике пространства имен недостаточно гибки и плохо вписываются в некоторые распространенные сценарии использования. Например, некая команда владеет несколькими микросервисами с разными секретами и квотами. В идеале им следовало бы разнести эти сервисы по отдельным пространствам имен, чтобы изолировать их друг от друга, но тут возникают две проблемы.

Во-первых, у этих namespace'ов отсутствует единая концепция владения, хотя ими обоими владеет одна команда. Это означает, что не только Kubernetes ничего не знает о том, что у этих namespace'ов один владелец, но и отсутствует возможность применять глобальные политики сразу ко всем подконтрольным namespace'ам.

Во-вторых, как известно, автономность залог эффективной работы команд. Поскольку создание namespace'ов требует расширенных привилегий, маловероятно, что эти привилегии окажутся у кого-либо из команды разработчиков. Другими словами, всякий раз, когда команда решает создать новое пространство имен, ей приходится обращаться к администратору кластера. Это может быть вполне приемлемо для небольшой компании, однако по мере роста все сильнее проявляется негативный эффект от подобной организации.

Представляем иерархические пространства имен


Иерархические namespace'ы новая концепция, разработанная рабочей группой Kubernetes по multi-tenancy (wg-multitenancy), чтобы решить эти проблемы. В упрощенном виде иерархический namespace представляет собой обычное пространство имен Kubernetes со включением небольшого custom-ресурса, который указывает на одно (необязательное) родительское пространство имен. Тем самым концепция владения расширяется на сами пространства имен, а не только на объекты внутри их.

Концепция владения также реализует два дополнительных типа отношений:

  • Наследование политик: если один namespace является потомком другого, объекты, имеющие отношение к политике, такие как RoleBindings в RBAC, копируются из родительского пространства в дочернее.

  • Делегированное создание: обычно для создания namespace'а требуются привилегии на уровне кластера. Иерархические пространства имен предлагают альтернативу: subnamespaces, для работы с которыми достаточно ограниченных прав из родительского пространства.

Тем самым решаются обе проблемы типичной dev-команды. Администратор кластера может создать для нее одно корневое пространство вместе с необходимыми политиками и делегировать полномочия на создание подпространств участникам команды. Таким образом, разработчики смогут создавать подпространства имен для собственного использования, не нарушая политик, установленных администраторами кластера.

Немного практики


Иерархические namespace'ы реализованы с помощью расширения Kubernetes под названием Hierarchical Namespace Controller или HNC. HNC состоит из двух компонентов:

  • Manager работает в кластере, управляет подпространствами имен, распространяет policy-объекты, гарантирует допустимость выстроенных иерархий и управляет точками расширения.

  • Плагин kubectl под названием kubectl-hns позволяет пользователям взаимодействовать с manager'ом.

Руководство по установке компонентов можно найти на странице releases в репозитории проекта.

Давайте посмотрим на работу HNC. Предположим, у меня нет привилегий на создание namespace'ов, но я могут просматривать пространство имен team-a и создавать subnamespace'ы в нем*. Плагин позволяет мне ввести следующую команду:

$ kubectl hns create svc1-team-a -n team-a

* Технически говоря, вы создаете небольшой объект под названием subnamespace anchor (подпространственный якорь) в родительском пространстве и затем HNC создает subnamespace.

В результате будет создано пространство имен svc1-team-a. Обратите внимание, что subnamespace'ы ничем не отличаются от обычных пространств имен Kubernetes, поэтому их названия должны быть уникальными.

Просмотреть получившуюся структуру можно с помощью команды tree:

$ kubectl hns tree team-a# Output:team-a svc1-team-a

Если в родительском пространстве имеются какие-либо политики, они будут скопированы и в дочернее*. Предположим, например, что в team-a есть RBAC RoleBinding под названием sres. Этот RoleBinding также появится и в соответствующем подпространстве имен:

$ kubectl describe rolebinding sres -n svc1-team-a# Output:Name:         sresLabels:       hnc.x-k8s.io/inheritedFrom=team-a  # inserted by HNCAnnotations:  <none>Role:  Kind:  ClusterRole  Name:  adminSubjects: 

* По умолчанию распространяются только Roles и RoleBindings в RBAC, но можно настроить HNC на распространение любого объекта Kubernetes.

Наконец, HNC добавляет к этим пространствам имен метки с полезной информацией об иерархии. Их можно использовать для применения других политик. Например, можно создать следующую NetworkPolicy:

kind: NetworkPolicyapiVersion: networking.k8s.io/v1metadata:  name: allow-team-a  namespace: team-aspec:  ingress:  - from:    - namespaceSelector:        matchExpressions:          - key: 'team-a.tree.hnc.x-k8s.io/depth' # Label created by HNC            operator: Exists

Эта политика будет распространена на потомков team-a, а также разрешит ingress-трафик между всеми этими пространствами имен. Только HNC может присваивать метку tree. Она гарантированно отражает текущую иерархию.

Подробности о функционале HNC можно получить в руководстве пользователя.

Дальнейшие шаги и участие в процессе


Если вы считаете, что иерархические namespace'ы пригодятся в вашей организации, версия HNC v0.5.1 доступна на GitHub (с 28 августа доступен релиз v0.5.2 прим. перев.). Мы бы хотели узнать, что вы думаете о ней, какие проблемы решаете с ее помощью и какие функции хотели бы в нее добавить. Как и в случае с любым ПО на начальных этапах разработки, необходимо соблюдать осторожность при использовании HNC в production. При этом чем больше обратной связи мы получим, тем быстрее сможем прийти к HNC 1.0.

Мы также приветствуем вклад сторонних contributor'ов, будь то исправление ошибок/информация о них или помощь в создании прототипов новых функций, таких как исключения, улучшенный мониторинг, иерархическое квотирование ресурсов или оптимизация конфигурации.

Связаться с нами можно в репозитории, рассылке или в Slack. С нетерпением ждем ваших комментариев!

Автор оригинального анонса Adrian Ludwin, инженер-программист и технический руководитель Hierarchical Namespace Controller.

Бонус! Дорожная карта и issues


Пожалуйста, создавайте issues чем больше, тем веселее! Баги будут анализироваться в первую очередь, а для feature request'ов будет определяться приоритет, после чего их включат в план работ или в бэклог.

HNC пока еще не получил статус GA, поэтому будьте осторожны при его использовании в кластерах с конфигурационными объектами, которые не можете позволить себе потерять (например, с теми, которые не хранятся в репозитории Git).

Все issues HNC включаются в соответствующий план работ. На данный момент реализованы или намечены следующие основные этапы этого плана:

  • v1.0: конец I начало II квартала 2021-го; HNC рекомендован для production.
  • v0.8: начало 2021-го; могут появиться новые критически важные функции.
  • v0.7: конец 2020-го; скорее всего, появится v1beta1 API.
  • v0.6: запланирована на октябрь 2020-го; реализация v1alpha2 API и полностью автоматизированное сквозное тестирование.
  • v0.5: вышла в июле 2020-го; упрощение функционала, улучшение тестирования и повышение стабильности.
  • v0.4: вышла в июне 2020-го; стабилизация API и добавление production-функционала.
  • v0.3: вышла в апреле 2020-го; конфигурирование типов и улучшение UX в области работы с subnamespace'ами.
  • v0.2: вышла в декабре 2019-го; содержит достаточный функционал для применения в средах категории non-production.
  • v0.1: вышла в ноябре 2019-го; первый релиз со всем необходимым функционалом. Его можно попробовать, но он не походит для каких-либо реальных рабочих нагрузок.
  • Бэклог: все внеплановые работы.

P.S. от переводчика


Читайте также в нашем блоге:

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru