В попередній частині – AWS: знайомство з OpenSearch Service в ролі vector store – подивились на AWS OpenSearch Service взагалі, трохи розібрались з тим, як в ньому організовані дані, що таке shards та nodes, і які нам власне типи інстансів для data nodes треба.
Наступний крок – створити кластер і подивитись на аутентифікацію, яка, як на мене, в чомусь навіть складніша за AWS EKS. Хоча, можливо, просто діло звички.
Що будемо робити сьогодні – вручну створимо кластер AWS OpenSearch Service, глянемо на основні опції при створенні кластеру, а потім копнемо в налаштування доступу до кластеру і до OpenSearch Dashboards з AWS IAM та Fine-grained access control самого OpenSearch і його Security plugin.
А вже в наступній частині, якщо буде час на написання, то дійдемо до Terraform.
Зміст
Ручне створення кластера в AWS Console
Робити будемо мінімальний PoC аби погратись, тобто з t3
інстансами і в одній Availability Zone та без Master Nodes.
В Production у нас теж планується один маленький кластер з трьома індексами dev/staging/prod в ролі vector store для AWS Bedrock Knowledge Base.
Документація від AWS – Creating OpenSearch Service domains.
Переходимо в Amazon OpenSearch Service > Domains, клікаємо “Create domain”.
Задаємо ім’я, вибираємо “Standart create”, аби мати доступ до всіх опцій:
В “Templates” вибираємо “Dev/”test – тоді можна буде вибрати конфіг без Master Nodes і можна буде деплоїти в одній Availability Zone.
В “Deployment option(s)” вибираємо “Domain without standby” – тоді нам будуть доступні інстанси t3
:
Справа нам зручненько відразу показує весь сетап.
Storage
Питання кількості шардів на кластер розбирали в попередньому пості, будемо вважати, що у нас планується даних максимум 20-30 GiB, тому будемо створювати 1 primary шард та 1 replica. Але шарди налаштовуються пізніше, коли будемо робити індекси з Terraform і opensearch_index_template
.
І для цих двох шардів будемо робити дві Data Nodes – одна для primary шарду, одна для репліки.
“Engine options” описані в Features by engine version in Amazon OpenSearch Service, просто залишаємо дефолтне значення, останню версію.
“Instance family” вибираємо “General puprose”, в “Instance type” – t3.small.search
.
“EBS storage size per node” візьмемо 50 GiB – 20-30 гігабайт під дані, і трохи запасу для самої операційної системи:
Nodes
“Number of master nodes” та “Dedicated coordinator nodes” залишаємо без змін, тобто без них:
Network
В “Custom endpoint” поки теж нічого не міняємо, але потім тут можна додати який власний домен із Route53 з сертифікатом з AWS Certificate Manager для доступу до кластеру, див. Creating a custom endpoint for Amazon OpenSearch Service.
В “Network” – поки робимо найпростіший варіант, з “Public access”, але для Production будемо робити всередині VPC:
Але треба буде потестити доступ до Dashboards, бо якщо кластер створюється в сабнетах VPC, то до нього не можна застосувати IP-based policies, див. About access policies on VPC domains. Про IP-based policies будемо говорити тут далі.
Access && permissions
“Fine-grained access control” (FGAC) – поки відключаємо, далі детальніше подивимось на цей механізм. Хоча я не впевнений, що він буде потрібен, бо розділити доступ до різних індексів в одному кластері можна і просто з IAM.
SAML, JWT та IAM Identity Center залежать від FGAC, тому теж скіпаємо, і надалі я їх використовувати не планую, не наш кейс.
Cognito теж мимо – ми ним не користуємось (хоча пізніше, можливо, подивлюсь в сторону інтеграції з Auth0 чи Cognito для Dashboards):
“Access policy” можна порівняти з S3 Access Policy, або з IAM Policy для EKS яка дозволяє IAM-юзеру доступ до кластеру.
Детальніше поговоримо в частині про аутентифікацію, поки просто залишаємо дефолтний “Do not set domain level access policy”:
“Off-peak window” – час найменшого навантаження для встановлення апдейтів і виконання Auto-tune операцій.
У нас off-peak буде вночі по США, тому в Production тут буде Central Time (CT) 05:00 UTC.
Але так як зараз тестовий PoC – то теж скіпаємо.
Auto-Tune власне теж нормально описана, і недоступна для наших інстансів t3
.
Automatic software update – корисна штука для Production, і буде виконуватись в час, заданий в Off-peak window:
В “Advanced cluster settings” можна відключити rest.action.multi.allow_explicit_index
, але не знаю, як у нас будуть будуватись запити, і начебто десь зустрічав, що може поламати Dashboard – тому нехай залишиться дефолтне enabled:
Ну і все, в результаті маємо такий сетап:
Клікаємо “Create”, і йдемо пити чай, бо створюється кластер довго – довше, ніж EKS, і створення OpenSearch зайняло хвилин 20.
Аутентифікація та авторизація
Тепер, мабуть, саме цікаве – про юзерів і доступи.
Після створення кластера по дефолту ми маємо обмежені права доступу до самого OpenSearch API:
Бо в “Security Configuration” у нас є явний Deny:
Доступ до AWS OpenSearch Service має три таких собі “рівня” – мережа, IAM, та Security Plugin самого OpenSearch.
При цьому в IAM у нас є дві сутності – Domain Access Policy, який ми бачимо в Security Configuration > Access Policy (атрибут access_policies
в Terraform), та Identity-based policies – які є звичайними AWS IAM Policies.
Якщо говорити про ці рівні більш детально, то вони виглядають якось так:
- мережа: параметр Network > VPC access або Public access: задаємо ліміт доступу на рівні мережі (див. Launching your Amazon OpenSearch Service domains within a VPC)
- або, якщо брати аналогію з EKS – То це Public та Private API endpoint, або з RDS – створювати інстанс в публічних чи приватних сабнетах
- AWS IAM:
- Domain Access Policies:
- Resource-based policies: політики, які описуються безпосередньо в налаштуваннях самого кластеру
- доступ задається для IAM Role, IAM User, AWS Accounts до конкретного OpenSearch domain
- IP-based policies: фактично ті самі Resource-based policies, але з можливістю дозволити доступ без аутентифікації для конкретних IP (тільки якщо тип доступу Public, див. VPC versus public domains)
- Resource-based policies: політики, які описуються безпосередньо в налаштуваннях самого кластеру
- Identity-based policies: якщо Resource-based policies є частиною налаштувань security-політик кластера – то Identity-based policies є звичайними AWS IAM Policies, які додаються конкретному юзеру чи ролі
- Domain Access Policies:
- Fine-grained access control (FGAC): Security Plugin самого OpenSearch – атрибут
advanced_security_options
в Terraform- якщо в Resource-based policies і Identity-based policies ми задаємо правила на рівні кластеру (домену) і індексів, то в FGAC можна додатково описати обмеження на конкретні документи або поля
- і навіть якщо в Resource-based policies і Identity-based policies дозволено доступ до ресурсу в кластері – через Fine-grained access control його можна “обрізати”
Тобто authentification та authorization flow буде таким:
- AWS API отримує запит від юзера, наприклад
es:ESHttpGet
- AWS IAM виконує аутентифікацію – перевіряє ACCESS:SECRET ключі або Session token
- AWS IAM виконує авторизацію:
- перевіряє IAM Policy юзера (Identity-based policy), якщо тут є явний дозвіл – пропускаємо
- перевіряє Domain Access Policy (Resource-based policy) кластеру, якщо тут явний дозвіл – пропускаємо
- запит приходить до самого OpenSearch
- якщо Fine-grained access control не включений – дозволяємо
- якщо є налаштований Fine-grained access control – перевіряємо внутрішні ролі, і якщо юзеру дозволено – то виконуємо запит
Давайте робити доступи, подивимось, як воно все працює.
Налаштування Domain Access policy
Базовий варіант – додати IAM User доступ до кластеру.
Resource-based policy
Редагуємо “Access policy”, і вказуємо свого юзера, типи API-операцій, та домен:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::492***148:user/arseny.zinchenko" }, "Action": "es:*", "Resource": "arn:aws:es:us-east-1:492***148:domain/test/*" } ] }
Чекаємо хвилину – і тепер маємо доступ до OpenSearch API (бо Cluster health в AWS Console отримується саме з OpenSearch – див. Cluster Health API):
І тепер можемо з curl та --aws-sigv4
отримати доступ до кластеру (див. Authenticating Requests (AWS Signature Version 4)):
$ curl --aws-sigv4 "aws:amz:us-east-1:es" \ > --user "AKI***B7A:pAu***2gW" \ > https://search-test-***.us-east-1.es.amazonaws.com/_cluster/health?pretty { "cluster_name" : "492***148:test", "status" : "green", "timed_out" : false, "number_of_nodes" : 2, "number_of_data_nodes" : 2, "discovered_master" : true, "discovered_cluster_manager" : true, "active_primary_shards" : 5, "active_shards" : 10, "relocating_shards" : 0, "initializing_shards" : 0, "unassigned_shards" : 0, "delayed_unassigned_shards" : 0, "number_of_pending_tasks" : 0, "number_of_in_flight_fetch" : 0, "task_max_waiting_in_queue_millis" : 0, "active_shards_percent_as_number" : 100.0 }
IP-based policies та доступ до OpenSearch Dashboards
Аналогічно, через Domain Access Policy можемо відкрити доступ до Dashboards – самий простий варіант, але працює тільки з Public domains. Якщо кластер буде в VPC – то треба буде робити додаткову аутентифікацію, див. Controlling access to Dashboards.
Редагуємо політику, додаємо умову IpAddress.aws:SourceIp
:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::492***148:user/arseny.zinchenko" }, "Action": "es:*", "Resource": "arn:aws:es:us-east-1:492***148:domain/test/*" }, { "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": "es:ESHttp*", "Resource": "arn:aws:es:us-east-1:492***148:domain/test/*", "Condition": { "IpAddress": { "aws:SourceIp": "178.***.***.184" } } } ] }
І тепер маємо доступ до дашборди:
Identity-based policy
Тепер другий варіант – створимо окремого IAM User і йому підключити окрему IAM Policy.
В AWS IAM додаємо юзера:
Можемо взяти AWS managed policies for Amazon OpenSearch Service:
Далі просто створюємо ключі доступу для Command Line Interface (CLI), і – нічого не змінюючи в Access policy самого кластеру – перевіряємо доступ:
$ curl --aws-sigv4 "aws:amz:us-east-1:es" --user "AKI***YUK:fXV***34I" https://search-test-***.us-east-1.es.amazonaws.com/_cluster/health?pretty { "cluster_name" : "492***148:test", "status" : "green", "timed_out" : false, "number_of_nodes" : 2, "number_of_data_nodes" : 2, "discovered_master" : true, "discovered_cluster_manager" : true, "active_primary_shards" : 5, "active_shards" : 10, "relocating_shards" : 0, "initializing_shards" : 0, "unassigned_shards" : 0, "delayed_unassigned_shards" : 0, "number_of_pending_tasks" : 0, "number_of_in_flight_fetch" : 0, "task_max_waiting_in_queue_millis" : 0, "active_shards_percent_as_number" : 100.0 }
Тобто тепер у нас є Domain Acces Policy – яка дозволяє доступ конкретно моєму юзеру, і є окрема IAM Ploicy – Identity-based policy – яка дозволяє доступ тестовому юзеру.
Але тут є один важливий момент: в IAM Policy ми вказуємо або весь домен – або тільки його subresources.
Тобто, якщо замість політики AmazonOpenSearchServiceFullAccess
ми створимо власну полісі, в якій вкажемо "Resource":***:domain/test/*"
:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "es:*" ], "Resource": "arn:aws:es:us-east-1:492***148:domain/test/*" } ] }
То ми зможемо виконати es:ESHttpGet
(GET _cluster/health
) – але не зможемо виконати cluster-level операції, наприклад – es:AddTags
, навіть при тому, що в Actions
IAM-політики маємо дозвіл на всі виклики – es:*
:
$ aws --profile test-os opensearch add-tags --arn arn:aws:es:us-east-1:492***148:domain/test --tag-list Key=environment,Value=test An error occurred (AccessDeniedException) when calling the AddTags operation: User: arn:aws:iam::492***148:user/test-opesearch-identity-based-policy is not authorized to perform: es:AddTags on resource: arn:aws:es:us-east-1:492***148:domain/test because no identity-based policy allows the es:AddTags action
Якщо ж ми хочемо дозволити взагалі всі операції з кластером – то "Resource"
задаємо як "arn:aws:es:us-east-1:492***148:domain/test"
, і тоді можемо додати теги.
Всі API actions див. в Actions, resources, and condition keys for Amazon OpenSearch Service.
Fine-grained access control
Документація – Fine-grained access control in Amazon OpenSearch Service.
Основна ідея дуже схожа з Kubernetes RBAC.
В OpenSearch маємо три основних концепти:
- users – як Kubernetes Users та ServiceAccounts
- roles – як Kubernetes RBAC Roles
- mappings – як Kubernetes Role Bindings
Юзери можуть бути як з AWS IAM, так і з внутрішньої бази OpenSearch.
Як і в Kubernetes, в OpenSearch є набір дефолтних ролей – див. Predefined roles.
При цьому ролі, як і в Kubernetes, можуть бути cluster-wide або index-specific – аналог ClusterRoleBinding та просто namespaced RoleBinding в Kubernetes, плюс в OpenSearch FGAC можна додатково мати document level або field level permissions.
Налаштування Fine-grained access control
Важливий момент: після включення FGAC не можна буде повернутись на стару схему. Але всі доступи з IAM залишаться, навіть якщо переключитись на internal database.
Редагуємо “Security configuration”, вмикаємо “Fine-grained access control”:
Спершу тут нам треба задати Master user, якого можна вказати з IAM – або створити локально в OpenSearch.
Якщо ми створюємо юзера через опцію “Create master user” – то вказуємо звичайний логін:пароль, і в такому випадку OpenSearch підключить internal user database (internal_user_database_enabled
в Terraform).
Якщо використовуємо внутрішню базу OpenSearch – то можемо мати звичайних юзерів і виконувати HTTP basic authentication, див. документацію AWS – Tutorial: Configure a domain with the internal user database and HTTP basic authentication та Defining users and roles в документації самого OpenSearch, бо це вже його внутрішні механізми.
Має сенс, якщо не хочеться крутити Cognito чи SAML, і якщо налаштування юзерів у кожного кластеру будуть власні.
Якщо задавати IAM-юзера, то схема буде схожою з AIM аутентифікацією для RDS і IAM database authentication – доступ до кластеру контролюється AWS IAM, але внутрішні першмішени до схем та баз – ролями PostgreSQL чи MariaDB, див. AWS: RDS з IAM database authentication, EKS Pod Identities та Terraform.
Тобто в такому випадку AWS IAM буде виконувати виключно аутентифікацію юзера, а авторизація (перевірка прав доступу) вже через Security plugin та ролі самого OpenSearch.
Спробуємо локальну базу, і, думаю, в Production ми теж візьмемо цю схему:
“Access Policy” можемо залишити як є:
Переключення на internal database займе час, бо викличе blue/green deployment нового кластеру – див. Making configuration changes in Amazon OpenSearch Service.
І зайняло це прям багато часу – більше години, при тому, що в кластері нема ніяких наших даних.
Після того як зміни застосовані – в Dashboards у нас тепер буде просити логін і пароль, використовуємо нашого Master user:
Master user отримує дві підключені ролі – all_access
та security_manager
.
І саме security_manager
дає доступ до розділу Security та Users в дашборді:
При цьому у нас залишається доступ наших AIM-юзерів, і ми можемо далі використовувати curl
: IAM users будуть мапитись на роль default_role
, яка дозволяє виконувати GET/PUT на всі індекси – див. About the default_role:
Перевіряємо доступ нашого тестового юзера зараз:
$ curl --aws-sigv4 "aws:amz:us-east-1:es" --user "AKI***YUK:fXV***34I" https://search-test-***.us-east-1.es.amazonaws.com/_cluster/health?pretty { "cluster_name" : "492***148:test", "status" : "green", "timed_out" : false, "number_of_nodes" : 2, ...
А тепер поріжемо доступи всім IAM-юзерам.
Створення OpenSearch Role
Аби подивитись, як воно працює – додамо тестовий індекс і замапимо нашого тестового юзера з доступом до цього індексу.
Додаємо індекс:
Переходимо в Securty > Roles, додаємо роль:
Задаємо Index permissions – повний доступ на індекс (crud
):
Далі в цій ролі переходимо до Mapped users > Map users:
І додаємо ARN нашого тестового юзера:
Видаляємо дефолтну роль:
Тепер наш юзер не має доступ до GET _cluster/health
– тут отримуємо помилку 403, no permissions:
$ curl --aws-sigv4 "aws:amz:us-east-1:es" --user "AKI***YUK:fXV***34I" https://search-test-***.us-east-1.es.amazonaws.com/_cluster/health?pretty { "error" : { ... "type" : "security_exception", "reason" : "no permissions for [cluster:monitor/health] and User [name=arn:aws:iam::492***148:user/test-opesearch-identity-based-policy, backend_roles=[], requestedTenant=null]" }, "status" : 403 }
Але має доступ до тестового індексу:
$ curl --aws-sigv4 "aws:amz:us-east-1:es" --user "AKI***YUK:fXV***34I" https://search-test-***.us-east-1.es.amazonaws.com/test-allowed-index/_search?pretty -d '{ "query": { "match_all": {} } }' -H 'Content-Type: application/json' { "took" : 78, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 0, "relation" : "eq" }, "max_score" : null, "hits" : [ ] } }
Готово.